我有一个控制器:
private readonly ILogger _logger;
private readonly IRepository _repository;
public HomeController(ILogger logger, IRepository repository)
{
_logger = logger;
_repository = repository;
}
这是库:
public class EfRepository : IRepository
{
// ...methods for add, delete, update entities
// ....
public void Dispose()
{
if (this._context != null)
{
this._context.SaveChanges();
(this._context as IDisposable).Dispose();
this._context = null;
}
}
}
最后,注册类型IOC:
_builder.RegisterType<Logger>().As<ILogger>();
_builder.RegisterType<EfRepository>().As<IRepository>().WithParameter("context", new PcpContext());
当我运行该应用程序我得到这个错误:
因为的DbContext已被释放,操作无法完成。
我试图改变注册EfRepository是这样的:
_builder.RegisterType<EfRepository>()
.As<IRepository>()
.WithParameter("context", new PcpContext()).InstancePerLifetimeScope();
在这种情况下,第一个请求处理的,但试图打开其他网页的时候,我再次得到错误。 问题出在哪儿?
当使用WithParameter方法,参数实例将每一个解析的对象相同。 因此,与.WithParameter("context", new PcpContext())
能有效利用的PcpContext类的同一个实例IRepository的任何解决实例。
根据您当前的代码,当一个IRepository实例配置,它也将是配置实例PcpContext。 然后后续解决的IRepository任何企图将接收被设置在PcpContext实例。 你需要一种方法来获得对是在请求结束处置的每个HTTP请求EF的DbContext的一个全新的实例。
因此代码块,每次执行一个IRepository需要解决的时候一种选择是注册为IRepository一个代码块:
_builder.Register<IRepository>(c => new EfRepository(new PcpContext()))
更好的选择是创建一个新的IDatabaseContext
抽象,更新EfRepository
所以这取决于新IDatabaseContext抽象,而不是PcpContext
类(可能已经是这样的:))。
对于IDatabaseContext实现类将是你PcpContext类,它必须从EF的DbContext继承和可能收到连接字符串作为参数。
public class EfRepository : IRepository
{
private readonly IDatabaseContext _context;
public EfRepository(IDatabaseContext context)
{
_context = context;
}
...methods for add, delete, update entities
//There is no longer need for this to be disposable.
//The disaposable object is the database context, and Autofac will take care of it
//public void Dispose()
}
public interface IDatabaseContext : IDisposable
{
... declare methods for add, delete, update entities
}
public class PcpContext: DbContext, IDatabaseContext
{
public EntityFrameworkContext(string connectionString)
: base(connectionString)
{
}
...methods exposing EF for add, delete, update entities
//No need to implement IDisposable as we inherit from DbContext
//that already implements it and we don´t introduce new resources that should be disposed of
}
这得到使用IoC容器,留下生命周期管理的负担,他们的想法更好。 现在你的库类不需要是一次性的,也不需要管理和IDatabaseContext依赖处置。 这是Autofac谁将会跟踪上下文实例和处理它在适当的时候。
出于同样的原因,你可能想使用InstancePerLifetimeScope与数据库环境的依赖。 这将意味着相同EF上下文用于在相同的HTTP请求每库实例共享和设置在所述请求的结束。
_builder.RegisterType<EfRepository>()
.As<IRepository>();
_builder.RegisterType<PcpContext>()
.As<IDatabaseContext>()
.WithParameter("connectionString", "NameOfConnStringInWebConfig")
.InstancePerLifetimeScope();
我去以“代码块”作为@Daniel JG建议(拉姆达)的简单解决方案。
下面的这Autofac的代码示例。 丹尼尔斯的例子是团结,他也提到了自己。 由于OP添加Autofac作为标记,这似乎与我有关:
_builder.Register(c => new AppDbContext()).As(typeof(AppDbContext));
该代码解决DbContext has been disposed
我与实体框架有问题。 需要注意的是相较于其他大多数DI容器 - 包括统一 - Autofac注册周围的东西和它解决的事情进行切换。
对于由OP给出的代码示例的解决将是这样的:
_builder.Register(c => new EfRepository(new PcpContext())).As(IRepository);
请注意,这最后一点是未经测试的代码。 但是,你应该参考丹尼尔斯回答更多的信息,无论如何,因为我认为他是对与“更好的选择”。 但是,如果你没有时间,现在(像我一样),你可以使用我的解决方案选项。 只需添加一个TODO这样就可以使您所招致的:)技术债务不错。
当我这样做,我会看看我是否可以用工作Autofac跟随他的“更好的选择”代码更新这个答案。 首先,我要仔细阅读这篇文章 。 在快速阅读在我看来,在Autofac的人使用“服务定位器”处理一生范围推广。 但根据马克塞曼是一个反模式 ,所以我有一些东西要弄清楚...任何DI专家的意见?