2014-12-18 4 views
2

Я знаю, что название звучит как дубликат нескольких существующих сообщений, но я прочитал немало из них, и моя ситуация на самом деле совсем другая. Я был бы очень признателен, если бы кто-нибудь, кто сталкивался с Entity Framework, мог предложить некоторые рекомендации по лучшей архитектуре для следующего сценария.Entity Framework с трехуровневой архитектурой, разными объектами по доменам

У меня есть приложение wpf с трехуровневой компоновкой, уровнем доступа к данным, бизнес-логикой и уровнем представления пользовательского интерфейса. Пользовательский интерфейс использует MVVM. DAL использует Entity Framework. Пользовательский интерфейс и уровень доступа к данным имеют свои собственные модели, UIModel и DataModel.

В текущем проекте используется глобальная платформа DbContext для Entity Framework в приложении. Для простой операции обновления объект извлекается из базы данных в виде DataModel, преобразуется в GUIModel, подключается к ViewModel и View для обновлений и преобразуется обратно в DataModel для обновления в базе данных. И вот проблема: когда новый DataModel создается из преобразования, он больше не связан с исходным объектом, и Entity Framework не может выполнить обновление, поскольку теперь он имеет две повторяющиеся модели одного и того же первичного ключа, подключенного к одному и тому же DbContext ,

Я сделал несколько исследований и нашел пару возможных способов исправить это. Один из них состоит в том, чтобы использовать единый модельный объект для всех слоев вместо разделения GUIModel и DataModel и разбивать глобальный DbContext на единицу работы. Это, по-видимому, очень общий дизайн, но мои проблемы с этим подходом заключаются в том, что слияние GUIModel и DataModel нарушает разделение обязанностей, а для использования единицы работы требуется Business Layer для управления временем жизни DbContext, что также размывает границу между BLL и DAL.

Второй вариант заключается в использовании локального DbContext для каждого запроса базы данных с блоком using. Это, пожалуй, большая эффективность памяти. Но делать это таким образом делает ленивую загрузку невозможной, и яростная загрузка всех свойств навигации в каждом запросе может повлиять на производительность. Также краткосрочные DbContexts требуют полной работы в отключенном графике, что становится довольно сложным с точки зрения отслеживания изменений.

Третьей возможностью было бы кэширование всех исходных DataModels и обновление этих объектов после обновления.

Я новичок в платформе Entity Framework, и я уверен, что должны быть и другие способы исправить эту проблему. Я буду очень признателен, если кто-нибудь может предложить некоторые идеи относительно наилучшего подхода к этому.

ответ

0

Я бы рекомендовал использовать отдельные бизнес-объекты, как описано в вашей второй альтернативе. В многоуровневом сценарии вы создадите повторно используемые объекты, которые поддерживают ваш прецедент с точки зрения пользовательского интерфейса, моделируя поведение вашего бизнес-домена (как вы их называете «GUIModel»). Эти модели должны быть сосредоточены на поведении вашей системы и содержать только данные, необходимые для поддержки такого поведения. Это прямо контрастирует с классами сущностей, которые фокусируются на данных.

Пример: база данных Northwind, таблица клиентов. Объект будет классом, содержащим все свойства клиента, возможно, имеющим свойства навигации к связанным вещам. Вы действительно хотите использовать эту модель, когда вам нужно отобразить список сокращенной информации о клиентах в раскрывающемся окне автоматического завершения поиска? Вы хотите использовать ту же модель для отображения клиентов вместе со своими данными агрегированных счетов в сетке? Вам нужно будет загрузить всю информацию о клиенте вместе с соответствующими счетами в ваш уровень представления. Вы, вероятно, не хотите этого делать.

Если вы имели различные модели для различных случаев применения, то имело бы больше смысла от объекта ориентированной точки зрения:

Класса CustomerSearchResult: Id, Name. GetCustomerEdit.

Класс CustomerInvoiceInfo: Id, Name, Совокупные значения счетов-фактур. GetCustomerEdit.

Класс CustomerEdit: Все объекты, которые вы хотите отображать и редактировать, временную метку для оптимистичных проверок параллелизма. Логика отслеживания изменений, логика проверки. Методы, которые моделируют поведение, которое вам необходимо при редактировании клиента.

Класс CustomerEntity: это ваш объект данных, который напоминает таблицу клиентов. Вы используете его как DTO, чтобы инициализировать другие объекты из базы данных или вносить изменения в базу данных. Вы не отправляете его по проводам.

Таким образом, когда вы перейдете на уровень доступа к данным, вы можете поместить свой DbContext в блоки using и уважать единицу шаблона работы. Конечно, вам нужно будет отражать изменения, внесенные в экземпляр CustomerEdit путем создания нового CustomerEntity от нее и приклеить ее в контексте, как изменение:

context.Entry(entity).State = EntityState.Modified; 
context.SaveChanges(); 

Это кажется сложным и обременительным на первый, но на самом деле, Entity Framework не содержит никакой магии, которая очень помогает вам в отключенном (n-уровневом) сценарии. Если вы попытаетесь использовать такие вещи, как ленивая загрузка или хранение экземпляров DbContext, все время быстро выходит из-под контроля.

Если вы ищете инфраструктуру, которая помогает в создании бизнес-объектов и поддерживает многоуровневые архитектуры, загляните в CSLA.net. Отказ от ответственности: многим людям это не нравится. Это будет хуже, если использовать неправильно. Тем не менее, это помогло мне в некоторых проектах, и я доволен этим.

0
  1. Вы можете прикрепить объект к существующему DbContext, используя следующий код, также here хорошее сообщение о сущностях штатах от MSDN
     
    var existingBlog = new Blog { BlogId = 1, Name = "ADO.NET Blog" }; 
    using (var context = new BloggingContext()) 
    { 
        context.Entry(existingBlog).State = EntityState.Modified; 
        // Do some more work...
    context.SaveChanges(); }
  2. Что касается 3-х уровневая, я хотел бы начать со небольшое описание с контекстом .net для каждого уровня

    • презентации, это слой, который возвращает результаты в использовании и может быть в виде ASP.Net веб-сайта, Windows Forms, Web Api, службы WCF или все, что угодно lse
    • Бизнес должен включать в себя модель домена вашего бизнеса, бизнес-логику и службы, которые предоставляют бизнес для нескольких объектов домена.
    • Доступ к данным/постоянство. Этот уровень должен включать в себя логику для сохранения и получения модели домена в прочную такие как DB, файловая система, ...

    Как правило, общая проблема заключается в том, какая модель входит в какой слой, например, класс X переходит в презентацию или бизнес, и я рекомендую простой способ помочь вам принять ваши решение, которое вводит новый уровень родства, так спросите себя, если вы хотите создать еще один слой презентации, поскольку консоль вместо окон вы будете копировать и вставлять tha t логики в новый слой? если да, тогда есть хорошая вероятность, что ваши классы не в нужном месте.

Наконец некоторые конкретные рекомендации,

  • Держите каждый слой имеют свои модели, поскольку каждый слой имеет уникальную ответственность, также есть хорошие структуры, которые могли бы помочь вам в отображения между моделями, такими, как AutoMapper
  • Не переносите модели Entity Framework во всех слоях, так как это разрушит разделение проблем, и у вас будет все больше и больше проблем, если вы включите ленивую загрузку.
  • Старайтесь избегать ленивой загрузки, если вы не знаете, что делаете, одна из распространенных ошибок - это выбор N + 1 и here - хорошая статья, описывающая это.
  • Кроме того, если у вас есть сложный бизнес, пытаюсь отделить от запроса системы и обновления, применяя CQRS шаблон, и есть некоторые структуры, которые могут помочь вам, таким как Dapper
1

Лучше подход, когда вы переход на вызов обновления в вашем репозитории сначала получает объект по первичному ключу, теперь вы находитесь в dbContext с требуемым объектом, который нужно обновить, а затем назначьте обновленные поля и обновите контекст.

Вот код:

public void UpdateEntity(Entity updatedEntity) 
    { 
     using (var db = new DBEntities()) 
     { 
      var entity = db.Entities.Find(updatedEntity.Id); 
      if (entity!= null) 
      { 
       entity.Name = updatedEntity.Name; 
       entity.Description = updatedEntity.Description; 
       entity.LastModifiedBy = updatedEntity.LastModifiedBy; 
       entity.Value = updatedEntity.Value; 
       entity.LastModifiedOn = DateTime.Now; 
       db.SaveChanges(); 
      } 
     } 
    } 
Смежные вопросы