Dependency Injection in Startup.cs difficulties - dependency-injection

I am trying to inject some Data into the Startup.cs file without success. For example, this injection works fine on my Email Class and I am able to read the _data
public class EmailSender : IEmailSender
{
private readonly DataConfig _data;
private readonly UserManager<ApplicationUser> _signManager;
public EmailSender(UserManager<ApplicationUser> signManager, IOptions<DataConfig> data)
{
_data = data.Value;
_signManager = signManager;
}
// I am able to retrieve the _data.SG without issues from the SendEmailAsync method.
public Task SendEmailAsync(string email, string subject, string message)
{
var sendGridKey = _data.SG;
return Execute(sendGridKey, subject, message, email);
}
}
However, when I try to perform the same operation from Startup.cs, the Program.cs crashes:
public class Startup
{
public IConfiguration Configuration { get; }
private readonly DataConfig _data;
public Startup(IConfiguration configuration, IOptions<DataConfig> data)
{
Configuration = configuration;
_data = data.Value;
}
}
Then, Program.cs crashes :
An error occurred while starting the application.
InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Options.IOptions`1[JSRoles.Services.DataConfig]' while attempting to activate 'JSRoles.Startup'.
Microsoft.Extensions.DependencyInjection.ActivatorUtilities+ConstructorMatcher.CreateInstance(IServiceProvider provider)
InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Options.IOptions`1[JSRoles.Services.DataConfig]' while attempting to activate 'JSRoles.Startup'.
Microsoft.Extensions.DependencyInjection.ActivatorUtilities+ConstructorMatcher.CreateInstance(IServiceProvider provider)
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, object[] parameters)
Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
Microsoft.AspNetCore.Hosting.GenericWebHostBuilder+<>c__DisplayClass12_0.<UseStartup>b__0(HostBuilderContext context, IServiceCollection services)
Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
Microsoft.Extensions.Hosting.HostBuilder.Build()
JSRoles.Program.Main(string[] args) in Program.cs
-
namespace JSRoles
{
public class Program
{
public async static Task Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
try
{
JSRoles.Program.<Main>(string[] args)
How do I get around this issue, and still manage to get the data that I need in StartUp.cs?

Related

Castle Windsor with Azure Functions (v3)

I'm going around in circles with this. Is there any way to use the Castle Windsor dependency injection adapter (https://github.com/volosoft/castle-windsor-ms-adapter) with Azure Function apps?
The code example on the adaptor github page is for ASP.NET Core but I would have imagined that there'd be a way to specify the service provider used for a function app, too?
There have been no official documents on this but there is a feature request made by DavidJFowler
Please find the sample code from the above feature request.
The IJobActivatorEx implementation:
public class CastleWindsorJobActivator : IJobActivatorEx
{
private readonly WindsorContainer container;
public CastleWindsorJobActivator(WindsorContainer container) => this.container = container;
public T CreateInstance<T>(IFunctionInstanceEx functionInstance)
{
var disposer = functionInstance.InstanceServices.GetRequiredService<ScopeDisposable>();
disposer.Scope = container.BeginScope();
return container.Resolve<T>();
}
// Ensures a created Castle.Windsor scope is disposed at the end of the request
public sealed class ScopeDisposable : IDisposable
{
public IDisposable Scope { get; set; }
public void Dispose() => this.Scope?.Dispose();
}
public T CreateInstance<T>()
{
var disposer = container.Resolve<ScopeDisposable>();
disposer.Scope = container.BeginScope();
return container.Resolve<T>();
}
}
Startup class:
[assembly: FunctionsStartup(typeof(FunctionApp2.Startup))]
namespace FunctionApp2
{
public class Startup: FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var container = new WindsorContainer();
container.Register(Component.For<IScoped1>().ImplementedBy<Scoped1>().LifestyleScoped())
.Register(Component.For<IScoped2>().ImplementedBy<Scoped2>().LifestyleScoped())
.Register(Component.For<ISingleton1>().ImplementedBy<Singleton1>())
.Register(Component.For<ISingleton2>().ImplementedBy<Singleton2>());
// register function classes in container
var functions = Assembly.GetExecutingAssembly().GetTypes().Where(t =>
t.GetMethods().Any(m => m.GetCustomAttributes(typeof(FunctionNameAttribute), false).Any()));
foreach (var function in functions)
{
container.Register(Component.For(function).LifestyleScoped());
}
builder.Services.AddScoped<CastleWindsorJobActivator.ScopeDisposable>()
.AddSingleton<IJobActivator>(new CastleWindsorJobActivator(container));
container.AddServices(builder.Services);
}
}
}
Example function:
public class Function1
{
public Function1(IScoped1 scoped1, IScoped2 scoped2, ISingleton1 singleton1, ISingleton2 singleton2)
{
}
[FunctionName("Function1")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
}
For now, this feature isn't officially released but you can follow other dependency injection patterns. You can raise a feature request from Microsoft QnA or from Azure Portal.

Passing DbContext through dependency injection to BackgroundService throwing exception

I'm trying to pass EF Core Dbcontext to my BackgroundService through dependency injection but somehow cannot get the hang of it.
Can someone tell me what I'm doing wrong and how to do it right/better/easier/another way (not cumbersome if possible)?
I'm using services.AddDbContext<MyDbContext> to add the DbContext in HostBuilder, then trying to receive it in the constructor of my BackgroundService.
I added the DbContext constructor receiving the DbContextOptions<MyDbContext> options and passing it to the base constructor of DbContext in my context, which is the way described in the documentation
Here's the exception I keep getting when starting the application:
System.AggregateException
HResult=0x80131500
Message=Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: XXXX.SenderService': Cannot consume scoped service 'XXXX.Entities.MyDbContext' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.)
Source=Microsoft.Extensions.DependencyInjection
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder)
at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at XXXX.Program.Main(String[] args) in C:\WORK\REPOS\CRR\Main\XXXX\Program.cs:line 16
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: XXXX.SenderService': Cannot consume scoped service 'XXXX.Entities.MyDbContext' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.
Inner Exception 2:
InvalidOperationException: Cannot consume scoped service 'XXXX.Entities.MyDbContext' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.
Here's the Program.cs:
public static class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).ConfigureLogging(config => config.ClearProviders())
.Build()
.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((builderContext, services) =>
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(builderContext.Configuration
.GetConnectionString(nameof(MyDbContext)))
).AddHostedService<SenderService>())
.UseWindowsService();
}
SenderService.cs:
public class SenderService : BackgroundService
{
public SenderService(IConfiguration configuration, MyDbContext context)
{
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
}
}
and MyDbContext:
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
public DbSet<XXXX> XXXX { get; set; }
}
In the end, I figured it out!
Problem description
What I'm basically doing is using ConfigureServices to AddScoped object, which is what happens when AddDbContext<MyDbContext> is called with only the delegate action filling out the DbContextOptionsBuilder that is then passed to my MyDbcontext constructor.
The dog is buried in adding the context as scoped object, while Service needs it as singleton to accept it in its constructor.
How to remedy that
Well, I can update the piece of code inside the delegate in ConfigureServices to treat the DbContext as a singleton:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host
.CreateDefaultBuilder(args)
.ConfigureServices((builderContext, services) =>
{
//this adds the dbContext, needs action that accepts DbContextOptionsBuilder
services
.AddDbContext<MyDbContext>(
optionsAction: (optionsBuilder) =>
{
//prepare connection string from IConfiguration existing in HostBuilderContext
//that was passed to ConfigureServices
var connectionString = builderContext.Configuration
.GetConnectionString(nameof(MyDbContext));
//and tell the optionsBuilder to use the SqlServer with the connectionstring
//which is then passed as parameter to MyDbContext constructor
optionsBuilder.UseSqlServer(connectionString);
},
//!!!IMPORTANT!!! - also specify the lifetime of the context, default is Scoped
contextLifetime: ServiceLifetime.Singleton,
optionsLifetime: ServiceLifetime.Singleton)
.AddHostedService<SenderService>()
})
.UseWindowsService();
Or, better, DON'T PASS CONTEXT AS SINGLETON TO THE SERVICE, CREATE IT WHEN NEEDED!
Which I can do for example by passing just the optionsBuilder like this:
// ---------in Program.cs------------
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((builderContext, services) =>
services.AddSingleton((_) => {
//again, get connection string from configuration
var connectionString = builderContext.Configuration
.GetConnectionString(nameof(MyDbContext));
//and return DbContextOptions to be saved as singleton
//to be passed to my service as constructor parameter
return new DbContextOptionsBuilder<MyDbContext>().UseSqlServer(connectionString).Options;
})
.AddHostedService<SenderService>())
.UseWindowsService();
// ---------in SenderService.cs------------
public class SenderService : BackgroundService
{
private readonly DbContextOptions<MyDbcontext> contextOptions;
public SenderService(IConfiguration configuration, DbContextOptions<MyDbContext> dbContextOptions)
{
contextOptions = dbContextOptions;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
....
using(var context = new MyDbContext(contextOptions))
{
....
}
....
}
}

Unable to resolve service for type 'Core.Data.CoreContext' while attempting to activate 'Core.Data.Repositories.UnitOfWork'

I have part of WebAPI application that I want to move to separate project as class library.
It is common base structure that almost every app has so idea is to make it easy shareable.
Application type is:
ASP.NET Core Web Application / .NET Framework - ASP.NET Core 2.0 / Web API
What I have done in that direction is created a project named Core and moved those shared elements there, including Base Entities (e.g. User, Settings, etc), CoreContext, UnitOfWork, Generic Repository, BaseRepositories,...
In the Main project of app there are others Entities, AppContext that inherits CoreContext, more Repositories, and all Controllers, ...
I was able to build the app but when starting it get the following error:
InvalidOperationException: Unable to resolve service for type 'Core.Data.CoreContext' while attempting to activate 'Core.Data.Repositories.UnitOfWork'.
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type
serviceType, Type implementationType, ISet callSiteChain,
ParameterInfo[] parameters, bool throwIfCallSiteNotFound)...
Problem seems to be that UnitOfWork class is in Core project and in Main project is Startup.cs with method ConfigureServices that has services.AddTransient<IUnitOfWork, UnitOfWork>();.
Is this a bug or am I just not configuring it correctly and how could it be achieved, if possible at all?
*Further technical details:
NetCore & EF Core version: 2.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
IDE: Visual Studio 2017 15.3
Operating system: Windows 10
UPDATE: Code Sample
namespace Main
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build();
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IUnitOfWork, UnitOfWork>();
services.AddMvc();
var conn = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<AppContext>(options => options.UseSqlServer(conn));
}
}
}
namespace Main.Data
{
public class AppContext : CoreContext
{
...
}
}
-
namespace Core.Data
{
public class UnitOfWork : IUnitOfWork, IDisposable
{
public CoreContext context;
public UnitOfWork(CoreContext context)
{
this.context = context;
}
public T Get<T>() where T : BaseRepository, new()
{
var repository = new T();
repository.Init(context);
return repository;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
public class UnitOfWork<TContext> : IUnitOfWork, IDisposable
where TContext : CoreContext
{
public UnitOfWork(TContext ctx) { ... }
...
}
-
public class AppUnitOfWork : UnitOfWork<AppContext> {
public AppUnitOfWork(AppContext ctx) : base(ctx) { }
}
...
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<IUnitOfWork, AppUnitOfWork>(); // Note the App prefix
services.AddDbContext<AppContext>(options => options.UseSqlServer(conn)); // Note the App prefix
}
...
}
Solution is from GitHub issue:

Calling service/repository methods in ASP.Net Core middleware

ASP.Net Core noob here...I am using an ASP.Net Core WebAPI core project using DNX451 with EF 6.
I have a requirement to implement API Key auth in our service. To do this I have created middleware that gets information from the request and proceeds with authentication. It is SUPPOSED to go to the database, get the key to match, and then return and do the validation.
Here is the middleware implemented to look at the context and get the APIKey
AuthenticationHandler
public class AuthorizationHandler
{
private readonly RequestDelegate _next;
private IAuthenticationService _authenticationService;
public AuthorizationHandler(RequestDelegate next, IAuthenticationService authService)
{
_authenticationService = authService;
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
var apiKey = context.Request.Headers["Key"];
var location = context.Request.Headers["Host"];
var locationKey = _authenticationService.GetApiKey(location);
if (apiKey == locationKey)
await _next(context);
context.Response.StatusCode = 403;
context.Response.Headers.Add("WWW-Authenticate",
new[] { "Basic" });
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
context.Response.Headers.Add("WWW-Authenticate",
new[] { "Basic" });
}
}
}
Here is the startup class with context and middleware registration
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfiguration Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(k => new DbContext(Configuration["Data:Context:ConnectionString"]));
// Add framework services.
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIISPlatformHandler();
app.UseStaticFiles();
app.RegisterAuthorizationHeader();
app.RegisterAuthorization();
app.UseMvc();
}
// Entry point for the application.
public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}
Here is Auth service
public interface IAuthenticationService
{
string GetApiKey(string location);
}
public class AuthenticationService: IAuthenticationService
{
private IApiKeyRepository _apiKeyRepository;
public AuthenticationService(IApiKeyRepository repo)
{
_apiKeyRepository= repo;
}
public string GetApiKey(string location)
{
return _apiKeyRepository.GetApiKeyByLocation(location);
}
}
The repo
public interface IApiRepository
{
string GetApiKeyByLocation(string location);
}
public class ApiRepository: IApiRepository
{
private DbContext _context;
public ApiRepository(DbContext context)
{
_context = context;
}
public string GetApiKeyByLocation(string location)
{
var apiRow = _context.ApiKeyStore.FirstOrDefault(a => a.Location == location);
return apiRow == null ? string.Empty : apiRow.APIKey;
}
}
When attempting this I get the following error:
The context cannot be used while the model is being created. This
exception may be thrown if the context is used inside the
OnModelCreating method or if the same context instance is accessed by
multiple threads concurrently. Note that instance members of DbContext
and related classes are not guaranteed to be thread safe.
Now, when I debug this every break point is hit twice. I believe I understand WHY this issue is occurring but have no idea how to fix it.
Can someone give me an idea, please? Any better solution ideas?
To use scoped dependencies in a middleware (which is necessarily a singleton by definition), the best approach is to flow it as a parameter of InvokeAsync instead of flowing it via the constructor:
public async Task Invoke(HttpContext context, IAuthenticationService authenticationService)
{
try
{
var apiKey = context.Request.Headers["Key"];
var location = context.Request.Headers["Host"];
var locationKey = authenticationService.GetApiKey(location);
if (apiKey == locationKey)
await _next(context);
context.Response.StatusCode = 403;
context.Response.Headers.Add("WWW-Authenticate",
new[] { "Basic" });
}
catch (Exception ex)
{
context.Response.StatusCode = 500;
context.Response.Headers.Add("WWW-Authenticate",
new[] { "Basic" });
}
}

MVC 6 (vNext) UserManager - DI - create user in specific database

I'm developing multitenant application. I use separate databases for each tenant. UserData are for each tenant in separate database TOO.
My problem is how can i create admin account for each tenant in "custom" database independently on DI. In MVC 5 was possible to instantiate UserManager base on UserStore(connection string). But UserManager in mvc6 depends on HttpContext... No documentation found...
Exist please some way how to do it??? I need in mvc 6 something like this in mvc 5:
UserStore<TenantUser> store = new UserStore<TenantUser>(new TenantDbContext("CONNECTION STRING")); //!!! NO POSSIBLE CREATE USER IN CUSTOM DATABASE
UserManager<TenantUser> t = new UserManager<TenantUser>(store);
t.CreateAsync(user, password);
Update:
public class TenantDbContext : IdentityDbContext<TenantUser, TenantRole, Guid>
{
private string _connectionString { get; set; }
private readonly IHttpContextAccessor _contextAccessor;
private readonly ApplicationDbContext _applicationDbContext;
//THIS SUB UNCOMENT ONLY IF CREATE MIGRATIONS (dnx ef...)
/*
public TenantDbContext(DbContextOptions<TenantDbContext> options) : base(options)
{
this._connectionString = "CONNECTION STRING";
}
*/
public TenantDbContext(DbContextOptions<TenantDbContext> options, IHttpContextAccessor contextAccessor, ApplicationDbContext applicationDbContext) : base(options) {
_contextAccessor = contextAccessor;
_applicationDbContext = applicationDbContext;
TenantResolver resolver = new TenantResolver(_contextAccessor, _applicationDbContext);
string con = resolver.GetConnectionString();
if (con != string.Empty)
{
this._connectionString = con; }
else
{
this._connectionString = "CONNECTION STRING"; //Development connection string
}
}
public TenantDbContext() //Posibility to create TenantDbContext migration and development database with no connectionString in constructor
{
//this._connectionString = "CONNECTION STRING";
}
public TenantDbContext(string ConnectionString)
{
this._connectionString = ConnectionString;
}
public static TenantDbContext Create(string ConnectionString)
{
return new TenantDbContext(ConnectionString);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString);
}
I hope that I correctly understand you. Let us we forget about the performance and the caching of multiple connections, which already opened to the databases. I suppose that you have multiple databases, which have the same schema. You need to access the databases using (sharing) the same database context.
I can suggest you two solutions.
The first solution consists from registering one context and reopening it if the one opened co change the connection string.
Let us you have TenantDbContext, which could be opened with different destination databases. For example with
#"Server=(localdb)\mssqllocaldb;Database=TenantDb1;Trusted_Connection=True;"
or
#"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;"
First of all you remove OnConfiguring like
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=TenantDb;Trusted_Connection=True;");
}
which could exist in the definition of TenantDbContext and you use the following code in ConfigureServices of Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
var connection1 = #"Server=(localdb)\mssqllocaldb;Database=TenantDb1;Trusted_Connection=True;";
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<TenantDbContext>(options => options.UseSqlServer(connection1));
services.AddMvc();
...
}
In the way you inject TenantDbContext with one from the database (TenantDb1). Let us the TenantDbContext contains some entity set like Blog for example. Thus your can define some MVC controller in the following way
public class TenantsController : Controller
{
private TenantDbContext _context;
public TenantsController (TenantDbContext context)
{
_context = context;
}
public IActionResult Index() {
var con = _context.Database.GetDbConnection();
// now the con uses either TenantDb2 or TenantDb2
// con.ConnectionString can be used to get or set the
// connection string
string needConStr = #"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;";
if (con.ConnectionString != needConStr) { // can be compared more carefully
_context.Database.CloseConnection();
con.ConnectionString = needConStr;
}
// get some data from the TenantDbContext
var blog = _context.Blog.ToList();
return View(blog);
}
}
The second solution don't need to inject any TenantDbContext using DependencyInjection. Instead of that you need just add one simple constructor to TenantDbContext:
public TenantDbContext(DbContextOptions optionsBuilder): base (optionsBuilder)
{
}
Such simple constructor will allows you to create the context at any time when you need it:
public class TenantsController : Controller
{
public IActionResult Index() {
var contextOptions = new DbContextOptionsBuilder();
contextOptions.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=TenantDb2;Trusted_Connection=True;");
var context = new BloggingContext(contextOptions.Options);
context.Database.OpenConnection();
// get some data from the TenantDbContext
var blog = context.Blog.ToList();
return View(blog);
}
}
I used all the connection strings directly in the code. You can easy modify the above code to get all connection strings from the config file appsettings.json.
Solved.
1. CreateCustomUserStore
public class TenantUserStore : UserStore<TenantUser, TenantRole, TenantDbContext, Guid>
{
public TenantUserStore(TenantDbContext context, IdentityErrorDescriber describer = null): base(context, describer)
{
}
}
And here is code how to instantiate UserManager with custom database:
IUserStore<TenantUser> CustomStore = new TenantUserStore(new TenantDbContext(coonection), null);
UserManager<TenantUser> manager = new UserManager<TenantUser>(CustomStore, _optionsAccessor, _passwordHasher, _userValidators,
_passwordValidators, _keyNormalizer, _errors, _services, _logger, _contextAccessor);
And DI used only for rest of UserManager Constructor:
public class TenantsController : Controller
{
private readonly IHttpContextAccessor _contextAccessor;
private readonly IOptions<IdentityOptions> _optionsAccessor;
private readonly IPasswordHasher<TenantUser> _passwordHasher;
private readonly IEnumerable<IUserValidator<TenantUser>> _userValidators;
private readonly IEnumerable<IPasswordValidator<TenantUser>> _passwordValidators;
private readonly ILookupNormalizer _keyNormalizer;
private readonly IdentityErrorDescriber _errors;
private readonly IServiceProvider _services;
private readonly ILogger<UserManager<TenantUser>> _logger;
public TenantsController(IHttpContextAccessor contextAccessor,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TenantUser> passwordHasher,
IEnumerable<IUserValidator<TenantUser>> userValidators,
IEnumerable<IPasswordValidator<TenantUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<TenantUser>> logger
)
{
_optionsAccessor = optionsAccessor;
_passwordHasher = passwordHasher;
_userValidators = userValidators;
_passwordValidators = passwordValidators;
_keyNormalizer = keyNormalizer;
_errors = errors;
_services = services;
_logger = logger;
_contextAccessor = contextAccessor;
}

Resources