【问题标题】:Entity Framework Best Practices In Business Logic?业务逻辑中的实体框架最佳实践?
【发布时间】:2011-04-26 19:53:01
【问题描述】:

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

我在我的业务逻辑中创建了一个单独的类来处理实体上下文。我遇到的问题是,在我看过的所有视频中,他们通常将上下文包装在 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);
    }

我只是想确保我不会构建在很多人使用时会死掉的东西?

【问题讨论】:

    标签: c# asp.net linq entity-framework


    【解决方案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();
    }
    

    或者第三个选项(我最喜欢的),使用依赖注入。将所有上下文工作与您的 Repository 分离,并让 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 上执行业务逻辑,然后将具体集合返回给 UI。

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

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

    【讨论】:

    • 不用担心。 :) 看看我的其他一些问题/答案——我最近一直在处理这个问题。我不打算让这个答案那么长,但我想我被带走了——毕竟,这是一个相当复杂的话题。 :)
    • 您能否详细解释一下您是如何使用工作单元模式的?我以为 EF ObjectContext 实际上是一个 UoW。
    • 我想知道这个最终版本增加了什么?它似乎只是实体框架之上的一个额外抽象,这已经是一个抽象,因为实体框架本身已经实现了工作单元和存储库。为什么使用这个存储库类而不是实体数据上下文本身?
    【解决方案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
    

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

    【讨论】:

    • 感谢您的示例 :) 感谢
    【解决方案3】:

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

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

    使用这种方法,您不能使用以下代码,因为 a.Members 始终为空(数据库上下文已关闭且无法自动获取数据)。

    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();
        }
    }
    

    【讨论】:

      【解决方案4】:

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

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

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

      然后,在外面的某个地方做这样的事情:

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

      然后,在 GetLastestArticles 中,您只需使用相同的 ContextManager.CurrentContext。

      当然,这只是基本概念。通过使用服务提供商、IoC 等,您可以使这更加可行。

      【讨论】:

        【解决方案5】:

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

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

        用于此练习的技术:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-06-21
          • 1970-01-01
          • 2011-04-15
          • 2013-09-14
          • 2012-03-08
          • 1970-01-01
          • 2012-01-12
          • 1970-01-01
          相关资源
          最近更新 更多