Hi -
I use the Multi Tenant - Multi Database approach in my application. I would like all the reporting request to hit a replicated database for the Tenant.
I was wondering how do I go about it?
Thanks, Partha
8 Answer(s)
-
0
Hi,
Sorry for the late response. You can check this example <a class="postlink" href="https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/MultipleDbContextDemo">https://github.com/aspnetboilerplate/as ... ontextDemo</a>.
-
0
I guess I asked the question in the wrong way.
I am currently using multi-databse and multi -tenant architecture. So all my connection string are stored in Tenant table.
In production each database will be replicated to either to the same server with a suffix as "DatabaseNaem_RPT" or might get replicated to a separate server with the same name.
I am using Grids extensively and also providing excel export option which will export the entire grid just not the visible data.
In such cases I would like to connect to the replicated or reporting database for the tenant and fetch the data.
But both the Grid and excel export will use the same service and only the page size will change based on the request.
So if I want to fetch all data I send the page size= number of records and for this scenario I would like to change the dbcontext connection string to the reporting database and for other request I will use the existing db context.
I need some kind of smart solution where I don't have to make lot of changes .
-
0
Hi,
Sorry, I got it now. DbPerTenantConnectionStringResolver class is used to determine the connection string, see <a class="postlink" href="https://github.com/aspnetboilerplate/module-zero/blob/09cb578f09ee0318b479aa31dd0ceff56a5d218d/src/Abp.Zero.EntityFramework/Zero/EntityFramework/DbPerTenantConnectionStringResolver.cs">https://github.com/aspnetboilerplate/mo ... esolver.cs</a>.
You can create your own implementation of IDbPerTenantConnectionStringResolver <a class="postlink" href="https://github.com/aspnetboilerplate/module-zero/blob/09cb578f09ee0318b479aa31dd0ceff56a5d218d/src/Abp.Zero/MultiTenancy/IDbPerTenantConnectionStringResolver.cs">https://github.com/aspnetboilerplate/mo ... esolver.cs</a>, and use it instead of DbPerTenantConnectionStringResolver.
For example, you can add an http header while you request an excel file from server and decide the database if this header is set in your custom ConnectionStringResolver.
-
0
Thanks a lot and I am going to try and implement this. Will let you know how it goes.
-
0
I wrote the below class :
public class DbPerTenantConnectionStringResolver : IDbPerTenantConnectionStringResolver { /// <summary> /// Reference to the session. /// </summary> public IAbpSession AbpSession { get; set; } private readonly ICurrentUnitOfWorkProvider _currentUnitOfWorkProvider; private readonly ITenantCache _tenantCache; /// <summary> /// Initializes a new instance of the <see cref="DbPerTenantConnectionStringResolver"/> class. /// </summary> public DbPerTenantConnectionStringResolver( IAbpStartupConfiguration configuration, ICurrentUnitOfWorkProvider currentUnitOfWorkProvider, ITenantCache tenantCache) { _currentUnitOfWorkProvider = currentUnitOfWorkProvider; _tenantCache = tenantCache; AbpSession = NullAbpSession.Instance; } public string GetNameOrConnectionString(ConnectionStringResolveArgs args) { if (args.MultiTenancySide == MultiTenancySides.Host) { return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(null, args)); } return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(GetCurrentTenantId(), args)); } public virtual string GetNameOrConnectionString(DbPerTenantConnectionStringResolveArgs args) { if (args.TenantId == null) { //Requested for host return GetNameOrConnectionString(args); } var tenantCacheItem = _tenantCache.Get(args.TenantId.Value); if (tenantCacheItem.ConnectionString.IsNullOrEmpty()) { //Tenant has not dedicated database return GetNameOrConnectionString(args); } return tenantCacheItem.ConnectionString; } protected virtual int? GetCurrentTenantId() { return _currentUnitOfWorkProvider.Current != null ? _currentUnitOfWorkProvider.Current.GetTenantId() : AbpSession.TenantId; } }
I wrote this in my Entityframework project.
Still the brek point is not hitting.
Am I doing something wrong here ?
-
0
Hi @maharatha,
Sorry, I forgot to mention. You need to use ReplaceService in PreInitialize of your module.
Configuration.ReplaceService<IDbPerTenantConnectionStringResolver, DbPerTenantConnectionStringResolver>
Since you have the same name as original DbPerTenantConnectionStringResolver, be sure the second generic argument is your class or you can give DbPerTenantConnectionStringResolver another name.
-
0
This is what I have done so far. Wrote a class on the Entityframeowrk project
public class MyDbPerTenantConnectionStringResolver : IDbPerTenantConnectionStringResolver { /// <summary> /// Reference to the session. /// </summary> public IAbpSession AbpSession { get; set; } private readonly ICurrentUnitOfWorkProvider _currentUnitOfWorkProvider; private readonly ITenantCache _tenantCache; /// <summary> /// Initializes a new instance of the <see cref="SumitDbPerTenantConnectionStringResolver"/> class. /// </summary> public SumitDbPerTenantConnectionStringResolver( IAbpStartupConfiguration configuration, ICurrentUnitOfWorkProvider currentUnitOfWorkProvider, ITenantCache tenantCache) { _currentUnitOfWorkProvider = currentUnitOfWorkProvider; _tenantCache = tenantCache; AbpSession = NullAbpSession.Instance; } public string GetNameOrConnectionString(ConnectionStringResolveArgs args) { if (args.MultiTenancySide == MultiTenancySides.Host) { return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(null, args)); } return GetNameOrConnectionString(new DbPerTenantConnectionStringResolveArgs(GetCurrentTenantId(), args)); } public virtual string GetNameOrConnectionString(DbPerTenantConnectionStringResolveArgs args) { if (args.TenantId == null) { //Requested for host return GetNameOrConnectionString(args); } var tenantCacheItem = _tenantCache.Get(args.TenantId.Value); if (tenantCacheItem.ConnectionString.IsNullOrEmpty()) { //Tenant has not dedicated database return GetNameOrConnectionString(args); } return tenantCacheItem.ConnectionString; } protected virtual int? GetCurrentTenantId() { return _currentUnitOfWorkProvider.Current != null ? _currentUnitOfWorkProvider.Current.GetTenantId() : AbpSession.TenantId; } }
Then added the code the following code on the data module on the PreInitialize
Configuration.ReplaceService<IDbPerTenantConnectionStringResolver, MyDbPerTenantConnectionStringResolver>();
Still it's not overiding. Am I missing anything ?
-
0
Hi,
I didn't know this but there is a special case for IDbPerTenantConnectionStringResolver. You need to replace it like this.
Configuration.ReplaceService(typeof(IConnectionStringResolver), () => { IocManager.IocContainer.Register( Component.For<IConnectionStringResolver, IDbPerTenantConnectionStringResolver>() .ImplementedBy<MyDbPerTenantConnectionStringResolver>() .LifestyleTransient() ); });