谢谢你的期待.
背景
在我的.NET应用程序中,我通常有一个包含我的业务方法的业务逻辑层(BLL)和一个数据访问层(DAL),它包含我的Entitiy类和处理原子实体的任何方法(即单个实体的CRUD方法).这是一种非常典型的设计模式.
这是我的意思的伪代码示例:
BLL
public static int CreateProduct(ProductModel product){
return DAL.SomeClass.CreateProduct(new DAL.Product{
Name = product.Name,
Price = product.Price
});
}
DAL
public int CreateProduct(Product p){
var db = new MyDataContext();
db.Products.AddObject(p);
db.SaveChanges();
return p.Id;
}
这个简单的例子没问题.
理想情况下,实例化数据上下文和使用该数据上下文的所有业务都存在于DAL中.但是,如果我尝试处理稍微复杂的对象,这就成了一个问题:
BLL
public static int CreateProduct(ProductModel product){
return DAL.SomeClass.CreateProduct(new DAL.Product{
Name = product.Name,
Price = product.Price,
ProductType = DAL.SomeClass.GetProductTypeById(product.ProductTypeId) //<--PROBLEM
});
}
现在,我没有保存实体,而是收到以下错误:
IEntityChangeTracker的多个实例不能引用实体对象
好的,所以处理这个问题的答案是将一个公共数据上下文传递给两个调用:
BLL
public static int CreateProduct(ProductModel product){
using{var db = new DAL.MyDataContext()){
return DAL.SomeClass.CreateProduct(new DAL.Product{
Name = product.Name,
Price = product.Price,
ProductType = DAL.SomeClass.GetProductTypeById(product.ProductTypeId, db) //<--CONTEXT
}, db); //<--CONTEXT
}
}
问题
这解决了眼前的问题,但现在我的参考透明度被吹了,因为我必须:
>在BLL中实例化数据上下文
>将数据上下文从BLL传递给DAL
>在DAL中创建接受数据上下文作为参数的重写方法.
这对某些人来说可能不是问题,但对我而言,因为我以更实用的方式编写代码,这是一个大问题.毕竟它是完全相同的数据库,那么为什么我不能处理独特的实体而不管它们的数据上下文实例呢?
其他说明
我意识到有些人可能只想说为所有调用创建一个公共数据上下文.这样做不会因为多种原因这样做是不好的做法,并最终导致连接池溢出. See this great answer for more details.
任何建设性的意见表示赞赏.
最佳答案 就个人而言,我通过静态方法跟踪我的工作单元并将数据上下文与之关联起来.如果您不是在谈论具有长生命周期的操作,例如我当前的项目,ASP.NET应用程序,其中每个请求都是(大部分)不同的单元,并且请求的开始和结束与单元的开始/结束一致,那么这很有用. .我将数据上下文存储在请求CurrentContext中,如果您不熟悉它,它基本上是由系统管理的字典,它分配静态方法可访问的特定于请求的存储.这项工作已经为我做了,但你可以找到很多实现你自己的工作单元模式的例子.
One DbContext per web request… why?
另一个同样可行的答案是注射.用于此目的(注入datacontext),它基本上模仿了你在问题结束时编写的代码,但屏蔽了你不喜欢的“非功能性”东西.
是的,您只访问一个数据库,但如果仔细观察,您会看到数据库不是此处的约束.这是由缓存引起的,缓存旨在允许多个不同的并发数据副本.如果您不希望允许,那么您可以获得许多其他解决方案.