Multiteancy Implementation On Single Database With Same Schema

Most of the people are implementing Multi-Tenancy using multiple databases, hence it's a good practice to distinguish tenant data on different databases. But in some cases, we have to implement multi-tenancy in the same database.
 
So, I am going to write my logic on how I have implemented multi-tenancy on the same database. It will definitely help you. For Multi-Tenancy implementation, I have used the EF Core 2.1 version.
 
Before implementation, please install NuGet package EF core 2.1.
 
PM > Install-Package Microsoft.EntityFrameworkCore -Version 2.1.0  
 
Generally, we will load our configuration like this in the OnModelcreating method.
  1. protected override void OnModelCreating(ModelBuilder modelBuilder)  
  2. {  
  3.    modelBuilder.ApplyConfiguration(new TenantEntityTypeConfiguration());   
  4. }  
But when you have 'n' number of tables and configurations, you have to apply configurations 'n' number of times. Rather than specifying configurations manually, we can load all the configurations by using this logic.
  1. Assembly assembly = Assembly.GetExecutingAssembly();  
Initialize the assembly, then find out all the configuration on your solution.
  1. var configurations = assembly.DefinedTypes.Where(t => t.ImplementedInterfaces.Any(i => i.IsGenericType && i.Name.Equals(typeof(IEntityTypeConfiguration < > ).Name, StringComparison.InvariantCultureIgnoreCase)) && t.IsClass && !t.IsAbstract && !t.IsNested).ToList();  
By using "IEntityTypeConfiguration" type we will list out all the configurations using assemblies, Once list out all the configurations, we have instantiate by using below code.
  1. foreach(var configuration in configurations) {  
  2.     var entityType = configuration.GetInterfaces().Single().GenericTypeArguments.Single();  
  3.     var applyConfigMethods = typeof(ModelBuilder).GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(m => m.Name.Equals("ApplyConfiguration") && m.IsGenericMethod);  
  4.     var applyConfigMethod = applyConfigMethods.Single(m => m.GetParameters().Any(p => p.ParameterType.Name.Equals(typeof(IEntityTypeConfiguration < > ).Name)));  
  5.     var method = applyConfigMethod.MakeGenericMethod(entityType);  
  6.     method.Invoke(modelBuilder, new object[] {  
  7.         Activator.CreateInstance(configuration)  
  8.     });  
This will help to load all your configuration generically.
 
Here are multiple ways to apply the filtering to the logic of your entities.
 
This is the simplest way, to apply your filtering logic to an entity, but you have to apply this logic to each entity. level.
 
Using this filtering logic you don't need above logic, load all your configurations manually and apply filtering logic.
  1. protected override void OnModelCreating(ModelBuilder modelBuilder)  
  2. {  
  3.    modelBuilder.ApplyConfiguration(new TenantEntityTypeConfiguration());  
  4.    modelBuilder.Entity<Tenant>().HasQueryFilter(t => t.Id == 1);  
  5. }  
Other way it's a generic way to apply filerting logic for this you have to load all your configuration either manually or generically.
  1. protected override void OnModelCreating(ModelBuilder modelBuilder) {  
  2.     //load all your configuration like use above generic logic or manually like this  
  3.     modelBuilder.ApplyConfiguration(new TenantEntity1TypeConfiguration());  
  4.     modelBuilder.ApplyConfiguration(new TenantEntity2TypeConfiguration());  
  5.     modelBuilder.ApplyConfiguration(new TenantEntity3TypeConfiguration());  
  6.     modelBuilder.ApplyConfiguration(new TenantEntity4TypeConfiguration());  
  7.     //here you will apply the filtering generically  
  8.     foreach(var type in GetEntityTypes()) {  
  9.         var method = SetGlobalQueryMethod.MakeGenericMethod(type);  
  10.         method.Invoke(thisnew object[] {  
  11.             modelBuilder  
  12.         });  
  13.     }  
  14. }  
  15. private static IList < Type > GetEntityTypes() {  
  16.     if (_entityTypeCache != null) {  
  17.         return _entityTypeCache.ToList();  
  18.     }  
  19.     _entityTypeCache = (from a in GetReferencingAssemblies() from t in a.DefinedTypes where t.BaseType == typeof(Entity) select t.AsType()).ToList();  
  20.     return _entityTypeCache;  
  21. }  
  22. private static IEnumerable < Assembly > GetReferencingAssemblies() {  
  23.     var assemblies = new List < Assembly > ();  
  24.     foreach(var assemblyName in Assembly.GetExecutingAssembly().GetReferencedAssemblies()) {  
  25.         try {  
  26.             Assembly assembly = Assembly.Load(assemblyName.Name);  
  27.             assemblies.Add(assembly);  
  28.         } catch (FileNotFoundException) {}  
  29.     }  
  30.     return assemblies;  
  31. }  
  32. static readonly MethodInfo SetGlobalQueryMethod = typeof(CustomerSuccessContext).GetMethods(BindingFlags.Public | BindingFlags.Instance).Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery");  
  33. public void SetGlobalQuery < T > (ModelBuilder builder) where T: class {  
  34.     builder.Entity < T > ().HasQueryFilter(e => EF.Property < int > (e, "table columnname") == "your tenantid");  
  35.     ///eg:- builder.Entity<T>().HasQueryFilter(e => EF.Property<int>(e, "TenantId") == _tenantId);  
  36. }