【问题标题】:DbContext global scope vs method level scopeDbContext 全局范围与方法级别范围
【发布时间】:2016-08-14 00:41:13
【问题描述】:

如果有一个类作为数据访问层,并为实体提供 CRUD 操作的功能,在考虑性能和多线程环境时哪个版本是最佳实践(即此类的方法由多个线程调用同时 。 。)。 . .

版本 1:

在类级别创建的 DbContext,由所有方法共享。 .

    class EmployeeService{

     private DbContext db=new DbContext();

     public  Employee GetEmployee(int id)

         return db.Employees.Find(id);
     }


     public void AddEmployee(Employee employee){

         db.Employees.Add(employee);
         db.SaveChanges();
     }
}

版本 2:

每个方法调用的 DbContext。 .

class EmployeeService{

     public  Employee GetEmployee(int id){
        using(DbContext db=new DbContext()){
         return db.Employees.Find(id);
        }
     }


     public void AddEmployee(Employee employee){         
        using(DbContext db=new DbContext()){                 
            db.Employees.Add(employee);
           db.SaveChanges();    
        }
     }
}

更新: 可能是发布的问题范围过于笼统,导致需要考虑几点。

兴趣点是,实例化 DbContext 对象的成本。 它可以根据请求创建(版本 2)还是重量级对象,最好创建少量实例并在不同的实例之间共享它们调用(版本 1)

【问题讨论】:

  • 以上都不是。您设置它的方式是不可测试的,并且返回模型的实体不是一个好主意。根据特定需求转换为更小的 DTO。例如,您的 GetEmployee 转换为数据存储中的 SELECT * 等效项。您真的在每种方法中都需要它吗?我会选择并改进选项 2。
  • V2 更适合多线程,EF 上下文不应该被共享。 stackoverflow.com/a/6126669/3142139

标签: c# entity-framework entity-framework-6


【解决方案1】:

甚至还有第三种基于手动或自动的方法dependency injection

public interface ISomeService 
{
     // Interface members
}

public class SomeService : ISomeService
{
    public SomeService(DbContext dbContext)
    {
         DbContext = dbContext;
    }

    private DbContext DbContext { get; }
}

那么,SomeService 将不负责定义 injected DbContext 的生命周期,但它是一个外部类。

这样,您的服务专注于做应做的事情(处理域和读取/写入数据)。

根据执行环境,您将需要不同的DbContext 生活方式:每个服务实例、每个请求、每个线程...这里有很多选择,具体取决于具体情况。

也许您没有考虑另一种情况:两个或多个服务之间的共享事务。您需要将实例化DbContext 的责任移交给上层,然后在所有参与的服务上注入相同的DbContext,然后全局确认或放弃整个事务。

【讨论】:

  • 然后,外部人员处理了上下文.. 现在怎么办?至少保持私密。并且需要释放 DbContext 的类级别实例,因此您需要一个 IDisposable 实现。这样你最好注入一个工厂并在每个方法中进行创建,以便范围保持在方法中。
  • @PeterBons 通常,您将使用与技术无关的接口公开您的服务。您无权访问整个注入的DbContext。另一方面,如果服务永远不会释放其关联的DbContext,为什么它应该是IDisposable
  • @PeterBons 我已经创建了DbContext 属性private,顺便说一句,我并不担心这一点,因为我在上面解释了你的原因......你永远不应该公开服务实现,但是服务接口。
【解决方案2】:

EF 的性能由几个因素组成,DbContext 的范围就是其中之一。

有关范围的一些背景信息可在此处找到:https://msdn.microsoft.com/en-us/data/jj729737.aspx

范围不仅与性能有关,还与返回的对象有关。如果在您访问某些导航属性之前应用了延迟加载并且 DbContext 已被释放,则会出现异常。见http://www.entityframeworktutorial.net/EntityFramework4.3/lazy-loading-with-dbcontext.aspx

你可以这样写代码:

public class EmployeeService
{
    public EmployeeDto GetEmployee(int id)
    {
        using(DbContext db=new DbContext())
        {
            return db.Employees.Select(e =>
                new EmployeeDto
                {
                    Id = e.Id,
                    Name = e.Name,
                    Department = e.Department.Name
                }).First(e => e.Id == id);
        }
    }
}

public class EmployeeDto
{
    public int Id { get;set;}
    public string Name { get;set;}
    public string Department { get;set;}
}

因此,您可以使用投影来限制返回的数据集,而不是返回整个对象。您可以使用它来减少数据库服务器上的负载,因为查询将不那么冗长,但它也有助于在处理上下文之前加载所有必需的数据。有关生成的查询的一些示例,请参阅 http://www.entityframeworktutorial.net/querying-entity-graph-in-entity-framework.aspx

所以我的建议是限制DbContext 的范围。您可以注入 DbContext 但您将无法控制 DbContext 这将导致如下错误:http://wallacekelly.blogspot.nl/2012/01/linq-to-entities-objectdisposedexceptio.html

但这一切都取决于您的需求以及您正在构建的服务类型。

【讨论】:

  • +1 for 你可以注入 DbContext 但你将无法控制 DbContext 这将导致错误。
猜你喜欢
  • 2014-02-22
  • 1970-01-01
  • 2012-03-08
  • 2012-05-13
  • 1970-01-01
  • 2019-08-08
  • 2015-12-02
  • 1970-01-01
  • 2011-03-17
相关资源
最近更新 更多