Entity Framework Лучшие практики в бизнес-логике? - PullRequest
31 голосов
/ 18 октября 2010

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

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

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

    public IEnumerable<Article> GetLatestArticles(bool Authorised) 
    {
        var ctx = new ArticleNetEntities();
        return ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
    }

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

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

Ответы [ 5 ]

64 голосов
/ 18 октября 2010

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

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

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

public class ArticleRepository
{
   public List<Article> GetArticles()
   {
      List<Article> articles = null;

      using (var db = new ArticleNetEntities())
      {
         articles = db.Articles.Where(something).Take(some).ToList();
      }
   }
}

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

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

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

Второй параметр - создает новый контекст как часть репозитория и делает его реализующим IDisposable.

public class ArticleRepository : IDisposable
{
   ArticleNetEntities db;

   public ArticleRepository()
   {
      db = new ArticleNetEntities();
   }

   public List<Article> GetArticles()
   {
      List<Article> 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<Article> Find()
   {
      return _ctx.Articles;
   }
}

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

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

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

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

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

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

3 голосов
/ 26 ноября 2011

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

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

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

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

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

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

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

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

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

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

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

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();
    }
}
3 голосов
/ 18 октября 2010

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

public class ArticleService
{
    private ArticleEntities _ctx;

    public ArticleService()
    {
        _ctx = new ArticleEntities();
    }

    public IEnumerable<Article> GetLatestArticles(bool Authorised) 
    {            
        return _ctx.Articles.Where(x => x.IsApproved == Authorised).OrderBy(x => x.ArticleDate);
    }

    public IEnumerable<Article> 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> article = articleService.GetLatestArticles(true);
articleService.Dispose(); // killing the connection

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

0 голосов
/ 05 февраля 2017

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

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

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

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

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

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

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

Затем, где-то за пределами вассделать что-то вроде этого:

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...