Search code examples
entity-frameworkasp.net-coretransactionsdbcontextaspnetboilerplate

ABP EF Core multiple DbContext access in IDomainService throws transaction error


The specified transaction is not associated with the current connection. Only transactions associated with the current connection may be used.

How do I use multiple DbContext in one transaction?

Update 1

If I use ExistingConnection, then all the DbContext will use the same connection string.

Did I add multiple DbContext in the wrong way?

In EntityFrameworkModule:

public override void PreInitialize()
{
    var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());

    Configuration.Modules.AbpEfCore().AddDbContext<BPDbContext>(options =>
    {
        if (options.ExistingConnection != null)
        {
            options.DbContextOptions.UseSqlServer(options.ExistingConnection);
        }
        else
        {
            options.DbContextOptions.UseSqlServer(configuration.GetConnectionString(ABPCoreConsts.BPConnectionStringName));
        }

        //options.DbContextOptions.UseSqlServer(
        //    configuration.GetConnectionString(ABPCoreConsts.BPConnectionStringName));
    });

    Configuration.Modules.AbpEfCore().AddDbContext<EPlusDBConext>(options =>
    {
        if (options.ExistingConnection != null)
        {
            options.DbContextOptions.UseSqlServer(options.ExistingConnection);
        }
        else
        {
            options.DbContextOptions.UseSqlServer(configuration.GetConnectionString(ABPCoreConsts.EECPlusConnectionStringName));
        }

        //options.DbContextOptions.UseSqlServer(
        //    configuration.GetConnectionString(ABPCoreConsts.EECPlusConnectionStringName));
    });

    Configuration.Modules.AbpEfCore().AddDbContext<ProjectManageDbContext>(options =>
    {
        if (options.ExistingConnection != null)
        {
            options.DbContextOptions.UseSqlServer(options.ExistingConnection);
        }
        else
        {
            options.DbContextOptions.UseSqlServer(configuration.GetConnectionString(ABPCoreConsts.PMConnectionStringName));
        }

        //options.DbContextOptions.UseSqlServer(
        //    configuration.GetConnectionString(ABPCoreConsts.PMConnectionStringName));
    });

    RegisterGenericRepositories();
}

Update 2

I got it to work by implementing IConnectionStringResolver for custom connections:

public class MyDBConnectionStringResolver : DefaultConnectionStringResolver
{
    public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
    {
        var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());

        switch (args["DbContextType"].ToString())
        {
            case "ABPCore.EPlusDBConext":
                return configuration.GetConnectionString(ABPCoreConsts.EECPlusConnectionStringName);
            case "ABPCore.BPDbContext":
                return configuration.GetConnectionString(ABPCoreConsts.BPConnectionStringName);
            case "ABPCore.ProjectManageDbContext":
                return configuration.GetConnectionString(ABPCoreConsts.PMConnectionStringName);
        }

        return string.Empty;
    }
}

Don't forget to replace service in EntityFrameworkModule's PreInitialize method:

Configuration.ReplaceService<IConnectionStringResolver, MyDbConnectionStringResolver>(DependencyLifeStyle.Transient);

Solution

  • Add this in *DbContextConfigurer.cs:

    public static void Configure(DbContextOptionsBuilder<*DbContext> builder, DbConnection connection)
    {
        builder.UseSqlServer(connection);
    }
    

    Change this in *EntityFrameworkModule.cs:

    public override void PreInitialize()
    {
        if (!SkipDbContextRegistration)
        {
            Configuration.Modules.AbpEfCore().AddDbContext<*DbContext>(options =>
            {
                if (options.ExistingConnection != null)
                {
                    options.DbContextOptions.UseSqlServer(options.ExistingConnection);
                }
                else
                {
                    options.DbContextOptions.UseSqlServer(options.ConnectionString);
                }
            });
        }
    }
    

    Reference: https://github.com/aspnetboilerplate/module-zero-core-template/commit/da522e76ca2ecefdb7670f009f78575c5b97b4a0


    Important

    If each DbContext has its own connection string, you need to implement IConnectionStringResolver as explained here and replace the service:

    Configuration.ReplaceService<IConnectionStringResolver, MyConnectionStringResolver>(DependencyLifeStyle.Transient);