如何在Autofac中使用通用的UnitOfWork<TContext&gt解决多个DBContext调用。

问题描述 投票:1回答:1

嗨,我已经创建了我的UnitOfWork作为通用,在运行时,它应该创建新的DB上下文实例与DBContextOption生成器的基础上的TContext传递,我已经注册了Mention DB上下文在autofac,但如何解决这个问题在DB上下文构造函数级别。

数据库上下文1实施

public class DBContext1 : DbContext
{
    public DBContext1(DbContextOptions<DBContext1> options1) : base(options1)
    {

    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

DB上下文2实施

public class DBContext2 : DbContext
{
    public DBContext2(DbContextOptions<DBContext2> options2) : base(options2)
    {

    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

IUnitOfWork接口Implemetation。

public interface IUnitOfWork<TContext> where TContext : DbContext, IDisposable
{

}

UnitOfWork class Implemetation

public class UnitOfWork<TContext> : IDisposable, IUnitOfWork<TContext> where TContext : DbContext, new()
{
    private DbContext _context;
    public UnitOfWork()
    {
        _context = new TContext();
    }
}

启动类实施

public class Startup
{
    protected IConfiguration _configuration { get; set; }

    public Startup(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {

        services.AddMvc();

        services.AddEntityFrameworkSqlServer()
            .AddDbContext<DBContext1>(options =>
            options.UseSqlServer(_configuration.GetConnectionString("DBContext1")))
            .AddDbContext<DBContext2>(options =>
            options.UseSqlServer(_configuration.GetConnectionString("DBContext2")));

        /* Autofac DI Configuration with registering DBContext/DataModule/ServiceModule to it */

        var containerBuilder = new ContainerBuilder();

        containerBuilder.RegisterInstance(_configuration).AsImplementedInterfaces().ExternallyOwned();

        var autoFacOptions1 = new DbContextOptionsBuilder<DBContext1>().UseSqlServer(_configuration.GetConnectionString("DBContext1")).Options;
        var autoFacOptions2 = new DbContextOptionsBuilder<DBContext2>().UseSqlServer(_configuration.GetConnectionString("DBContext2")).Options;


        containerBuilder.Register(c => new DBContext1(autoFacOptions1)).As<DbContext>();
        containerBuilder.Register(c => new DBContext2(autoFacOptions2)).As<DbContext>();
        containerBuilder.RegisterModule<DataModule>();
        containerBuilder.RegisterModule<ServiceModule>();

        containerBuilder.Register<String>(c => Guid.NewGuid().ToString())
           .Named<String>("correlationId")
           .InstancePerLifetimeScope();

        containerBuilder.Populate(services);
        var container = containerBuilder.Build();

        return new AutofacServiceProvider(container);

    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Account}/{action=Login}/{id?}");
        });

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync("Hello World!");
        });
    }
}

我可以实现多个DBContext Call,但我必须在DB上下文中创建Default constructor & 连接字符串,就像下面提到的那样。

数据库上下文1实施

public class DBContext1 : DbContext
{
    public DBContext1()
    {

    }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Data Source=Server;Database=DB;User Id=UserID;Password=Password;Integrated Security=False;MultipleActiveResultSets=true;");
    }

    public DBContext1(DbContextOptions<DBContext1> options1) : base(options1)
    {

    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

DB上下文2实施

public class DBContext2 : DbContext
{
public DBContext2()
    {

    }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Data Source=Server;Database=DB;User Id=UserID;Password=Password;Integrated Security=False;MultipleActiveResultSets=true;");
    }

    public DBContext2(DbContextOptions<DBContext2> options2) : base(options2)
    {

    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

请帮助我使用autofac依赖解析器调用DBContext1 & DBContext2的参数化构造函数。

chat asp.net-core-2.0 autofac
1个回答
1
投票

好吧,如果你使用autofac来解决依赖关系,那么你为什么要为它做它的工作呢?) 这就是你代码的主要问题。

首先,你不需要明确地注册IConfiguration。它已经在 IServiceCollection 传给 ConfigureServices() 方法,并将在执行过程中被autofac自动识别。containerBuilder.Populate(services) 呼叫。你可以直接删除这个注册,什么都不会改变。

此外,你在注册你的 DbContext两次--在服务集合和autofac容器构建器中。这没有必要,因为后者将有效地取代前者。此外,它还造成了关于什么在哪里注册,以及整个这一切是如何工作的混乱。最好选择一种注册方法,并坚持使用它。

下一个问题:你要如何对你的工作单元进行单元测试?它对DbContext有很强的依赖性,而DbContext的生命周期你无法在测试中控制。这正是你需要autofac的目的:为你管理组件的依赖性,让你专注于组件的目的而不是次要的东西。

下一个困惑点就在这里。

    containerBuilder.Register(c => new DBContext1(autoFacOptions1)).As<DbContext>();
    containerBuilder.Register(c => new DBContext2(autoFacOptions2)).As<DbContext>();

通过这样做,你实际上是用第二个db上下文注册代替了第一个db上下文注册。从这一点来看,没有办法注入 DBContext1 在您的应用程序的任何地方。 编辑了。 你仍然可以注入DbContext派生实现的集合,并在其中找到DBContext1... 但那会看起来非常奇怪。

总而言之,这可以用更干净和直接的方式来完成。

启动

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        var builder = new ContainerBuilder();

        builder.Register(c => c.CreateDbContextOptionsFor<DBContext1>("DBContext1")).As<DbContextOptions<DBContext1>>().SingleInstance();
        builder.Register(c => c.CreateDbContextOptionsFor<DBContext2>("DBContext2")).As<DbContextOptions<DBContext2>>().SingleInstance();

        builder.RegisterType<DBContext1>().AsSelf().InstancePerLifetimeScope();
        builder.RegisterType<DBContext2>().AsSelf().InstancePerLifetimeScope();

        builder.RegisterType<SomeComponent>().As<ISomeComponent>().InstancePerLifetimeScope();

        builder.RegisterGeneric(typeof(UnitOfWork<>)).As(typeof(IUnitOfWork<>)).InstancePerLifetimeScope();

        builder.Populate(services);

        var container = builder.Build();
        return new AutofacServiceProvider(container);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        ....
    }
}

创建DbContextOptionsFor 助手的实现。它的引入是为了使 Startup 代码更简洁、更易读。通过使用autofac的参数化工厂,而不是使用 new DbContextOptionsBuilder<TContext>()但我不知道在这种情况下是否有意义。

public static class DBExtentions
{
    public static DbContextOptions<TContext> CreateDbContextOptionsFor<TContext>(this IComponentContext ctx,
        string connectionName) where TContext : DbContext
    {
        var connectionString = ctx.Resolve<IConfiguration>().GetConnectionString(connectionName);
        return new DbContextOptionsBuilder<TContext>().UseSqlServer(connectionString).Options;
    }
}

工作单位

public class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext : DbContext
{
    private TContext _context;
    public UnitOfWork(TContext context)
    {
        _context = context;
    }
}

注入和使用工作单位

public class SomeComponent : ISomeComponent
{
    private readonly IUnitOfWork<DBContext1> _uow;

    public SomeComponent(IUnitOfWork<DBContext1> uow)
    {
        _uow = uow;
    }

    public void DoSomething()
    {
        _uow.DoWhatever();
    }

....
© www.soinside.com 2019 - 2024. All rights reserved.