Reputation: 914
I have an Asp.net core 2 application where I've configued a dbContext with a repository pattern and unit of work.
Everything works fine, I can access the UnitOfWork within a controller and get data.
I'm trying to access the context within a Middleware and also a class in another project, but I don't really know which is the best way.
DbContext
public class BloggingContext : DbContext
{
public BloggingContext(DbContextOptions<BloggingContext> options)
:base(options)
{ }
public DbSet<Blog> Blogs { get; set; }
...
}
Startup
public void ConfigureServices(IServiceCollection services)
{
...
services.AddDbContext<BloggingContext>(options => options.UseMySQL(configuration.GetConnectionString("DefaultDb")));
services.AddScoped<IBloggingContext, BloggingContext>();
services.AddTransient<IUnitOfWork, UnitOfWork>();
...
}
Middleware
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseMyMiddleware();
...
}
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private readonly IMemoryCache _memoryCache;
public MyMiddleware(RequestDelegate next, IConfiguration configuration, IMemoryCache memoryCache)
{
_next = next;
_configuration = configuration;
_memoryCache = memoryCache;
}
public Task Invoke(HttpContext httpContext)
{
...
HERE I NEED TO ACCESS DATABASE
...
return _next(httpContext);
}
}
public static class MyMiddlewareExtensions
{
public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MyMiddleware>();
}
}
What I'm doing inside the Invoke function is:
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseMySQL(_configuration.GetConnectionString("DefaultDb"));
using (var dbContext = new BloggingContext(optionsBuilder.Options))
{
var blog = dbContext.Blogs.FirstOrDefault();
...
}
I'm pretty sure this is not the best way. Also the ideal way would be accesing like I access in a controller, directly to the UnitOfWork without, but I don't know if this is possible.
Also, I need to access to the same DbCOntext within a class in a different project, and in that case I have another problem: how to access the Configuration connection string which is in the appsettings.json of my web project.
Could anyone help me understanding how to do this in the new asp.net core 2?
Also, if someone needs more code I don't mind sharing what I have if it helps others.
Thanks in advance.
Upvotes: 1
Views: 2693
Reputation: 914
Well, this looks like a blog instead of a stackoverflow question...I'm asnwering my own questions, but if it can help just one person...It has been a very productive Sunday.
After reading a lot of articles I figure out the best way to get the main DbContext in the initialization of the tenant DbContext is using the Service Provider to get it as injected dependency.
IServiceProvider serviceProvider
public class DbContextTenant : IdentityDbContext<ApplicationUser, ApplicationRole, int>, IDbContextTenant
{
private readonly string _connectionString;
public DbContextTenant(DbContextOptions<SpzContextClient> options,
IHttpContextAccessor contextAccessor, IServiceProvider serviceProvider) : base(options)
{
var tenantId = (int)contextAccessor.HttpContext.Items["TenantId"];
_connectionString = contextAccessor.HttpContext.Items["DbString"].ToString();
TenantInitialization.Initialize(serviceProvider, this, tenantId);
}
As you can see I pass the serviceProvider to the initialization of the tenant database. This way I can use the main DbContext in the initialization of the other DbCOntext, the one accessing all the tenants.
The initialization would be something like this.
public static void Initialize(IServiceProvider serviceProvider, DbContextTenant context, int tenantId)
{
if (((RelationalDatabaseCreator) context.GetService<IDatabaseCreator>()).Exists()) return;
context.Database.EnsureCreated();
LanguageInitialization.Initialize(context);
CountryInitialization.Initialize(context);
using (var DbContext = serviceProvider.GetRequiredService<DbContext>())
{
CompanyInitialization.Initialize(context, contextMaster, tenantId);
}
context.SaveChanges();
}
If you know a better way of doing this, please let me know.
If the database already exists, it diesn't do anything. This is only for the first time someone access to the tenant site, so we can just add the tenant to the database and the application creates the database itself. This way is really easy to create new clients (tenants), just adding a line to the clients table of the main database.
It creates the database and initialize the tables we need to, like languages, contries...and the company one, getting all the info from the table Client of main DbContext.
This is a multi-tenant application, asp.net core 2, single instalation of the code, one main database and one database for each tenant. Each tenant has a different domain, so we use the host to difference between tenants.
I have had a lot of issues to get here, so I'm sharing this in case someone needs it. I'd have loved to have all the info I have now. If you have any questions or need any code, I'd love to share whatever you need.
Upvotes: 0
Reputation: 914
I found the solition for the MiddleWare, but not for the class in other project.
You only have to inject the IUnitOfWork into the Invoke of the Middleware and it works.
public class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly IConfiguration _configuration;
private readonly IMemoryCache _memoryCache;
public MyMiddleware(RequestDelegate next, IConfiguration configuration,
IMemoryCache memoryCache)
{
_next = next;
_configuration = configuration;
_memoryCache = memoryCache;
}
public Task Invoke(HttpContext httpContext, IUnitOfWork unitOfWork)
{
var myData = _unitoOfWork.Blogs.GetFirst();
...
return _next(httpContext);
}
}
I still have the problem of calling the DbContext within my other DbContext when I'm seeding the database.
I have another DbContext, that I'm creating dinamically. It is a Multi-tenant application where I need to create the tenant database the first time a tenant access to the system, and seed that database, but I have to seed it from the main DbContext.
public class DbContextTenant : IdentityDbContext<ApplicationUser, ApplicationRole, int>, IDbContextTenant
{
private readonly string _connectionString;
public DbContextTenant(DbContextOptions<SpzContextClient> options, IHttpContextAccessor contextAccessor) : base(options)
{
var tenantId = (int)contextAccessor.HttpContext.Items["TenantId"];
_connectionString = contextAccessor.HttpContext.Items["DbString"].ToString();
SEED DATABASE
HERE IS WHERE I NEED TO CALL THE MAIN DBCONTEXT
}
How can I call the main DbCOntext there???
Upvotes: 0