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.
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.<CreateActivator>b__0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>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
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!
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
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();
}
}
}
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 ?
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
Yes We have added DependsOn attribute in FDDemoMicroserviceHostModule. We are still not able to access the dbContext.
hi
Please check PostGreController in FD.Demo.MicroService.Host project. We are trying to access appuserRepository for specific tenant but without any luck.
You can use
CurrentTenant.Change
to switch host and tenant.[ApiController] public class PostGreController : AbpController { private readonly IAppUsers _appuserRepository; private readonly IDbContextProvider<DemoDbContext> _dbContextProvider; public PostGreController(IAppUsers appuserRepository, IDbContextProvider<DemoDbContext> dbContextProvider) { _appuserRepository = appuserRepository; _dbContextProvider = dbContextProvider; } [Route("/api/v1/test")] [HttpGet] [ProducesResponseType((int)HttpStatusCode.Accepted)] public async Task<IActionResult> test() { using (CurrentTenant.Change(null)) { var abcInHostDatabase = await _appuserRepository.GetListAsync(); } var ten1 = new Guid("your tenant id to switch tenant"); using (CurrentTenant.Change(ten1)) { var abcInTenantDatabase = await _appuserRepository.GetListAsync(); } var demoDbContext = await _dbContextProvider.GetDbContextAsync(); return Ok(); } }
Our general issue is, how to access DemoDBContext from FD.Demo.Microservice.Host project.
Reference
FD.Demo.EntityFrameworkCore.csproj
in yourFD.Demo.Microservice.Host
project.
var demoDbContext = await _dbContextProvider.GetDbContextAsync();
Hello Maliming,
As per your reply, I tried giving reference of FD.Demo.EntityFrameworkCore.csproj to my FD.Demo.Microservice.Host project but I am still getting the same error. Can we please schedule a call?
it gives a 500 error.
Can you share the error logs?
Hello maliming,
I have downloaded a sample code from Abp.io and added a microservice project named FD.Demo.MicroService.Host to it. I have created PostGreController in V1 folder of controller folder inside FD.Demo.MicroService.Host project. Now that controller contains a method test(API) which calls IAppUserRepository method (GetListAsync for get the list of users) in FD.Demo.Domain project and returns the list of users. When executing Test API of PostGreController from FD.Demo.MicroService.Host, swagger is showing 500 error. I have attached sample project file which is given below.
Steps:- 1)download the below project 2)Execute the microservice Test api
Project Link:- deleted
Our Purpose is to Change Tenant using CurrentTenant.Change() in MicroService. We are not able to access the Repository in MicroService Layer.We have added the Reference of FD.Demo.Domain for accessing Repository of AppUser. We are not getting any compile time error however we are not able to access the Repository on MicroService API.
Can you please check above example and let me know how can I Access the reposity in MicroService Layer.