Awesome. Looking forward to it !!!
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 ?
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 ?
Thanks a lot and I am going to try and implement this. Will let you know how it goes.
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 .
We did something similar. Put the tenants you want to share data into one organization and then switch off the data filter for Tenant and switch on the Organization filter.
We figured out the culprit to be Dependency Injection.
We were injecting services at the constructor level which was adding to the slowness.
Is there any reason why Dependency Injection would add such slowness to the application ?
We are planning to Inject dependency wherever it's required rather than injecting at the constructor.
Do you have a better suggestion on how to use the DI ?
Sure will share it in a short time. In the mean time I have been trying to write the extension method and I am kind of lost on the implementation :
public static GetAll<TEntity> GetAllFromCache<TEntity, TPrimaryKey>(IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>, new()
{
return repository.GetAll().FromCache();
}
Obviously the above one doesn't work.
Could you help me on this.
This is how I implemented :
public static void BulkInsert<TEntity, TPrimaryKey>(IRepository<TEntity, TPrimaryKey> repository, IEnumerable<TEntity> entities) where TEntity : class, IEntity<TPrimaryKey>, new() { var enumerable = entities as IList<TEntity> ?? entities.ToList(); if (enumerable.Any()) { if (!Z.EntityFramework.Extensions.LicenseManager.ValidateLicense()) { throw new Exception("Invalid License!"); } var dbContext = repository.GetDbContext(); //dbContext.Set<TEntity>().AddRange(entities); dbContext.BulkInsert(enumerable); } }
The BulkInsert is from Z.Entityframework and it works fine. It also has BulkInserAsync, but the moment I make the method Async and use Await operator I am losing my DbContext. Any reason ?
I will also implement the Caching and update the thread
if (zip.trim().length > 2) {
$.getJSON('http://maps.googleapis.com/maps/api/geocode/json?address=' + zip).success(function (response) {
var city = [],
state = [],
country = [];
//find the city , state and country
if (response.results[0]) {
var address_components = response.results[0].address_components;
Ext.each(address_components, function (component) {
var types = component.types;
Ext.each(types, function (type) {
if (type == 'locality') {
cityField.setValue(component.long_name);
}
if (type == 'administrative_area_level_1') {
state.push({ name: component.long_name, state: component.short_name });
}
if (type == 'country') {
country.push({ name: component.long_name, country: component.short_name });
}
});
});
if (stateCombo && state.length > 0) {
var stateRecord = stateCombo.getStore().findRecord('state', state[0].state);
if (stateRecord) {
stateCombo.select(stateRecord);
}
else {
stateCombo.reset();
}
}
if (countryCombo && country.length > 0) {
var countryRecord = countryCombo.getStore().findRecord('country', country[0].country);
if (countryRecord) {
countryCombo.select(countryRecord);
}
else {
countryCombo.reset();
}
}
}