代码之家  ›  专栏  ›  技术社区  ›  YodasMyDad

业务逻辑中的实体框架最佳实践?

  •  31
  • YodasMyDad  · 技术社区  · 14 年前

    我第一次使用实体框架,想知道我是否在最佳实践中使用。

    我在业务逻辑中创建了一个单独的类,它将处理实体上下文。我遇到的问题是,在我看过的所有视频中,他们通常将上下文包装在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 回复  |  直到 7 年前
        1
  •  64
  •   RPM1984    14 年前

    这实际上取决于如何公开存储库/数据存储。

    不确定您所说的“上下文将关闭,因此我不能进行业务逻辑”。做你的业务逻辑 里面 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容器将使用配置的生存期(singleton、httpcontext、threadlocal等)将具体的objectcontext注入到存储库的实例化中,并根据该配置处置它。

    我有它的设置,所以每个HTTP请求都会得到一个新的上下文。当请求完成时,我的DI容器将自动释放上下文。

    我还使用这里的工作单元模式来允许多个存储库使用一个对象上下文。

    您可能还注意到我更喜欢从我的存储库返回iQueryable(而不是具体的列表)。更强大(如果你不理解其含义,那么风险也很大)。我的服务层在IQueryable上执行业务逻辑,然后将具体的集合返回给UI。

    这是我迄今为止最强大的选项,因为它允许一个简单的heck存储库,工作单元管理上下文,服务层管理业务逻辑,DI容器处理资源/对象的生存期/处置。

    如果你想要更多的信息,请告诉我——因为有很多信息,甚至比这个令人惊讶的长答案还要多。:)

        2
  •  3
  •   Tim B James    14 年前

    我将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
    

    这样,您还可以在同一上下文中添加/更新其他对象,并调用保存方法,该方法通过实体保存对数据库的任何更改。

        3
  •  3
  •   Sentry    8 年前

    根据我的经验,这段代码并不好,因为您失去了通过导航属性导航关系的能力。

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

    使用这种方法,您不能使用以下代码,因为a.members始终为空(db context关闭,无法自动获取数据)。

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

    只使用全局数据库上下文是一个坏主意,因为必须使用delete changes函数

    在应用程序的某个点上,执行此操作,但不保存更改并关闭窗口。

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

    然后在另一个应用程序点中进行一些操作并保存

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

    在这种情况下,上一篇文章批准的属性设置为“由实体框架修改”。保存时,已批准设置为真!!!!

    对我来说,最好的方法是每个类使用一个上下文 如果需要,可以将上下文传递给其他外部方法

    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
  •  0
  •   Pieter van Ginkel    14 年前

    您还可以在更高的级别存储上下文。

    例如,您可以有一个静态类来存储当前上下文:

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

    然后,在外面的某个地方,你会这样做:

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

    然后,在getlastestarticles中,只使用相同的contextmanager.currentcontext。

    当然,这只是基本的想法。通过使用服务提供商、IOC等,您可以使这一点变得更加可行。

        5
  •  0
  •   Graham francescalus    7 年前

    通过为所有必需的实体框架函数创建通用存储库类,可以从数据访问层开始准备实体框架。然后您可以在业务层中使用它(封装的)

    以下是我在数据层、业务层和UI层中用于实体框架的最佳实践

    用于此实践的技术:

    1. 应用 SOLID architecture principles
    2. 使用存储库设计模式
    3. 只上一节课(你会发现它已经准备好了)