Open Closed

overriding IConnectionStringResolver #749


User avatar
0
RonaldR created

Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.

  • ABP Framework version: v4.0
  • UI type: MVC
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Seperated (Angular): yes
  • Exception message and stack trace:
  • Steps to reproduce the issue:
  • I am trying to create a connection string resolver. i think inheriting the default one would solve my problem as i dont want to change the logic, i just need to decrypt an encrypted string. where would i implement this? do you have a good sample for me to look at?

19 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Just an example:

    [Dependency(ReplaceServices = true)]
    public class MyConnectionStringResolver : MultiTenantConnectionStringResolver
    {
        private readonly IStringEncryptionService _encryptionService;
        
        public MyConnectionStringResolver(
            IOptionsSnapshot<AbpDbConnectionOptions> options,
            ICurrentTenant currentTenant,
            IServiceProvider serviceProvider,
            IStringEncryptionService encryptionService) : base(options, currentTenant, serviceProvider)
        {
            _encryptionService = encryptionService;
        }
    
        public override string Resolve(string connectionStringName = null)
        {
    
            var connectionString = base.Resolve(connectionStringName);
    
            return _encryptionService.Decrypt(connectionString);
        }
    }
    
  • User Avatar
    0
    RonaldR created

    This works fairly well, the one issuei am finding is that i sometimes get a null reference on this line, whcih makes no sense to me:

    var connectionString = base.Resolve(connectionStringName);

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    If it is empty, you can return null directly without decrypt

  • User Avatar
    0
    RonaldR created

    i already do that, if the string being returned from the Base.resolver is empty i just return string.empty. should it be null instead? that being said, thats not the line that fails, i am getting the exception on the call to base.resolver trying to get the connection string so i can test its value.

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Can I check it remotely? shiwei.liang@volosoft.com

  • User Avatar
    0
    RonaldR created

    i have no way to create the issue on demand, it just sort of happens some times, here is a screenshot of the code along with the error. not that it should matter, but the connectionStringName is not null, but null should be a valid option for it

    https://gyazo.com/e1c07cce38a2e189cef5e7c4e4445639

  • User Avatar
    0
    RonaldR created

    Here is the whole error stack trace

    " at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.Enumerator.Dispose()\r\n at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable1 source)\r\n at Castle.Proxies.Invocations.ITenantRepository_FindById.InvokeMethodOnTarget()\r\n at Castle.DynamicProxy.AbstractInvocation.Proceed()\r\n at Castle.DynamicProxy.AbstractInvocation.ProceedInfo.Invoke()\r\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedSynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.<ProceedAsync>d__7.MoveNext()\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Volo.Abp.Uow.UnitOfWorkInterceptor.<InterceptAsync>d__3.MoveNext()\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.<InterceptAsync>d__31.MoveNext()\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at Castle.DynamicProxy.NoCoverage.RethrowHelper.Rethrow(Exception exception)\r\n at Castle.DynamicProxy.NoCoverage.RethrowHelper.RethrowInnerIfAggregate(Exception exception)\r\n at Castle.DynamicProxy.AsyncInterceptorBase.InterceptSynchronousResult[TResult](AsyncInterceptorBase me, IInvocation invocation)\r\n at Castle.DynamicProxy.AbstractInvocation.Proceed()\r\n at Castle.Proxies.IBasicRepository1Proxy_12.FindById(Guid id, Boolean includeDetails)\r\n at Volo.Saas.Tenants.TenantStore.Find(Guid id)\r\n at Volo.Abp.MultiTenancy.MultiTenantConnectionStringResolver.Resolve(String connectionStringName)\r\n at DAG.TFORM.Web.Resolvers.TFORMMultiTenantConnectionStringResolver.Resolve(String connectionStringName) in C:\Users\ronald.rizk\source\repos\tform_repo\DAG.TFORM\aspnet-core\src\DAG.TFORM.Web\Resolvers\TFORMMultiTenantConnectionStringResolver.cs:line 27"

    That line 27 is the line of code i identified above: var connectionString = base.Resolve(connectionStringName);

  • User Avatar
    0
    RonaldR created

    More information: i can repeat it fairly regularly when i am running multiple jobs simultaneously from hangfire.

    hope that helps

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Can you provide a simaple project to reproduce the problem?

  • User Avatar
    0
    RonaldR created

    our development cycle ends this coming week and i will have the time to put together the demo for that issue

  • User Avatar
    0
    RonaldR created

    we are having another resolver issue. when we tried to upgrade from 4.1.2 to 4.2.0 we started getting connection string issues. below is a screenshot of the error when we run. out connection string is encrypted. we have a resolver to decrypt it. it look to me as if there is somewhere we need to add a decryption somewhere that is not being covered by the resolver. thoughts?

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    I will check it out

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    In 4.2.0 we started using asynchronous methods, try:

    [Dependency(ReplaceServices = true)]
    public class MyConnectionStringResolver : MultiTenantConnectionStringResolver
    {
        private readonly IStringEncryptionService _encryptionService;
    
        public MyConnectionStringResolver(
            IOptionsSnapshot<AbpDbConnectionOptions> options,
            ICurrentTenant currentTenant,
            IServiceProvider serviceProvider,
            IStringEncryptionService encryptionService) : base(options, currentTenant, serviceProvider)
        {
            _encryptionService = encryptionService;
        }
    
        public override async Task<string> ResolveAsync(string connectionStringName = null)
        {
            var connectionString = await base.ResolveAsync(connectionStringName);
    
            return _encryptionService.Decrypt(connectionString);
        }
    }
    
  • User Avatar
    0
    RonaldR created

    no, that didnt solve it. i updated to 4.2.0, then i changed the resolved:

    namespace DAG.TFORM.Web.Resolvers
    {
        [Dependency(ReplaceServices = true)]
        public class TFORMMultiTenantConnectionStringResolver : MultiTenantConnectionStringResolver
        {
            public TFORMMultiTenantConnectionStringResolver(
                IOptionsSnapshot<AbpDbConnectionOptions> options,
                ICurrentTenant currentTenant,
                IServiceProvider serviceProvider) : base(options, currentTenant, serviceProvider)
            {
            }
    
            public override async Task<string> ResolveAsync(string connectionStringName = null)
            {
                string connectionString = string.Empty;
    
                try
                {
                    connectionString = await base.ResolveAsync(connectionStringName);
                }
                catch
                {
                    return connectionString;
                }
    
                return string.IsNullOrEmpty(connectionString) ? connectionString : EncryptionHelper.Decrypt(connectionString);
            }
        }
    }
    

    But when i try to runit i still get this error:

    An unhandled exception occurred while processing the request. ArgumentException: Format of the initialization string does not conform to specification starting at index 0. System.Data.Common.DbConnectionOptions.GetKeyValuePair(string connectionString, int currentPosition, StringBuilder buffer, bool useOdbcRules, out string keyname, out string keyvalue)

    ArgumentException: Format of the initialization string does not conform to specification starting at index 0. System.Data.Common.DbConnectionOptions.GetKeyValuePair(string connectionString, int currentPosition, StringBuilder buffer, bool useOdbcRules, out string keyname, out string keyvalue) System.Data.Common.DbConnectionOptions.ParseInternal(Dictionary<string, string> parsetable, string connectionString, bool buildChain, Dictionary<string, string> synonyms, bool firstKey) System.Data.Common.DbConnectionOptions..ctor(string connectionString, Dictionary<string, string> synonyms, bool useOdbcRules) System.Data.Common.DbConnectionStringBuilder.set_ConnectionString(string value) Npgsql.NpgsqlConnectionStringBuilder..ctor(string connectionString) Npgsql.NpgsqlConnection.GetPoolAndSettings() Npgsql.NpgsqlConnection.set_ConnectionString(string value) Npgsql.NpgsqlConnection..ctor(string connectionString) Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlRelationalConnection.CreateDbConnection() Microsoft.EntityFrameworkCore.Storage.RelationalConnection.get_DbConnection() Microsoft.EntityFrameworkCore.Storage.RelationalCommand.CreateDbCommand(RelationalCommandParameterObject parameterObject, Guid commandId, DbCommandMethod commandMethod) Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable<T>+AsyncEnumerator.InitializeReaderAsync(DbContext _, bool result, CancellationToken cancellationToken) Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync<TState, TResult>(TState state, Func<DbContext, TState, CancellationToken, Task<TResult>> operation, Func<DbContext, TState, CancellationToken, Task<ExecutionResult<TResult>>> verifySucceeded, CancellationToken cancellationToken) Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable<T>+AsyncEnumerator.MoveNextAsync() Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync<TSource>(IQueryable<TSource> source, CancellationToken cancellationToken) Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync<TSource>(IQueryable<TSource> source, CancellationToken cancellationToken) Volo.Abp.LanguageManagement.EntityFrameworkCore.EfCoreLanguageRepository.GetListAsync(bool isEnabled) Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous<TResult>(IInvocation invocation, IInvocationProceedInfo proceedInfo) Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue<TResult>.ProceedAsync() Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter<TInterceptor>.InterceptAsync<TResult>(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task<TResult>> proceed) Volo.Abp.LanguageManagement.DatabaseLanguageProvider.MBFPd5o9y() Volo.Abp.Caching.DistributedCache<TCacheItem, TCacheKey>.GetOrAddAsync(TCacheKey key, Func<Task<TCacheItem>> factory, Func<DistributedCacheEntryOptions> optionsFactory, Nullable<bool> hideErrors, bool considerUow, CancellationToken token) Volo.Abp.LanguageManagement.DatabaseLanguageProvider.GetLanguagesAsync() Microsoft.AspNetCore.RequestLocalization.DefaultAbpRequestLocalizationOptionsProvider.GetLocalizationOptionsAsync() Microsoft.AspNetCore.RequestLocalization.AbpRequestLocalizationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) Microsoft.AspNetCore.Builder.UseMiddlewareExtensions+<>c__DisplayClass6_1+<<UseMiddlewareInterface>b__1>d.MoveNext() Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    I can't reproduce the problem. Can you use the CLI to create a simple project to reproduce?

  • User Avatar
    0
    RonaldR created

    yes we did. so let me catch you up on what we did. we migrated to 4.2. we implemented the async resolvers. we also discovered that in 4.2 the connection string for postgres changed, so we fixed that issue. so the next provlem we are having is it is throwing errors on some of your conneciton string names that do not seem to go through the resolver.

    from our log file: An exception occurred while iterating over the results of a query for context type 'Volo.Abp.LanguageManagement.EntityFrameworkCore.LanguageManagementDbContext'. System.ArgumentException: Keyword not supported: rn4ocm5xnxtsv7e0prkzlqmuhgvtophtqi7w1+a4eavhxhmt3kb4iimrdn5iienlrggqmluzbqb2r+f5gv6hqwoy1szdhtn1hnisqckb0mskgwanbruw/m9nb5q8twubtxhdl0qqvbnxofmclur89onez9hacgivfz2lsghlr+rzjvgzpdx0yv1ltgwhb3f9q2a/vaxahp02+6xhjrnudzbvihxlp776hn2il7p0vzq (Parameter 'keyword') at Npgsql.NpgsqlConnectionStringBuilder.GetProperty(String keyword) at Npgsql.NpgsqlConnectionStringBuilder.Remove(String keyword) at System.Data.Common.DbConnectionStringBuilder.set_ConnectionString(String value) at Npgsql.NpgsqlConnectionStringBuilder..ctor(String connectionString) at Npgsql.NpgsqlConnection.GetPoolAndSettings() at Npgsql.NpgsqlConnection.set_ConnectionString(String value) at Npgsql.NpgsqlConnection..ctor(String connectionString) at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlRelationalConnection.CreateDbConnection() at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.get_DbConnection() at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.CreateDbCommand(RelationalCommandParameterObject parameterObject, Guid commandId, DbCommandMethod commandMethod) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.Enumerator.InitializeReader(DbContext _, Boolean result) at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func3 verifySucceeded) at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.Enumerator.MoveNext()

    the language management db context is somehow getting the encrypted connection string, not the decrypted version coming through the resolver. we also see this issue in the SaasDbContext as well. we are not implemnting a Saas connection string, it just grabs default, but how to we decrypt it in the pipeline of how you are getting it. i do have a project i can send you with code, the like is below:

  • User Avatar
    0
    RonaldR created

    is there a way to send the link privately? let me knwo if you need to the code or is my description is enough

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    Can I check it remotely? shiwei.liang@volosoft.com

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Override Resolve and resolveAsync methods

Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11