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方法被在不同的线程中触发。

有人可以帮助我吗?

+0

一种选择是使用“拥有” - http://docs.autofac.org/en/latest/advanced/owned-instances.html,并负责自行清理它。 – mjwills

+0

我确实在想这件事。你能给我一个例子,或者它看起来如何? – r3plica

+0

看起来像使用'拥有'就是答案。我现在只是做一些测试 – r3plica

一个选项是使用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>().Inst‌​ancePerLifetimeScope‌​().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

+0

外部拥有的实例工作。不知道为什么,但我很高兴:D – r3plica

+0

它基本上工作,因为你控制它。当您使用async/await时,生存期范围会比您预期的更早关闭 - 因为它在整个Web请求期间都不会保持打开状态。通过使用“拥有”,你可以说'不用担心,我会打电话给Dispose'。所以,容器不会调用Dispose - 因此你的错误消失了。 – mjwills

+0

我没有使用Owned,只是'.ExternallyOwned'。这很重要吗? – r3plica