Подтвердить что ты не робот

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

Я использую фреймворк 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);
    }

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

4b9b3361

Ответ 1

На самом деле это зависит от того, как вы хотите открыть хранилище репозитория/данных.

Не уверен, что вы подразумеваете под "контекст будет закрыт, поэтому я не могу вести бизнес-логику". Сделайте свою бизнес-логику внутри оператора 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 в экземпляр Репозитория с настроенным временем жизни (Singleton, HttpContext, ThreadLocal и т.д.) и избавит его от этой конфигурации.

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

Я также использую шаблон Unit of Work здесь, чтобы позволить нескольким репозиториям работать с одним Контекстом объектов.

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

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

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

Ответ 2

У меня будет 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

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

Ответ 3

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

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

Используя этот подход, вы не можете использовать следующий код, потому что a.Members всегда является нулевым (контекст 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 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();
    }
}

Ответ 4

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

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

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

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

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

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

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

Ответ 5

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

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

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

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