我在我的网络应用程序中使用实体框架6和Autofac。
我用DbContext注入工作单元,两者都是外部拥有的,所以我可以自己处理它们。
DbContext注册了PerLifetimeScope,
工作单位是工厂,因此按照依赖性进行注册。
当执行第一个http Get动作时,everthing工作正常,我看到响应来自db的工作单元是很好的。
我的问题是每当我执行第二个请求时,出于某种原因,在返回IQueryable之前就会处理上下文。所以我得到了一个说法:
由于DbContext已被处理,因此无法执行该操作。
例如 - 调用GetFolders方法第一次工作,然后失败..
我看到上下文处理得太早,我不明白的是在第二次请求中过早触发的内容..
public interface IUnitOfWork : IDisposable
{
bool Commit();
}
public EFUnitOfWork : IUnitOfWork
{
public IRepository<Folder> FoldersRepository {get; set;}
public IRepository<Letter> LettersRepository {get; set;}
private readonly DbContext _context;
public EFUnitOfWork(DbContext context, IRepository<Folder> foldersRepo, IRepository<Letter> lettersRepo)
{
_context = context;
_foldersRepo = foldersRepo;
LettersRepository = lettersRepo;
}
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
_context.Dispose();
}
disposed = true;
}
}
public bool Commit()
{
try
{
return SaveChanges() > 0;
}
catch (DbEntityValidationException exc)
{
// just to ease debugging
foreach (var error in exc.EntityValidationErrors)
{
foreach (var errorMsg in error.ValidationErrors)
{
logger.Log(LogLevel.Error, "Error trying to save EF changes - " + errorMsg.ErrorMessage);
}
}
return false;
throw exc;
}
}
}
public class Repository<T> : IRepository<T>
{
protected readonly DbContext Context;
protected readonly DbSet<T> DbSet;
public EFRepository(DbContext context)
{
Context = context;
}
public IQueryable<T> Get()
{
return DbSet;
}
public void Add(T item)
{
DbSet.Add(item);
}
public virtual Remove(T item)
{
DbSet.Remove(item);
}
public void Update(T item)
{
Context.Entry(item).State = EntityState.Modified;
}
public T FindById(int id)
{
return DbSet.Find(id);
}
}
public class DataService : IDataService
{
private Func<IUnitOfWork> _unitOfWorkFactory;
public (Func<IUnitOfWork> unitOfWorkFactory)
{
_unitOfWorkFactory = unitOfWorkFactory;
}
public List<FolderPreview> GetFolders()
{
using(unitOfWork = _unitOfWorkFactory())
{
var foldersRepository = unitOfWork.FoldersRepository;
var foldersData = foldersRepository.Get().Select(p => new FolderPreview
{
Id = p.Id,
Name = p.Name
}).ToList();
return foldersData;
}
}
}
public class FolderPreview
{
public int Id {get; set;}
public string Name {get; set;}
}
Startup code:
{
_container.RegisterGeneric<IRepository<>,Repository<>>().InstancePerLifetimeScope();
_container.RegisterType<IDataService, DataService>().SingleInstance();
_container.RegisterType<EFUnitOfWork, IUnitOfWork>().PerDepnendecny().ExternallyOwned();
_container.RegisterType<DbContext, MyDbContext>().InstancePerLifetimeScope().ExternallyOwned();
}
这与单身人士有什么关系?我的几乎所有应用程序都是单例,DataService也是Singleton。任何人吗?
谢谢!
答案 0 :(得分:1)
问题在于,您每个请求只实例化一个Repository
和一个DbContext
,但每次实例化一个新的IUnitOfWork
。
所以当你致电GetFolders
时,你正在创建一个新的IUnitOfWork
并处理它(它处理DbContext
-on IUnitOfWork.Dispose()
- ):所以当你致电{{ 1}}再次,当你创建第二个GetFolders
,因为它是相同的生命周期范围,它注入已经创建的存储库和已经创建的IUnitOfWork
,它被处置(容器没有尝试创建一个新实例,因为您处于相同的生命周期范围内)...
因此,在第二次通话时,您的DbContext
和Repository
正在尝试使用已处置的IUnitOfWork
实例,从而导致您看到的错误。
作为一种解决方案,您不能将DbContext
放在DbContext
上,并且仅在请求结束时处置它......或者您甚至根本不能处置它:这可能听起来很奇怪,但是check this post
我正在复制重要的部分,以防链接失效,由Diego Vega:
DbContext的默认行为是底层连接在任何时候都需要自动打开,并在不再需要时关闭。例如。当您执行查询并使用“foreach”迭代查询结果时,对IEnumerable.GetEnumerator()的调用将导致连接被打开,并且当以后没有更多结果可用时,“foreach”将负责调用Dispose在枚举器上,它将关闭连接。以类似的方式,对DbContext.SaveChanges()的调用将在向数据库发送更改之前打开连接,并在返回之前将其关闭。
鉴于这种默认行为,在许多实际案例中,离开上下文而不处理它并仅仅依赖于垃圾收集是无害的。
也就是说,我们的示例代码总是使用“使用”或以其他方式处理上下文有两个主要原因:
默认的自动打开/关闭行为相对容易覆盖:您可以通过手动打开连接来控制何时打开和关闭连接。一旦你开始在代码的某些部分中执行此操作,那么忘记使用上下文变得有害,因为您可能正在泄漏打开的连接。
- 醇>
DbContext按照推荐的模式实现IDiposable,其中包括公开一个虚拟保护的Dispose方法,派生类型可以覆盖该方法,例如,需要将其他非托管资源聚合到上下文的生命周期中。
所以基本上,除非你正在管理连接,或者有特定的需要处理它,否则不安全。
当然,我仍然建议处理它,但是如果你没有看到它是一个好时机,你可能根本就不这样做。