我正在尝试找出管理 DbContext 的最佳方法。我见过不进行处置的代码示例,并且我见过人们说这是一个坏主意。我做下面这样的事情合适吗?另外,我应该将每个事务(包括读取)放在新的 DbContext 中吗?这可能是另一个问题,但是有关 EntityState 的部分有必要吗?
public abstract class GenericRepository<T> where T : EntityData
{
protected MyDbContext Context
{
get { return new MyDbContext(); }
}
public T Save(T obj)
{
T item;
using (var context = Context)
{
var set = context.Set<T>();
if (String.IsNullOrEmpty(obj.Id))
item = set.Add(obj);
else
{
item = set.Find(obj.Id);
item = obj;
}
// taken from another code sample
var entry = context.Entry(item);
if (entry.State == EntityState.Detached)
{
//Need to set modified so any detached entities are updated
// otherwise they won't be sent across to the db.
// Since it would've been outside the context, change tracking
//wouldn't have occurred anyways so we have no idea about its state - save it!
set.Attach(item);
context.Entry(item).State = EntityState.Modified;
}
context.SaveChanges();
}
return item;
}
}
编辑
我还有一个扩展类来实现下面的这个功能。上下文没有包含在该查询中的 using 语句中,因此我对我的代码有点怀疑。
public IQueryable<T> FindByAccountId(string accountId)
{
return from item in Context.Set<T>()
let user = UserRepository.FindByAccountId(accountId).FirstOrDefault()
where item.UserId == user.Id
select item;
}
上下文实际上应该基于每个请求。请求到来并创建一个新的上下文。该上下文用于请求的其余部分,然后在请求结束时相应地进行处理。这为您带来了请求长事务的好处,并且正如 HamidP 所强调的那样,您还可以获得缓存实体的额外好处;这意味着加载到上下文中的任何实体都可以通过检索来加载,而无需实体框架查询数据库。
如果您使用任何类型的控制反转容器(例如 StructureMap),那么您可以通过以下配置轻松创建 HTTP 请求绑定上下文:
this.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<DbContext>();
然后,您可以将您的
DbContext
(或其衍生物)注入到您的存储库中,并保留您选择的 IOC 容器以在请求结束时处理上下文。如果您要将相同的上下文注入另一个存储库,那么您将收到相同的上下文实例。
我希望这有帮助!
不,不应该
此处的最佳方法是仅为请求分配上下文。您应该将上下文附加到传入请求,并在请求完成时处理您的上下文。在这种方法中,您可以节省为每个事务创建上下文的开销,并且还可以从上下文的缓存机制中受益,因为每个上下文都位于缓存内,并且请求可以访问它最近访问过的数据。
为每个事务创建一个上下文并不比拥有一个长生命周期上下文那么糟糕! 永远不要这样做,长生命周期的上下文会导致许多并发问题,缓存会变得陈旧,内存消耗会越来越高,你应该在未来奇迹般地维护你的应用程序。
当使用 EF 上下文时,它应该仅用于对数据库的单次访问,并在提交更改后重新创建。
应该是这样的:
var myContext = new MyDBContext();
using (myContext)
{
...
...
}
上下文不应用于您的服务的整个请求。仅用于对数据库的单次访问。