Open Closed

Issue in accessing tenant database from Microservice Project #1358


0
MarekH created
  • ABP Framework version: v4.3
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): yes
  • Exception message and stack trace: Unable to access the action method which is used to switch tenant and get users list of that tenant
  • Steps to reproduce the issue: We have a Microservice project in our solution from which we are trying to connect the database of tenant by providing tenant Id to insert or update any data of particular tenant from the tenant database.
  • We created a controller inside that microservice project and trying to change the tenant using dbcontext but we are not able to call that method. after adding such reference of entityframeworkCore project
  • We have created a question related to that and we got that question credited back by team.
  • We are sharing Demo Project link and screen shot here in which are are trying to access the tenant database.
  • Demo Project Link : https://drive.google.com/file/d/1RZUKdUTNM7Az8t_IRr0i1oF_nVvVb17q/view?usp=sharing

13 Answer(s)
  • 0
    albert created
    Support Team

    try

    private readonly ICurrentTenant _currentTenant;
    
     private async Task DoWorkAsync()
            {
    
                using (_currentTenant.Change(new Guid("xxxxxxxxx")))
                {
                    using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: false))
                    {
                        //.............
                        await uow.CompleteAsync();
                    }
                }
            }
    
  • 0
    MarekH created

    try

    private readonly ICurrentTenant _currentTenant; 
     
     private async Task DoWorkAsync() 
            { 
     
                using (_currentTenant.Change(new Guid("xxxxxxxxx"))) 
                { 
                    using (var uow = _unitOfWorkManager.Begin(requiresNew: true, isTransactional: false)) 
                    { 
                        //............. 
                        await uow.CompleteAsync(); 
                    } 
                } 
            } 
    

    Hello Alper we have tried to use above method in the demo project. we are still not able to get the users list of the tenant which we have switched , we have 2 users for the tenant and we are getting count 0

  • 0
    albert created
    Support Team

    I see that your Microservice project uses SQL your main project uses PostgreSQL. And both of them uses the same dbcontext. it'll not work this way.

  • 0
    MarekH created

    I see that your Microservice project uses SQL your main project uses PostgreSQL. And both of them uses the same dbcontext. it'll not work this way.

    We have used PostGre SQL in Microservice project and still we are not getting the data from tenant database. Can you please eleborate where it uses SQL ?

  • 0
    MarekH created

    We Tried to implement with PostGre Also Tried with creating new API Project and then gave refrence of existing Project as well as added required abp packages into it.

    Also Added Module and depends on inside that new Project it gives us below error when starting debuging:

    System.TypeLoadException: 'Could not load type 'Volo.Abp.Authorization.Permissions.IPermissionDefinitionManager' from assembly 'Volo.Abp.Authorization, Version=4.3.0.0, Culture=neutral, PublicKeyToken=null'.'

    Sharing code of Module class here :

    using FinalDestination.EntityFrameworkCore; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using Volo.Abp; using FinalDestination.MultiTenancy; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.AspNetCore.Mvc.UI.MultiTenancy; using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared; using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.Caching; using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Modularity; using Volo.Abp.Swashbuckle; using Volo.Abp.VirtualFileSystem; using Microsoft.OpenApi.Models; using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore.Cors; using System.IO; using FinalDestination.MicroService.HealthChecks;

    namespace FinalDestination.MicroService { [DependsOn( //typeof(FinalDestinationHttpApiModule), typeof(AbpAutofacModule), typeof(AbpCachingStackExchangeRedisModule), typeof(AbpAspNetCoreMvcUiMultiTenancyModule), typeof(AbpIdentityAspNetCoreModule), typeof(FinalDestinationApplicationModule), typeof(FinalDestinationEntityFrameworkCoreDbMigrationsModule), typeof(AbpSwashbuckleModule), typeof(AbpAspNetCoreSerilogModule) )] public class FinalDestinationMicroServiceModule : AbpModule { private const string DefaultCorsPolicyName = "Default"; public override void ConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); var hostingEnvironment = context.Services.GetHostingEnvironment();

            //ConfigureUrls(configuration);
            ConfigureConventionalControllers();
            ConfigureAuthentication(context, configuration);
            ConfigureSwagger(context, configuration);
            ConfigureCache(configuration);
            ConfigureVirtualFileSystem(context);
            //ConfigureRedis(context, configuration, hostingEnvironment);
            ConfigureCors(context, configuration);
            //ConfigureExternalProviders(context);
            ConfigureHealthChecks(context);
        }
        private void ConfigureHealthChecks(ServiceConfigurationContext context)
        {
            context.Services.AddFinalDestinationHealthChecks();
        }
    
    
    
        private void ConfigureCache(IConfiguration configuration)
        {
            Configure<AbpDistributedCacheOptions>(options =>
            {
                options.KeyPrefix = "FinalDestination:";
            });
        }
    
        private void ConfigureVirtualFileSystem(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
    
            if (hostingEnvironment.IsDevelopment())
            {
                Configure<AbpVirtualFileSystemOptions>(options =>
                {
                    options.FileSets.ReplaceEmbeddedByPhysical<FinalDestinationDomainSharedModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}FinalDestination.Domain.Shared", Path.DirectorySeparatorChar)));
                    options.FileSets.ReplaceEmbeddedByPhysical<FinalDestinationDomainModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}FinalDestination.Domain", Path.DirectorySeparatorChar)));
                    options.FileSets.ReplaceEmbeddedByPhysical<FinalDestinationApplicationContractsModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}FinalDestination.Application.Contracts", Path.DirectorySeparatorChar)));
                    options.FileSets.ReplaceEmbeddedByPhysical<FinalDestinationApplicationModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}FinalDestination.Application", Path.DirectorySeparatorChar)));
                    //options.FileSets.ReplaceEmbeddedByPhysical<FinalDestinationHttpApiModule>(Path.Combine(hostingEnvironment.ContentRootPath, string.Format("..{0}..{0}src{0}FinalDestination.HttpApi", Path.DirectorySeparatorChar)));
                });
            }
        }
    
        private void ConfigureConventionalControllers()
        {
            Configure<AbpAspNetCoreMvcOptions>(options =>
            {
                options.ConventionalControllers.Create(typeof(FinalDestinationApplicationModule).Assembly);
            });
        }
    
        private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.Authority = configuration["AuthServer:Authority"];
                    options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]);
                    options.Audience = "FinalDestination";
                });
        }
    
        private static void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAbpSwaggerGenWithOAuth(
                configuration["AuthServer:Authority"],
                new Dictionary<string, string>
                {
                    {"FinalDestination", "FinalDestination MicroService API"}
                },
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo { Title = "FinalDestination MicroService API", Version = "v1" });
                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                });
        }
    
        //private void ConfigureRedis(
        //    ServiceConfigurationContext context,
        //    IConfiguration configuration,
        //    IWebHostEnvironment hostingEnvironment)
        //{
        //    if (!hostingEnvironment.IsDevelopment())
        //    {
        //        var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
        //        context.Services
        //            .AddDataProtection()
        //            .PersistKeysToStackExchangeRedis(
        //            redis,
        //            "FinalDestination-Protection-Keys");
        //    }
        //}
    
        private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddCors(options =>
            {
                options.AddPolicy(DefaultCorsPolicyName, builder =>
                {
                    builder
                        .WithOrigins(
                            configuration["App:CorsOrigins"]
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .Select(o => o.RemovePostFix("/"))
                                .ToArray()
                        )
                        .WithAbpExposedHeaders()
                        .SetIsOriginAllowedToAllowWildcardSubdomains()
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials();
                });
            });
        }
    
      
    
        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();
    
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            app.UseAbpRequestLocalization();
    
            if (!env.IsDevelopment())
            {
                app.UseErrorPage();
            }
    
            app.UseCors(DefaultCorsPolicyName);
    
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
    
            if (MultiTenancyConsts.IsEnabled)
            {
                app.UseMultiTenancy();
            }
    
            app.UseAuthorization();
            app.UseSwagger();
            app.UseAbpSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "FinalDestination API");
    
                var configuration = context.GetConfiguration();
                options.OAuthClientId(configuration["AuthServer:SwaggerClientId"]);
                options.OAuthClientSecret(configuration["AuthServer:SwaggerClientSecret"]);
            });
            app.UseAuditing();
            app.UseAbpSerilogEnrichers();
            app.UseUnitOfWork();
            app.UseConfiguredEndpoints();
        }
    }
    

    }

  • 0
    albert created
    Support Team

    Hi

    this is an issue we have faced lately, when you use AppUser which is used to add new properties to IdentityUser throws exception in different modules. I suggest you to use IdentityUser instead of AppUser.

  • 0
    MarekH created

    Hi

    this is an issue we have faced lately, when you use AppUser which is used to add new properties to IdentityUser throws exception in different modules. I suggest you to use IdentityUser instead of AppUser.

    Hello

    We have tried it with using IdentityUser insted of AppUser , But still we are not able to access any repoostories. As mentioned above we have a API Project Name : Microservice , We are trying to access database of tenant to perform CRUD Opration from the same project.

    Issue: We are not able to switch or access any reporsitories in that Project

    I have created a DEMO Project with the Microservice Project (In which we would like to access tenant database and perform CRUD oprations).

    I am updating Link with using After tryng it with IdentityUser. https://drive.google.com/file/d/1nntKu7W6resMqoIY0bA8DGd00FwZnYu9/view

    Thank you

  • 0
    albert created
    Support Team

    ok @gterdem will check the solution

  • 0
    gterdem created
    Support Team

    Hello @MarekH,

    The sample you shared is not a microservice template. It is a tiered application. You can check microservice docs about abp microservice template.

    In this concept, your question is not related with microservices.

    We have tried it with using IdentityUser insted of AppUser , But still we are not able to access any repoostories. As mentioned above we have a API Project Name : Microservice , We are trying to access database of tenant to perform CRUD Opration from the same project.

    Issue: We are not able to switch or access any reporsitories in that Project

    I have created a DEMO Project with the Microservice Project (In which we would like to access tenant database and perform CRUD oprations).

    The solution you have provided is a tiered application and has an extra api project named Microservice. So you have 3 different web applications:

    • FD.Demo.HttpApi.Host (supplied by template)
    • FD.Demo.IdentityServer (supplied by template)
    • FD.Microservice (your additional project)

    You are not trying to perform CRUD operations in the same projects, they are all diferent projects. You are just using the same solution. That is the big difference you may be missing out.

    Again, this is a concept of distributed systems: You may have different applications signing in to same IdentityServer and applications communicate each other via using the token they get from identity server.

    It doesn't matter if you put them in same solution or not.

  • 0
    ServiceBot created
    Support Team

    This question has been automatically marked as stale because it has not had recent activity.

  • 0
    MarekH created

    Hello @gterdem

    sorry for not responding to your last message, but this project was mitigated from our side. I re-visited our source code and i have created new project with same parts:

    *.HttpApi.Host (supplied by template) *.IdentityServer (supplied by template) *.Microservice (your additional project) (Web API)

    As you mentioned we are using tiered appliaction. We added few entities into DbContext and they are nicely accessible from all default layers.

    The issue comes when i want to access one of our entities from additional project (*.Microservice...). I cant really switch context based on tenantId (with _currentTenant.Change(randomTenantId)). Can we maybe schedule a quick call to show you the issue?

    Thanks a lot!

  • 0
    gterdem created
    Support Team

    Sorry, I can not make remote session.

    Please explain your issue in details with related code pieces. If it is not related with main question which was created 5 months ago, please create a new one.

  • 0
    MarekH created

    Okay, let me explain our problem.

    We have tiered application (multi-tenant) using PostgreSQL. Into this project, we have added our own API project which will be used as public API for other components. Here is the sln structure: As you can see, our API is called WebComHost. We have created (via abp suite) our custom entity called "Endpoint", we can add/edit/remove everything from UI as expected.

    1. Another application wants to call WebComHost API and update something in Endpoint table.
    2. This request contains TenantId to identify which tenant db should be updated.
    3. As first thing, we would like to switch to proper tenant DbContext using standard approach via _currentTenant.Change(tenantId) (suggested in #1219), here is code in controller method:
    4. When we call Test method, we are getting following error:
    5. An unhandled exception has occurred while executing the request.

    System.InvalidOperationException: Unable to resolve service for type 'Volo.Abp.MultiTenancy.ICurrentTenant' while attempting to activate 'Expireon.WebComHost.Controllers.V1.EndpointController'.          at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)          at lambda_method9(Closure , IServiceProvider , Object[] )          at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.&lt;CreateActivator>b__0(ControllerContext controllerContext)          at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<&gt;c__DisplayClass5_0.&lt;CreateControllerFactory&gt;g__CreateController|0(ControllerContext controllerContext)          at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)          at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()

     Volo.Abp.MultiTenancy package is included in WebComHost project. Is it a correct approach how to access DbContext from different applicaiton?

    Thanks