Шаблон AspNetBoilerplate: выполнение хранимой процедуры в другом тексте DbContext, транзакция не фиксируется

#asp.net-core #entity-framework-core #aspnetboilerplate #boilerplate

Вопрос:

Я создаю веб-приложение с шаблоном AspNetBoilerplate. Основная DbContext уже настроена и работает правильно, но мне также нужно подключиться ко второй базе данных, чтобы запустить некоторые хранимые процедуры.

Мне удалось создать, внедрить и использовать это DbContext , но когда я выполнить хранимую процедуру, транзакция «зависает», и я не могу получить доступ к таблицам, что хранимая процедура влияет, пока приложение не остановится (когда служба завершает выполнение метода), а также вставок и обновлений из хранимой процедуры не помогут.

Я создал все это на основе этих ссылок: https://aspnetboilerplate.com/Pages/Documents/Articles/Using-Stored-Procedures,-User-Defined-Functions-and-Views/index.html
https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/MultipleDbContextEfCoreDemo

Это код, касающийся второго DbContext :

 public class OperacionesRbDbContext : AbpDbContext
{
    public OperacionesRbDbContext(DbContextOptions<OperacionesRbDbContext> options)
        : base(options)
    {
    }
}
 

Репозиторий, выполняющий хранимую процедуру:

 public class PersonaRepository : OperacionesRbRepositoryBase<Persona, int>, IPersonaRepository
{
    public ILogger Logger { get; set; }
    private readonly IActiveTransactionProvider _transactionProvider;

    public PersonaRepository(IDbContextProvider<OperacionesRbDbContext> dbContextProvider, IActiveTransactionProvider transactionProvider)
        : base(dbContextProvider)
    {
        _transactionProvider = transactionProvider;
        Logger = NullLogger.Instance;
    }

    public async Task<int> AltaEmpleado(Persona persona)
    {
        await EnsureConnectionOpenAsync();

        SqlParameter[] sqlParameters = CrearParametros(persona);

        using (var command = CreateCommand("SP_insert", CommandType.StoredProcedure, sqlParameters))
        {
            int result = 0;

            using (var dataReader = await command.ExecuteReaderAsync())
            {
                while (dataReader.Read())
                {
                    result = (int)dataReader["@RETURN"];
                }
            }

            return resu<
        }
    }

    private DbCommand CreateCommand(string commandText, CommandType commandType, params SqlParameter[] parameters)
    {
        var command = Context.Database.GetDbConnection().CreateCommand();

        command.CommandText = commandText;
        command.CommandType = commandType;
        command.Transaction = GetActiveTransaction();

        foreach (var parameter in parameters)
        {
            command.Parameters.Add(parameter);
        }

        return command;
    }

    private async Task EnsureConnectionOpenAsync()
    {
        var connection = Context.Database.GetDbConnection();

        if (connection.State != ConnectionState.Open)
        {
            await connection.OpenAsync();
        }
    }

    private DbTransaction GetActiveTransaction()
    {
        return (DbTransaction)_transactionProvider.GetActiveTransaction(new ActiveTransactionProviderArgs
            {
                {"ContextType", typeof(OperacionesRbDbContext) },
                {"MultiTenancySide", MultiTenancySide }
            });
    }
}
 

Entity Framework module of my application initializing both contexts:

 [DependsOn(
    typeof(ABTransitCoreModule), 
    typeof(AbpZeroCoreEntityFrameworkCoreModule))]
public class ABTransitEntityFrameworkModule : AbpModule
{
    /* Used it tests to skip dbcontext registration, in order to use in-memory database of EF Core */
    public bool SkipDbContextRegistration { get; set; }

    public bool SkipDbSeed { get; set; }

    public override void PreInitialize()
    {
        Configuration.ReplaceService<IConnectionStringResolver, OperacionesRbStringResolver>();

        Configuration.Modules.AbpEfCore().AddDbContext<ABTransitDbContext>(options =>
        {
            if (options.ExistingConnection != null)
            {
                ABTransitDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
            }
            else
            {
                ABTransitDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
            }
        });

        Configuration.Modules.AbpEfCore().AddDbContext<OperacionesRbDbContext>(options =>
        {

            if (options.ExistingConnection != null)
            {
                OperacionesRbDbContextConfigurer.Configure(options.DbContextOptions, options.ExistingConnection);
            }
            else
            {
                OperacionesRbDbContextConfigurer.Configure(options.DbContextOptions, options.ConnectionString);
            }
        });
    }

    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(typeof(ABTransitEntityFrameworkModule).GetAssembly());
    }

    public override void PostInitialize()
    {
        if (!SkipDbSeed)
        {
            SeedHelper.SeedHostDb(IocManager);
        }
    }
}
 

String resolver:

 public class OperacionesRbStringResolver : DefaultConnectionStringResolver
{
    private readonly IConfigurationRoot _appConfiguration;

    public OperacionesRbStringResolver(IAbpStartupConfiguration configuration, IHostingEnvironment hostingEnvironment)
        : base(configuration)
    {
        _appConfiguration =
            AppConfigurations.Get(hostingEnvironment.ContentRootPath, hostingEnvironment.EnvironmentName);
    }

    public override string GetNameOrConnectionString(ConnectionStringResolveArgs args)
    {
        if (args["DbContextConcreteType"] as Type == typeof(OperacionesRbDbContext))
        {
            var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
            return configuration.GetConnectionString(OperacionesRbConsts.ConnectionStringName);
        }

        return base.GetNameOrConnectionString(args);
    }
}
 

There’s also the ContextFactory and ContextConfigurer for the second DbContext , but I don’t want to flood the post with code.

Кажется, я не могу найти документацию по выполнению хранимых процедур, которые вставляют и обновляют записи, только для операторов SELECT, и я все еще новичок в шаблонах и ядре EF. Кто-нибудь может мне в этом помочь?

Заранее спасибо.