单例中工作单元的自动工厂
本文关键字:工厂 工作 单元 单例中 | 更新日期: 2023-09-27 18:36:50
标题所暗示的那样简单明了,这是使用Autofac依赖注入的可能吗?我一直在尝试和寻找到处..我在这里失去了希望。
我的班级必须是单例的——这不能改变。我希望它采用工作单元的工厂 - 用于数据库事务。
请帮我弄清楚这一点,我是分开的。
尝试了 Func<>并以各种可能的方式(各种生命周期,外部拥有与否)注册我的工作单元,但失败了,因为工作单元中的 DbContext 被释放并且不会在第一次请求后再次创建
编辑:
添加了代码,希望有助于理解我的问题:
public class SingletonDataService : IDataService
{
private _uowFactory;
public SingletonDataService(Func<IEFUnitOfWork> uowFactory)
{
_uowFactory = uowFactory
}
public List<Folder> GetAllFolders ()
{
using (uow = uowFactory())
{
return uow.FoldersRepository.GetAll();
}
}
}
public MyDbContext : DbContext
{
public DbSet<Folder> Folders {get; set;}
public DbSet<Letter> Letters {get; set;}
public MyDbContext() : base("myContext...")
}
public EFUnitOfWork : IEFUnitOfWork, IDisposable
{
public IRepository<Folder> FoldersRepository;
public IRepository<Letter> LettersRepository;
private DbContext _context;
public EFUnitOfWork(IRepository<Folder> folders, IRepository<Letter> letters, DbContext context)
{
_folders = folders;
_letters = letters;
_context = context;
}
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 Repository<T> : IRepository<T> where T: BaseEntity
{
private DbContext _context;
private DbSet<T> _set
public Repository(DbContext context)
{
_context = context;
_set = _context.Set<T>();
}
}
public LettersController : ApiController
{
private IDataService _dataService;
public LettersController(IDataService dataService)
{
_dataService = dataService;
}
[HttpGet]
public IHttpActionResult GetAllLetters()
{
return Ok(_dataService.GetAllLetters());
}
}
// Must be singleton
builder.Register<SingletonDataService>().As(IDataService).SingleInstance();
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
builder.Register<EFUnitOfWork>().As(IEFUnitOfWork).InstancePerLifetimeScope();
builder.Register<DbContext>().As(AppDbContext).InstancePerLifetimeScope();
在第一个请求中一切正常,在第二个第三个请求中,依此类推,我得到这个异常:
system.ObjectDisposedException: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
我清楚地看到它发生了,因为我的存储库中的上下文现在为空,但我不知道如何使用 DI 更改它。
如果没有 DI,我想要实现的目标是如此简单:
如何使用Autofac实现以下目标?
public class UowFactory
{
public UowFactory()
{
}
public IEFUnitOfWork Create()
{
var context = new AppDbContext()
var uow = new EFUnitOfWork(new Repository<Folder>(context), new Repository<Letter>(context), context);
return uow;
}
}
问题
您正在向InstancePerLifetimeScope()
注册关键组件
构建 autofac 容器时,它还会创建一个根ILifetimeScope
,该根一直存在到调用IContainer.Dispose()
。现在,除非您在链中的某个地方创建嵌套ILifetimeScope
到SingletonDataService
,否则组件使用的ILifetimeScope
是根ILifetimeScope
,InstancePerLifetimeScope()
实际上等同于SingleInstance()
。
溶液
可能的解决方案之一是为每个IEFUnitOfWork
及其子项创建一个ILifetimeScope
。Autofac 通过提供Owned<T>
类型来促进这一点。我们可以将其与Func<>
工厂结合使用(另请参阅文档):
public class SingletonDataService : IDataService
{
private Func<Owned<IEFUnitOfWork>> _uowFactory;
public SingletonDataService(Func<Owned<IEFUnitOfWork>> uowFactory)
{
_uowFactory = uowFactory
}
public List<Folder> GetAllFolders ()
{
using (var uow = _uowFactory())
{
return uow.Value.FoldersRepository.GetAll();
}
}
}
这应该可以很好地与以下注册配合使用:
// Must be singleton
builder.Register<SingletonDataService>().As(IDataService).SingleInstance();
builder.RegisterGeneric(typeof(Repository<>))
.As(typeof(IRepository<>))
.InstancePerLifetimeScope();
builder.Register<EFUnitOfWork>().As(IEFUnitOfWork).InstancePerLifetimeScope();
builder.Register<DbContext>().As(AppDbContext).InstancePerLifetimeScope();
但是,请注意,DbContext
受InstancePerLifetimeScope
约束基本上使手动处置EFUnitOfWork
多余。
关于正确处置的旁注
由于IDisposable
类型应该支持优雅的多处置,因此应该能够简化EFUnitOfWork.Dispose()
public void Dispose()
{
_context.Dispose();
}
另请注意,我省略了呼叫GC.SuppressFinalize(this);
.此调用仅在类实现自定义终结器(~EFUnitOfWork
方法)或派生类可以这样做的情况下相关,否则该对象无论如何都不会放在终结器队列中。
您的问题是您有一个单一实例(SingletonDataService)依赖于生存期较短的服务(EFUnitOfWork)。当 autofac 创建 SingletonDataService 实例时,它会获取 EFUnitOfWork 的实例,但此实例将始终保持不变(其实际生存期将比您预期的要长),因此会被释放并再次使用,从而产生错误。
您有两种可能的解决方案:
一种是创建一个类似于您在底部定义的UowFactory类(但依赖于IRepository<Folder>, IRepository<Letter>, DbContext
),将其注册为任何内容(例如单例,但这无关紧要),并使SingletonDataService依赖于它。不过,这对您来说可能不是一个可行的解决方案,因为它还会延长IRepository<Folder>, IRepository<Letter>, DbContext
实例的生命周期并在那里产生问题。
正确的解决方案是删除您希望 SingletonDataService 成为单一实例(可能是某个缓存)的原因。并将其移动到 SingletonDataService 所依赖的新服务(CacheService?),并使该新服务成为单一实例。