我有一个专为依赖注入设计的Windows服务。该服务运行多个线程计时器,每个计时器轮询一个数据库,有时会处理其中的记录。
注入服务的一个依赖项是Repository对象,它提供对数据库的必需访问。存储库依次封装实体框架DbContext。
目前,在服务启动时会注入存储库的一个实例,并在服务器停止之前一直保持可用状态。我不喜欢这个有两个原因:
我想更改存储库的管理方式,以便每次调用计时器的execute方法都会获得一个新的存储库实例,同时将存储库保持为可注入服务。
我首选的方法是注入一个RepositoryFactory对象而不是存储库,以便定时器执行方法可以创建自己的存储库。但是,尽管存储库实例已成功创建,并且通过它(GET)对数据库的第一次访问成功,但下一次数据库访问(UPDATE方法中包含的另一个GET)将失败,因为DbContext似乎已被释放。
任何人都可以了解为何会发生这种情况,并提出解决方案吗?如果符合实例化要求,我可以使用除工厂以外的模式。
//DI configuration
public static void AddRepository(this IServiceCollection services, string connectionString)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(connectionString));
services.AddScoped<IRepository, Repository>();
services.AddScoped<IRepositoryFactory, RepositoryFactory>(Factory);
}
private static readonly Func<IServiceProvider, RepositoryFactory> Factory = (_serviceProvider) =>
{
Console.WriteLine("ServiceProvider instantiated: {0}", _serviceProvider != null);
return new RepositoryFactory(_serviceProvider);
};
//Windows service constructor
public AllocationService(IRepositoryFactory factory, IMapperConfiguration mapperConfig)
{
_repositoryFactory = factory;
_mapperConfig = mapperConfig as MapperConfiguration;
InitializeComponent();
}
//Timer execution method
private void _registrationTimer_Elapsed(object stateObject)
{
EventLog.WriteEntry(Constants.AllocationSourceName, string.Format("Registration Timer Elapsed on thread {0}", Thread.CurrentThread.ManagedThreadId), EventLogEntryType.Information, (int)UseCaseType.Register);
try
{
using (var repository = _repositoryFactory.Create())
{
//query the database for Approved Assets,
var list = repository.GetRegistrations(Status.Approved);
EventLog.WriteEntry(Constants.AllocationSourceName, string.Format("{0} registrations with status=Approved", list.Count()), EventLogEntryType.Information, (int)UseCaseType.Register);
foreach (var registration in list)
{
try
{
//some processing removed here for simplicity
//update database
registration.Status = Status.Allocated;
//exception thrown here (see below)
repository.UpdateRegistration(registration);
}
catch (Exception ex)
{
//Log any problems and continue
EventLog.WriteEntry(Constants.AllocationSourceName, ex.ToString(), EventLogEntryType.Error, (int)UseCaseType.Register);
}
}
}
}
catch (Exception ex)
{
//Log any problems on saving
EventLog.WriteEntry(Constants.AllocationSourceName, ex.ToString(), EventLogEntryType.Error, (int)UseCaseType.Register);
}
}
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'MyDbContext'.
at Microsoft.Data.Entity.DbContext.get_ServiceProvider()
at Microsoft.Data.Entity.DbContext.Microsoft.Data.Entity.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
at Microsoft.Data.Entity.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
at Microsoft.Data.Entity.Internal.InternalDbSet`1.<.ctor>b__2_0()
at Microsoft.Data.Entity.Internal.LazyRef`1.get_Value()
at Microsoft.Data.Entity.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.Any[TSource](IQueryable`1 source, Expression`1 predicate)
at RegistryApp.Repository.Repository.ProcessAsset(Asset asset) in C:\svn\Client Applications\ITC\RegistryApp\RegistryApp.Repository\Repository.cs:line 364
at RegistryApp.Repository.Repository.UpdateAsset(Asset asset) in C:\svn\Client Applications\ITC\RegistryApp\RegistryApp.Repository\Repository.cs:line 270