Оптимальная практика для платформы Entity Framework в бизнес-логике?

Я использую инфраструктуру Entity в первый раз и хотел бы знать, использую ли я в лучшей практике.

Я создал отдельный class в моей бизнес-логике, который будет обрабатывать контекст сущности. проблема у меня есть, во всех видео, которые я видел, они обычно переносят контекст в оператор using, чтобы убедиться, что он закрыт, но, очевидно, я не могу сделать это в своей бизнес-логике, поскольку контекст будет закрыт, прежде чем я смогу на самом деле используй это?

Так это нормально, что я делаю? Несколько примеров:

public IEnumerable
GetLatestArticles(bool Authorised) { var ctx = new ArticleNetEntities(); return ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate); } public IEnumerable
GetArticlesByMember(int MemberId, bool Authorised) { var ctx = new ArticleNetEntities(); return ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate); }

Я просто хочу удостовериться, что я не строю то, что будет умирать, когда многие люди его используют?

Это действительно зависит от того, как вы хотите открыть хранилище / хранилище данных.

Не уверен, что вы подразумеваете под «контекст будет закрыт, поэтому я не могу вести бизнес-логику». Сделайте свою бизнес-логику внутри оператора using. Или если ваша бизнес-логика находится в другом classе, то давайте продолжим. 🙂

Некоторые люди возвращают конкретные коллекции из своего репозитория, и в этом случае вы можете обернуть контекст в инструкции using:

 public class ArticleRepository { public List
GetArticles() { List
articles = null; using (var db = new ArticleNetEntities()) { articles = db.Articles.Where(something).Take(some).ToList(); } } }

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

Вы можете инкапсулировать всю свою бизнес-логику в оператор using.

Недостатки – ваш repository становится осведомленным о бизнес-логике, которую мне лично не нравится, и вы получаете другой метод для каждого конкретного сценария.

Второй вариант – новый контекст как часть репозитория и сделать его реализацией IDisposable.

 public class ArticleRepository : IDisposable { ArticleNetEntities db; public ArticleRepository() { db = new ArticleNetEntities(); } public List
GetArticles() { List
articles = null; db.Articles.Where(something).Take(some).ToList(); } public void Dispose() { db.Dispose(); } }
- public class ArticleRepository : IDisposable { ArticleNetEntities db; public ArticleRepository() { db = new ArticleNetEntities(); } public List
GetArticles() { List
articles = null; db.Articles.Where(something).Take(some).ToList(); } public void Dispose() { db.Dispose(); } }

А потом:

 using (var repository = new ArticleRepository()) { var articles = repository.GetArticles(); } 

Или третий вариант (мой любимый), используйте инъекцию зависимости . Развяжите всю контекстную работу из своего репозитория и разрешите контейнеру DI обрабатывать ресурсы:

 public class ArticleRepository { private IObjectContext _ctx; public ArticleRepository(IObjectContext ctx) { _ctx = ctx; } public IQueryable
Find() { return _ctx.Articles; } }

Ваш выбранный контейнер DI вставляет конкретный объект ObjectContext в экземпляр Репозитория с настроенным временем жизни (Singleton, HttpContext, ThreadLocal и т. Д.) И удаляет его на основе этой конфигурации.

У меня есть настройка, поэтому каждый HTTP-запрос получает новый контекст. Когда запрос будет завершен, мой контейнер DI автоматически удалит контекст.

Я также использую шаблон «Единица работы» здесь, чтобы позволить нескольким репозиториям работать с одним контекстом объекта.

Возможно, вы также заметили, что я предпочитаю возвращать IQueryable из моего репозитория (в отличие от конкретного списка). Гораздо более мощный (но рискованный, если вы не понимаете последствий). Мой уровень сервиса выполняет бизнес-логику на IQueryable, а затем возвращает конкретную коллекцию в пользовательский интерфейс.

Это мой самый мощный вариант, поскольку он позволяет использовать простой repository, а Unit Of Work управляет контекстом, уровень обслуживания управляет бизнес-логикой, а контейнер DI обрабатывает время жизни / распоряжение ресурсами / объектами.

Дайте мне знать, если вы хотите получить дополнительную информацию об этом – так как этого достаточно, даже больше, чем этот удивительно длинный ответ. 🙂

У меня бы был ctx как приватная переменная в каждом classе, а затем каждый раз создавал новый экземпляр, а затем удалял, когда закончил.

 public class ArticleService { private ArticleEntities _ctx; public ArticleService() { _ctx = new ArticleEntities(); } public IEnumerable
GetLatestArticles(bool Authorised) { return _ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate); } public IEnumerable
GetArticlesByMember(int MemberId, bool Authorised) { return _ctx.Articles.Where(x => x.MemberID == MemberId && x.IsApproved == Authorised).OrderBy(x => x.ArticleDate); } public void Dispose() { _ctx.Dispose(); _ctx = null; } }

Тогда при вызове этого.

 ArticleService articleService = new ArticleService(); IEnumerable
article = articleService.GetLatestArticles(true); articleService.Dispose(); // killing the connection

Таким образом вы также можете добавить / обновить другие объекты в одном контексте и вызвать метод сохранения, который сохраняет любые изменения в db через Entity.

По моему опыту этот код не очень хорош, потому что вы теряете способность перемещаться по свойствам навигации.

 public List getArticles( ){ using (var db = new ArticleNetEntities()) { articles = db.Articles.Where(something).ToList(); } } 

Используя этот подход, вы не можете использовать следующий код, потому что a.Members всегда имеет значение null (контекст db близок и не может получить данные автоматически).

 var articles = Data.getArticles(); foreach( var a in articles ) { if( a.Members.any(p=>p.Name=="miki") ) { ... } else { ... } } } 

Использование только глобального контекста db – плохая идея, потому что вы должны использовать функцию удаления изменений

в точке вашего приложения, сделайте это, но не сохраняйте изменения и не закрывайте окно.

 var article= globalcontext.getArticleByID(10); article.Approved=true; 

то в другой точке приложения вы выполняете некоторую операцию и сохраняете

 //..... something globalcontext.saveChanges(); 

в этом случае свойство, утвержденное предыдущей статьей, настроено на изменение структуры объекта. Когда вы сохраняете, одобренный присваивается значение true !!!

Лучший подход для меня – использование 1 контекста для каждого classа. Если вам нужно, вы можете передать контекст другому внешнему методу

 class EditArticle { private DbEntities de; private currentAricle; public EditArticle() { de = new DbEntities; //inizialize on new istance } loadArticleToEdit(Articele a){ // a is from another context currentArticle= de.Article.Single(p=>p.IdArticle==a.IdArticle){ } private saveChanges(){ ... pe.saveChanges(); } } 

То, что вы также можете сделать, это сохранить ваш контекст на более высоком уровне.

Например, вы можете иметь статический class, сохраняющий текущий контекст:

 class ContextManager { [ThreadStatic] public static ArticleEntities CurrentContext; } 

Затем где-то снаружи вы делаете что-то вроде этого:

 using (ContextManager.CurrentContext = new ArticleEntities()) { IEnumerable
article = articleService.GetLatestArticles(true); }

Затем, внутри GetLastestArticles, вы просто используете тот же ContextManager.CurrentContext.

Конечно, это только основная идея. Вы можете сделать это намного более эффективным, используя поставщиков услуг, IoC и т. Д.

Вы можете начать подготовку Entity Framework с уровня доступа к данным, создав общий class репозитория для всех необходимых функций Entity Framework. Затем вы можете использовать его в Business layer (Encapsulated)

Вот лучшие практики, которые я использовал для Entity Framework в области данных, бизнеса и пользовательского интерфейса

Методы, используемые для этой практики:

  1. Применение принципов архитектуры SOLID
  2. Использование шаблона проекта репозитория
  3. Только один class (и вы найдете его готовым)