首页 > 解决方案 > Autofac - DataContext 未在生命周期结束时处理

问题描述

我有一个命令行程序,它将数据导入我的系统。因为它将数据插入到许多表中,所以我需要更改跟踪。为了防止工作随着时间的推移而变慢,我使用了 Autofac(我的依赖注入框架)来创建一个内部生命周期范围,我从中解决依赖关系。在每批结束时,我重新创建生命周期范围并获取我的依赖项的新实例。问题是,当我这样做时,我的 UnitOfWork 所依赖的 DataContext 不会每次都被刷新,从而导致工作变慢并最终在完成之前终止的情况。

我可以在调试时通过在我的 DbContext 上设置“生成对象 ID”来看到这一点,例如

在此处输入图像描述

在每批之后,对象 ID 保持为 $2,表明 DataContext 实例没有获得新实例。为什么它没有得到一个新的实例?

我的代码看起来像这样:

foreach (var batch in data.Batch(10))
{
    using (var scope = LifetimeScope.BeginLifetimeScope(b =>
    {
        b.RegisterType<UnitOfWork>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
        b.RegisterType<MyService1>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
        b.RegisterType<MyService2>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
        b.RegisterGeneric(typeof(EntityBaseRepository<>)).As(typeof(IEntityBaseRepository<>)).InstancePerLifetimeScope();
    }))
    {
        UnitOfWork = scope.Resolve<IUnitOfWork>();
        MyService1 = scope.Resolve<IMyService1>();
        MyService2 = scope.Resolve<IMyService2>();
        Thing1Repository = scope.Resolve<IEntityBaseRepository<Thing1Repository>>();
        Thing2Repository = scope.Resolve<IEntityBaseRepository<Thing2Repository>>();

        foreach (var row in batch)
        {
            try
            {
                ParseRow(row);
            }
            catch (Exception e)
            {
                JobLogger.Error(e, "Failed to parse row. Exception: " + e.Message);
                throw;
            }

        }
    }
}

我的理解是,当我获得依赖项的新实例时,子依赖项也会获得新实例吗?为什么原来的 DataContext 还挂着?

我的 UnitOfWork 看起来像这样:

public class UnitOfWork : Disposable, IUnitOfWork
{
    private readonly IDbFactory _dbFactory;
    private DataContext _dbContext;

    public UnitOfWork(IDbFactory dbFactory)
    {
        _dbFactory = dbFactory;
    }

    public DataContext DbContext => _dbContext ?? (_dbContext = _dbFactory.Initialise());

    public void Commit()
    {
        DbContext.Commit();
    }
}

我的 DbFactory 负责创建我的 DataContext 的新实例:

public class DbFactory : Disposable, IDbFactory
{
    DataContext _dbContext;

    public DbFactory()
    {
        _dbContext = new DataContext();
    }

    public DataContext Initialise()
    {
        return _dbContext ?? (_dbContext = new DataContext());
    }

    protected override void DisposeCore()
    {
        _dbContext?.Dispose();
    }
}

我的服务是在程序首次启动时通过调用此方法扫描程序集来注册的:

AutofacConfig.InitialiseJobRunner();

在这个方法中,我像这样注册我的类型:

builder.RegisterType<DataContext>().AsSelf().InstancePerMatchingLifetimeScope(lifetimeScope);
builder.RegisterGenericInstance(typeof(EntityBaseRepository<>), typeof(IEntityBaseRepository<>), lifetimeScope);
builder.RegisterAssemblyInterfaces(Assembly.Load(Data), lifetimeScope);

RegisterAssemblyInterfaces 实现为:

public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
        RegisterAssemblyInterfaces(this ContainerBuilder builder, Assembly assembly, object lifetimeScope)
{
    return builder.RegisterAssemblyTypes(assembly)
        .AsImplementedInterfaces()
        .InstancePerMatchingLifetimeScope(lifetimeScope);
}

标签: c#autofacunit-of-work

解决方案


当您注册汇编接口时,如下所示

public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
    RegisterAssemblyInterfaces(this ContainerBuilder builder, Assembly assembly, object lifetimeScope)
{
    return builder.RegisterAssemblyTypes(assembly)
        .AsImplementedInterfaces()
        .InstancePerMatchingLifetimeScope(lifetimeScope);
}

我的猜测是您的 DbFactory 也是以这种方式注册的。在这种情况下(根据:https ://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope ),您将获得与您的工厂相同的实例子范围未命名。尝试添加

b.RegisterType<DbFactory>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();

在您的循环中或更改 DbFactory 以始终在 Initialise 方法中返回新上下文,而不是在已经实例化的情况下返回相同的上下文。


推荐阅读