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

时间:2010-10-18 08:18:06

标签: c# asp.net linq 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);
    }

我只是想确保当很多人使用它时,我不会构建一些会死的东西?

5 个答案:

答案 0 :(得分:64)

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

不确定你的意思是“上下文将被关闭,因此我不能做业务逻辑”。在您的业务逻辑里面使用using语句。或者,如果您的业务逻辑属于不同的类,那么让我们继续。 :)

有些人从他们的Repository返回具体的集合,在这种情况下你可以在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语句中。

缺点 - 您的存储库会发现我个人不喜欢的业务逻辑,并且您最终会针对每个特定方案使用不同的方法。

第二个选项 - 在Repository中新建一个上下文,并使其实现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请求都会获得一个新的Context。请求完成后,我的DI容器将自动处理上下文。

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

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

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

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

答案 1 :(得分:3)

我会将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保存对db的任何更改。

答案 2 :(得分:3)

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

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

使用此方法,您无法使用以下代码,因为a.Members始终为null(db context is close并且无法自动获取数据)。

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 :(得分:0)

您还可以做的是将您的背景存储在更高的级别。

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

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

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

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

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

当然,这只是基本的想法。通过使用服务提供商,IoC等,您可以使其更加可行。

答案 4 :(得分:0)

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

中使用它

以下是我在数据,业务和UI层中用于Entity Framework的最佳实践

用于此练习的技巧:

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