Модераторы: gambit, Kefir, Partizan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Взгляд на RIA Services (.net4) 
:(
    Опции темы
jonie
Дата 23.9.2010, 00:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 2
Всего: 118



Читать много, не для слабонервных...  smile 

Итак, решил побаловать себя просмотром RIA Services в .NET 4 (SL4).
Просто примеры это неинтересно, посему решил сделать нечто более приближенное к реальности.

Итак, исходно имеется база данных (mssql2008r2) с таблицей и некой view на эту таблицу.
Задача: отобразить данные в SL гриде из view, дать возможность добавить в таблицу.

Как решил делать:
1) EF4 как слой Data Access Layer
2) Написал свой класс (POCO ) для пробрасывания в SL. Я не стал использовать генерацию, что предлагается при добавлении DomainService-а, т.к. у нас источник для получения записей и источник для внесения записей различен.
Вот такой вот POCO объект получился:
Код

    [NotifyPropertyChanged]
    public partial class TaskJournal : INotifyPropertyChanged
    {
        [Key]
        public int Id { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public virtual void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

NotifyPropertyChanged это атрибут для использования совместо с PostSharp (автоматическая реализация INotifyPropertyChanged), к сути дела не имеет отношения.

3) Написал сервис:
Код

 [EnableClientAccess]
    public class TaskJournalService : DomainService
    {
        private readonly ITaskJournalRepository _repository;
        public TaskJournalService(ITaskJournalRepository repository)
        {
            _repository = repository;
        }

        public IQueryable<TaskJournal> GetCurrentUserTask()
        {
            var rv = _repository.GetCurrentUserTask().Where(c=>c.Id<14); //пример запроса на вьюху через IRepository
            return rv;
        }
    }


где ITaskJournalRepository нужно для инжектирования зависимости репозитория вида:
Код

 public interface ITaskJournalRepository
    {
        IQueryable<TaskJournal> GetCurrentUserTask();
    }

    public class TaskJournalRepository : ITaskJournalRepository
    {
        public IQueryable<TaskJournal> GetCurrentUserTask()
        {
//Mapper.CreateMap<uv_ServiceTaskJournal, TaskJournal>(); 
//автомаппер надо бы использовать, но надо писать создавать expression создающий объект
//т.к. напрямую вызвать из linq2ef не получится (ограничение linq)

            var dbCtx = new CivilizationEntities();
            var rv = from journal in dbCtx.uv_ServiceTaskJournal
                     select new TaskJournal {Id = journal.Id};
            return rv;
        }
    }


Процесс инжектирования поручил Unity 2.0 в Global.asax:
Код


//где-то в коде:
    using Microsoft.Practices.Unity;
    public class DomainServiceFactory : IDomainServiceFactory
    {
        public DomainService CreateDomainService(Type domainServiceType, DomainServiceContext context)
        {
            var service = Global.UnityContainer.Resolve(domainServiceType) as DomainService;
            service.Initialize(context);
            return service;
        }

        public void ReleaseDomainService(DomainService domainService)
        {
            domainService.Dispose();
        }
    }

//в global.asax:
 protected void Application_Start(object sender, EventArgs e)
        {
            DomainService.Factory = new DomainServiceFactory();
            UnityContainer.RegisterType<ITaskJournalRepository, TaskJournalRepository>();
            UnityContainer.RegisterType<TaskJournalService>();
        }



Ок, у нас есть сервис позволяющий отдавать POCO сущности и даже судя по трассировке к базе вполне себе нормальные запросы строит (оборачивает select * запрос в запрос конкретных полей, указанных в перечислении при создании POCO объекта).

Аналогичным методом думаю добавить и добавление....

ИТОГО для тех кому лень читать получается схема:
1) Silverlight запрашивает данные у RIA сервиса
2) хост создает RIA сервис, используя DomainServiceFactory, который получает собственно экземпляр сервиса с инжектированными через Unity зависимостями
3) сервис используя инжектированный IRepository запрашивает данынные у него
4) IRepository выполняет запрос на базе данных используя EntityFramework, и производит маппинг на POCO объекты, которые и возращает
5) силвералайт получает POCO и использует их.


Так вот вопросы: 
1) А правильной ли дорогой я иду и нет ли методов попроще?
2) А что насчет ленивой загрузки связанных коллекций?


Пример солюшена выложу завтра (постараюсь)....


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
Rohoss
Дата 15.4.2011, 23:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Начальник интернета
***


Профиль
Группа: Завсегдатай
Сообщений: 1308
Регистрация: 9.10.2006
Где: Matrix

Репутация: 4
Всего: 18



Цитата(jonie @  23.9.2010,  00:49 Найти цитируемый пост)
1) А правильной ли дорогой я иду и нет ли методов попроще?

Тоже кажется всё слишком запутанным  smile 

Цитата(jonie @  23.9.2010,  00:49 Найти цитируемый пост)
А что насчет ленивой загрузки связанных коллекций?

А разве есть возможность использовать ленивую загрузку когда данные получаем через сервис?



--------------------
Файловый менеджер Explorer.Net скачать  video
PM ICQ   Вверх
Chef
Дата 21.4.2011, 20:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 167
Регистрация: 7.12.2007
Где: РК Павлодар

Репутация: нет
Всего: нет



В своей работе делал так:
Создавал DataModel. По ней строил сущности при помощи Domian Service.

В СЛ вызывал запросы к сервису. Единственно что очень не удобно это асинхронность.
--------------------
Разговоры об IT
PM MAIL WWW   Вверх
jonie
Дата 21.4.2011, 21:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 2
Всего: 118



Цитата(Chef @  21.4.2011,  20:30 Найти цитируемый пост)
В своей работе делал так:
Создавал DataModel. По ней строил сущности при помощи Domian Service.

к сожалению "данные базы данных" зачастую не равны "данные бизнес объектов". Бизнес объекты например могут отражаться на данные по каким-то бизнес правилам.
Именно поэтому я не переносил через RIA Services "базу данных". Вообще мне кажется это не очень хорошей идеей.

Цитата


А разве есть возможность использовать ленивую загрузку когда данные получаем через сервис?
ну для EF-ка нагенится (WCF RIA генератором) автоматических свойств-коллекций, которые будет возможно вызвать из клиента.... а своё такое писать придётся как-то самим...


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
-Mikle-
Дата 22.4.2011, 08:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Невидимка Vingrad'а
***


Профиль
Группа: Экс. модератор
Сообщений: 1672
Регистрация: 22.6.2003
Где: Казахстан, Астана

Репутация: 13
Всего: 59



Цитата(jonie @  22.4.2011,  00:20 Найти цитируемый пост)
Именно поэтому я не переносил через RIA Services "базу данных". Вообще мне кажется это не очень хорошей идеей.

Поддерживаю. Пусть теперь тебе не кажется, а будь уверенным. Переносить базу данных через сервисы, это все равно что иметь прямое подключение к базе данных с клиента. В чем тогда смысл сервисных методов, если они отражают только базу. Сервисы должны отражать бизнес задачи. Сам сервис да, работает с базой через какой-нить DAL, но помимо этого выполняет еще бизнес логику. В противном случае, баланс бизнес логики будет преимущественно на клиентской стороне (если не полностью).


--------------------
Если тебе плюют в спину, значит ты впереди...
PM   Вверх
jonie
Дата 22.4.2011, 08:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 2
Всего: 118



-Mikle-, на самом деле тут двоякое отношение. Перенос "базы" на SL клиента дает выйгрыш по скорости разработки и как бы это не было странным по логике работы самого ПО (до определенного момента). Например посмотри как работает IdeaBlade DevForce - у них сервер принимает сериализованный Expression, который исполняет и выдает обратно результат. На клиенте в свою очередь прокси классы генерируются (POCO), с которыми и работает клиент...



--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
-Mikle-
Дата 25.4.2011, 09:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Невидимка Vingrad'а
***


Профиль
Группа: Экс. модератор
Сообщений: 1672
Регистрация: 22.6.2003
Где: Казахстан, Астана

Репутация: 13
Всего: 59



Для небольшой системы согласен, можно хоть как делать. Но когда встает вопрос о масштабировании и необходимости предоставлять сервис third-party конторе, возникает много разных НО. Для небольшой системы можно даже OData взять.


--------------------
Если тебе плюют в спину, значит ты впереди...
PM   Вверх
DenWPF
Дата 9.6.2011, 22:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1659
Регистрация: 26.9.2009

Репутация: нет
Всего: 1



хнык, дядьки вы о чем =((
PM MAIL   Вверх
v00d00
Дата 9.6.2011, 23:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 142
Регистрация: 28.12.2005

Репутация: нет
Всего: нет



Отпишусь в эпичном треде.

Пишем проект с использованием RIA Services. Поначалу пытались работать с ентити графами на клиенте, но эта идея была впоследствии зафакана. Решили использовать плосские дто.

Модель базы данных довольно сложная, порядка 200 сущностей. Причем большая часть сущностей являются дочерними, аля Адрес, Телефон, EMail, Страховка и тд , которые без привязки к компании, эмплою, менеджреу (логическому корню репозитория) не имеют смысла.

Обычная вьюха дает возможность работать как раз с таким корнем. Например Контакты эмплоя. На эту вьюху тянется плоский дто, контролы биндятся к дата контексту напрямую.
Пользователь поработал, создал/удалил/изменил дочерние сущности. Эта красота гонится на сервис и парсится.

И тут начинается ад и Израиль.

Парсим дто, валидируем, поэтапно создаем/удаляем энтити. Код довольно простой но однотипный

Например:

Код

 [Update, EmpresaHasPermissions("PERMIT_INS_Employee")]
    public void UpdateBackground(EmployeeBackgroundDTO dto)
    {
        using (var context = GetObjectContext())
        {
            var user = EmpresaAuthentication.Current.User;

            Employee employee = context.Employees
                .Include(it => it.Nationality)
                .Include(it => it.EthnicOrigin)
                .Include(it => it.MaritalStatus)
                .Include(it => it.Religion)
                .Include(it => it.CRB)
                .Include(it => it.Passport)
                .Single(it => it.OwnerOrganizationId == user.OrganizationId &&
                              !it.Deleted && it.Id == dto.Id);

            var updater = new EmployeeBackgroundUpdater(context);

            updater.UpdateEntity(employee, dto);

            context.SaveChanges();

            dto.MaritalStatusId = employee.MaritalStatusId;
            dto.EthnicOriginId = employee.EthnicOriginId;
            dto.ReligionId = employee.ReligionId;
        }
    } 


Весь парсинг в методе апдейтера

Код

public override void UpdateEntity(Employee employee, EmployeeBackgroundDTO dto)
{
    employee.InjectFrom(dto);

    if (!IsPassportNull(dto))
    {
        if (employee.Passport == null)
        {
            employee.Passport = new Passport();
        }

        employee.Passport.IssueDate = dto.PassportIssueDate.Value;
        employee.Passport.ExpiryDate = dto.PassportExpiryDate.Value;
        employee.Passport.PassportNo = dto.PassportPassportNo;
        employee.Passport.IssuingCountryId = dto.PassportIssuingCountryId.Value;
        employee.Passport.OwnerUserId = UserId;
    }
    else
    {
        if (employee.Passport != null)
        {
            DeleteObject(employee.Passport);
            employee.Passport = null;
        }
    }

    if (!IsCRBNull(dto))
    {
        if (employee.CRB == null)
        {
            employee.CRB = new CRB();
        }

        employee.CRB.IssueDate = dto.CRBIssueDate.Value;
        employee.CRB.ExpiryDate = dto.CRBExpiryDate.Value;
        employee.CRB.Registration = dto.CRBRegistration;
        employee.CRB.Notes = dto.CRBNotes;
    }
    else
    {
        if (employee.CRB != null)
        {
            DeleteObject(employee.CRB);
            employee.CRB = null;
        }
    }

    var epmpresaContext = (EmpresaEntities)ObjectContext;

    AddMaritalStatus(employee, dto, epmpresaContext);

    AddReligion(employee, dto, epmpresaContext);

    AddEthnicOrigin(employee, dto, epmpresaContext);

    employee.NationalityId = dto.NationalityId;
}

private void AddMaritalStatus(Employee employee, EmployeeBackgroundDTO dto, EmpresaEntities epmpresaContext)
{
    if (!dto.MaritalStatusId.HasValue && !String.IsNullOrWhiteSpace(dto.MaritalStatusDescription))
    {
        var item = epmpresaContext.MaritalStatuses.FirstOrDefault(
            it => it.Description.ToUpper() == dto.MaritalStatusDescription.ToUpper());

        if (item == null)
        {
            employee.MaritalStatus = new MaritalStatus
            {
                Description = dto.MaritalStatusDescription
            };
        }
        else
        {
            employee.MaritalStatus = item;
        }
    }
    else
    {
        employee.MaritalStatusId = dto.MaritalStatusId;
    }
}



Как по мне код, архитектура и сам факт использования RIA как кастрированного WCF сервиса меня напрягают. Если вы смогли прочитать стену текста, надеюсь найдутся силы дать совет. 

Также интересно как у вас на проектах используется RIA и используется ли вообще?
PM MAIL   Вверх
jonie
Дата 10.6.2011, 00:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 2
Всего: 118



v00d00,  у меня есть проект на WCF.RIA и используя IdeaBlade.DevForce for SL.
В обоих проектах в базе около 800 таблиц.

По поводу первого - да, это израильский АдЪ. Я не считал, но около 2 тыс бизнес объектов там точно есть.
По поводу второго - адЪ не многим лучше, но... но бизнес объектов как таковых нет, есть почти что "клиент-сервер"... что в большой степени помогает быстро разработать интерфейс и не думать о "бизнесе", занеся всю логику прямо на клиента.

Важно: оба проекта для intranet-а, поэтому к ним не предъявляется уж сильно больших требований по безопасности.

Цитата(DenWPF @  9.6.2011,  22:42 Найти цитируемый пост)
хнык, дядьки вы о чем =(( 

Опишу проблему (пишу прямо тут, так что мб ошибки).
Есть база ТелефонныйСправочникНаРаботе {юзер, департамент, телефоны}. Примерно такой:
Код

create table Departament(
  DepartamentId int not null primary key identity(1,1), 
  Name nvarchar(200) not null
  IsDeleted bit not null default (0)
)

create table User ( 
  UserId int not null primary key identity(1,1), 
  Name nvarchar(200) not null, 
  DepartamentId int not null REFERENCES Departament(DepartamentId),
  IsDeleted bit not null default (0)
)

create table Phone(
  PhoneId int not null primary key identity(1,1), 
  Number nvarchar(120) not null,
  UserId int not null REFERENCES User(UserId),
  IsDeleted bit not null default (0)
)


Ставится следующая задача:
1) отобразить на странице (неважно SL или asp.net или winforms) три грида "мастер-детали" по таблицам выше
2) при редактировании юзера отображать форму: textbox имя, combobox департамент, grid номера.
3) предусмотреть "простую форму" просмотра юзеров - грид вида {имя, департамент, список телефонов через запятую }

везде в гридах надо уметь делать фильтрацию, группировки. Естественно с пйджингом (у нас работает 10 млн пользователей).

Проблемы:
1) если мы не юзаем wcf ria сервисы, то нам как-то надо сериализовать Expression<Funct<...>> - по сути условие фильтра или группировки. Если юзаем wcf ria то он это делает за нас как я понимаю...
2) проблема с п3 - это то что мы не очень-то хотим передавать на клиента данные, которые нам не нужны дляотображения информации (например DepartamentId).
3) IsDeleted нам как и в случае п2 вообще на клиенте не нужен (у нас например может быть фото юзера - не грузить же его каждый раз при просмотре списка юзеров, а отдельную таблицу заводить - нууу сомнительное удовольствие).


Попробуй реализовать этот простой пример - увидишь что проблем на самом деле не так и мало.... и вот как их решить это и есть обсуждаемый вопрос 8-)

В частности я предложил "начало" - не использовать в DomainSources (основа wcf ria) генерируемые EF-ком сущности. Ибо они не отвечают п2 и п3. Также в этих рамках я сразу предложил отказаться от прямого использования EF в слое Service-ов - это решит проблему "фильтрация по признаку IsDeleted" на уровне Repository.

Также слой репозитория поможет нам разруливать ситуации когда мы тянем данные из view-шек базы (многие реальные запросы очень сложны (у меня есть запросы select на 5 тыс. строк) чтобы писать их в linq. Кроме того linq не даст нам оптимизированный код запроса), а записываем в реальные таблицы (притом, возможно, не в одну).

Такие дела...

Говоря вообще про всякие такие красивые вещи вроде EF или NHibernate - я в реальных бизнес приложениях уже начинаю считать что эти ORM на самом деле тоже зло, ибо зачастую они заводят в жопу, вместо выхода из неё...


Это сообщение отредактировал(а) jonie - 10.6.2011, 00:20


--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
v00d00
Дата 10.6.2011, 00:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 142
Регистрация: 28.12.2005

Репутация: нет
Всего: нет



Цитата

Говоря вообще про всякие такие красивые вещи вроде EF или NHibernate - я в реальных бизнес приложениях уже начинаю считать что эти ORM на самом деле тоже зло, ибо зачастую они заводят в жопу, вместо выхода из неё...


Не нужно быть пуристом, иногда можно EntityQL и SQL загнать. Про запрос на 5к строк, скорее всего у вас все равно хранимка там.

По поводу ria. На вашем проекте как разруливаются графы сущностей? Так и гонится через Ria в сгенереных классах?
Юзаете presntation view?
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | WPF и Silverlight | Следующая тема »


 




[ Время генерации скрипта: 0.1104 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.