Autofac和后台进程
问题描述:
我正在使用WebApi 2并且需要在后台启动进程。我需要它来做它的东西,而不会影响当前的请求。Autofac和后台进程
因此,一些周围挖掘后,我发现我可以只使用
public void Save(Order model) => Task.Run(() => postmanService.Update(model));
我想调试它,以确保它是工作,所以我把它改成这样:
public void Save(Order model) => await postmanService.Update(model).ConfigureAwait(false);
但当我运行这个我得到这个错误:
"The ObjectContext instance has been disposed and can no longer be used for operations that require a connection."
我不知道为什么发生这种情况,因为我的DatabaseContext注册这样的:
builder.RegisterType<DatabaseContext>().As<DbContext>().InstancePerLifetimeScope();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<CollectionManagerProvider>().As<ICollectionManagerProvider>().InstancePerRequest();
builder.RegisterType<PostmanService>().As<IPostmanService>();
的PostmanService就是将数据保存到数据库中,它看起来像这样:
public class PostmanService : Service<Postman>, IPostmanService
{
public PostmanService(IUnitOfWork unitOfWork) : base(unitOfWork)
{}
/// <summary>
/// Save or update a message
/// </summary>
/// <param name="model"></param>
public void Save(Postman model)
{
if (model.Id == 0)
Repository.Create(model);
else
Repository.Update(model);
}
/////////--------- Removed for brevity ---------/////////
}
信息库看起来是这样的:
public class Repository<T> : IRepository<T> where T : class
{
// Create our private properties
private readonly DbContext _context;
private readonly DbSet<T> _dbEntitySet;
/// <summary>
/// Default constructor
/// </summary>
/// <param name="context">The database context</param>
public Repository(DbContext context)
{
// Assign our context and entity set
_context = context ?? throw new ArgumentNullException("context");
_dbEntitySet = context.Set<T>();
}
/// <summary>
/// Creates an entity
/// </summary>
/// <param name="model"></param>
public void Create(T model) => _dbEntitySet.Add(model);
/// <summary>
/// Updates an entity
/// </summary>
/// <param name="model"></param>
public void Update(T model) => _context.Entry<T>(model).State = EntityState.Modified;
/////////--------- Removed for brevity ---------/////////
}
最后,工作单元看起来像这样:
public class UnitOfWork : IUnitOfWork
{
private readonly Dictionary<Type, object> _repositories;
// Public properties
public DbContext Context { get; }
/// <summary>
/// Default constructor
/// </summary>
public UnitOfWork(DbContext context)
{
Context = context;
_repositories = new Dictionary<Type, object>();
}
/// <summary>
/// Gets the entity repository
/// </summary>
/// <typeparam name="TEntity">The entity model</typeparam>
/// <returns></returns>
public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
{
// If our repositories have a matching repository, return it
if (_repositories.Keys.Contains(typeof(TEntity)))
return _repositories[typeof(TEntity)] as IRepository<TEntity>;
// Create a new repository for our entity
var repository = new Repository<TEntity>(Context);
// Add to our list of repositories
_repositories.Add(typeof(TEntity), repository);
// Return our repository
return repository;
}
/// <summary>
/// Saves the database changes asynchronously
/// </summary>
/// <returns></returns>
public async Task SaveChangesAsync()
{
try
{
// Save the changes to the database
await Context.SaveChangesAsync();
}
catch (DbEntityValidationException ex) {
// Retrieve the error messages as a list of strings.
var errorMessages = ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage);
// Join the list to a single string.
var fullErrorMessage = string.Join("; ", errorMessages);
// Combine the original exception message with the new one.
var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
// Throw a new DbEntityValidationException with the improved exception message.
throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
}
}
}
我不知道,如果问题出在CollectionManagerProvider以及如何注册(因为我已经把它注册为InstancePerRequest和techinically方法被在不同的线程中触发。
有人可以帮助我吗?
答
一个选项是使用Owned
- http://docs.autofac.org/en/latest/advanced/owned-instances.html,并承担处置责任。
public Repository(Owned<DbContext> context)
public PostmanService(Owned<IUnitOfWork> unitOfWork)
等等等等
或:
builder.RegisterType<DatabaseContext>().As<DbContext>().InstancePerLifetimeScope().ExternallyOwned();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope().ExternallyOwned();
等等等等
参见:
http://docs.autofac.org/en/latest/advanced/owned-instances.html http://docs.autofac.org/en/latest/lifetime/disposal.html
一种选择是使用“拥有” - http://docs.autofac.org/en/latest/advanced/owned-instances.html,并负责自行清理它。 – mjwills
我确实在想这件事。你能给我一个例子,或者它看起来如何? – r3plica
看起来像使用'拥有'就是答案。我现在只是做一些测试 –
r3plica