Using Microsoft.AspNetCore.Identity.MongoDB for Multi Tenancy. How do we inject dynamic Tenant into MongoDbContext - mongodb

Does anyone know how we can inject context into User Manager > MongoDB serStore at runtime in .net core 2.0.
We cannot do this at startup due to the context being dynamic but the UserStore is not accessible and UserManager has too many variables to new up, and it is wrong. Are there any solutions?
public class UserStore<TUser> :
IUserPasswordStore<TUser>,
IUserRoleStore<TUser>,
IUserLoginStore<TUser>,
IUserSecurityStampStore<TUser>,
IUserEmailStore<TUser>,
IUserClaimStore<TUser>,
IUserPhoneNumberStore<TUser>,
IUserTwoFactorStore<TUser>,
IUserLockoutStore<TUser>,
IQueryableUserStore<TUser>,
IUserAuthenticationTokenStore<TUser>
where TUser : IdentityUser
{
private readonly IMongoCollection<TUser> _Users;
//THIS IS WHERE WE WANT TO INJECT THE users AT RUNTIME
public UserStore(IMongoCollection<TUser> users)
{
_Users = users;
}
public virtual void Dispose()
{
// no need to dispose of anything, mongodb handles connection pooling automatically
}
public virtual async Task<IdentityResult> CreateAsync(TUser user, CancellationToken token)
{
await _Users.InsertOneAsync(user, cancellationToken: token);
return IdentityResult.Success;
}
unfortunately users is null at startup, and should be as the tenant has not been created at that point.
We have also been using the saaskit.Multitenancy and just can't find a solution.
Any help would be much appreciated.
Thanks

i think u need a generic repository to act as a wrapper for IMongoCollection then inject the repository inside controllers
public class Repository<T>
{
public IMongoCollection<T> Collection { get; private set; }
public Repository(IDbFactory dbFactory)
{
MongoClient client = new MongoClient("ur connection string");
this.Collection = client.GetDatabase("db").GetCollection<T>(typeof(T).Name);
}
public T Find(Expression<Func<T, bool>> filter)
{
return this.Collection.AsQueryable<T>().FirstOrDefault<T>(filter);
}
public async Task<T> FindAsync(Expression<Func<T, bool>> filter)
{
return await this.Collection.AsQueryable<T>().FirstOrDefaultAsync<T>(filter);
}
// here add more methods
}
then register the dependency as below inside Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
services.AddMvc();
}
finally inside controllers u inject the generic repository, also dont forget to Implement the IDisopsible in genereic repository
public class ProductController : Controller
{
private readonly IRepository<Product> _productRepository = null;
public ProductController(IRepository<Product> productRepository)
{
this._productRepository = productRepository;
}
}

Related

How do I resolve a WebAPI dependency in Autofac that requires a parameter from the route?

I am struggling with wiring dependencies through autofac in my WebApi 2 project. I have a following interface and class that i'd like to inject in my GET and POST controller actions,
public interface IRepository
{
IContext Context
{
get;
}
void SomeOperation();
}
public MyRepository : IRepository
{
IContext _context;
public MyRepository(IContext context)
{
_context = context;
}
public Context
{
get
{
return _context;
}
}
public void SomeOperation
{
// Perform some operation using _context;
}
}
I 'd like IRepository to be injected in controller like this,
public class MyController : ApiController
{
private readonly IRepository _repo;
public ApplicationsController(IRepository repo)
{
_repo = repo;
}
// GET: api/v1/Contexts({contextId})
public IHttpActionResult Get(string contextId)
{
_repo.SomeOperation();
}
}
IContext object to be injected in MyRepository has to be fetched from a factory, something like this
public class ContextFactory
{
Hashtable contextMap;
IContext Get(string contextId)
{
if contextMap.Contains(contextId)
return contextMap[contextId].Value;
else
{
IContextConfiguration configuration = ContextConfigurationFactory.Get(contextId);
IContext context = new ConcreteContext(configuration);
contextMap.Add[contextId, context];
return context;
}
}
}
I am not sure how to wire all the classes and convert logic in factory classes by injecting relationships through Autofac so that context id passed in url is passed to ContextConfigurationFactory.Get and instantiate ConcreteContext object when not found in hash and eventually Autofac injecting right context object in MyRepository before passing it on to Get action in the controller.
Let's simplify this a bit. What you're trying to do is:
Get the context ID from a route parameter.
Use that route parameter in the factory to create a context.
The rest seems pretty much peripheral - the repository, the controller, all that. The crux of the question is that you need to get a route parameter into your factory.
Given that, let's put together some simplified code:
public class ContextFactory
{
public IContext Get(string contextId)
{
return new Context(contextId);
}
}
public interface IContext
{
string Id { get; }
}
public class Context : IContext
{
public Context(string id)
{
this.Id = id;
}
public string Id { get; private set; }
}
That's basically what you have:
An IContext interface that things need.
A ContextFactory that is basically responsible for building these things.
A Context concrete implementation of IContext that is built by the factory.
I would probably do something like this:
var builder = new ContainerBuilder();
builder.RegisterType<ContextFactory>();
builder.Register(ctx =>
{
var routeData = HttpContext.Current.Request.RequestContext.RouteData;
var id = routeData.Values["contextId"] as string;
var factory = ctx.Resolve<ContextFactory>();
return factory.Get(id);
}).As<IContext>()
.InstancePerLifetimeScope();
Now when you resolve IContext it will use your factory, get the current context ID from route data, and pass it through the factory.
I will leave the following for you to look into:
What happens if the route parameter isn't there? (Autofac won't let you return null.)
What happens if the route parameter has invalid data?
The route parameter is pretty hackable, is this a security risk?
...and so on.

How to pass options from Startup.cs to DbContextOptions constructor function at ASP.NET Core 2.0

I am using ASP.NET Core 2.0
At Startup.cs I have
services.AddDbContext<MailDBServicesContext>(optionsSqLite =>
{
optionsSqLite.UseSqlite("Data Source=Mail.db");
});
I have created a model and a DbContext where DbContext is:
public class MailDBServicesContext : DbContext
{
public MailDBServicesContext(DbContextOptions<MailDBServicesContext> options)
: base(options)
{
}
public DbSet<MailCountSentErrorMails> DbSetMailCountSentErrorMails { get; set; }
}
from a Class helper I need to pass DbContextOptions and my question is how can I tell to use the options from the Startup.cs ConfigureServices method
using (var db = new MailDBServicesContext())
{
}
It should be enough to simply inject MailDBServicesContext into your controller or a service class, for example.
public class SomeDataService
{
private readonly MailDBServicesContext _dbContext;
public SomeDataService(MailDBServicesContext dbContext)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public async Task AddMailCounts()
{
_dbContext.DbSetMailCountSentErrorMails
.Add(new MailCountSentErrorMails { CountSentMails = 55 });
await _dbContext.SaveChangesAsync();
}
}
Other DB context configuration options are defined in Configuring a DbContext on MSDN.
Update
Make sure to register your service in DI, i.e. ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISomeDataService, SomeDataService>();
services.AddDbContext<MailDBServicesContext>(optionsSqLite =>
{
optionsSqLite.UseSqlite("Data Source=Mail.db");
});
services.AddMvc();
}
Then make a call to AddMailCounts() in your controller.
public class HomeController : Controller
{
private readonly ISomeDataService _dataService;
public HomeController(ISomeDataService dataService)
{
_dataService = dataService ?? throw new ArgumentNullException(nameof(dataService));
}
public IActionResult Index()
{
_dataService.AddMailCounts();
return View();
}
}
Now every time you load homepage, a record is inserted into DbSetMailCountSentErrorMails table.
You can find working solution on my GitHub.

MsTest - Mocking a DbContext with DbContextOption and configuration

I have 2 project, Data and Data.test, I use ef core and .net core for both of them, for Data project I have ExpenseDb like this:
public class ExpenseDb: DbContext
{
private IConfigurationRoot _config;
public ExpenseDb(DbContextOptions<ExpenseDb> options, IConfigurationRoot config) : base(options)
{
_config = config;
}
public DbSet<Account> Accounts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlServer(_config["Data:ConnectionString"]);
}
}
And I have a repository for Account like this:
private ExpenseDb _db;
public AccountRepository(ExpenseDb db)
{
_db = db;
}
public IEnumerable<Account> All(Guid userId)
{
return (_db.Accounts.AsNoTracking().Where(a => a.UserId == userId).ToList());
}
I use ms IOC for injectiong dependencies like this :
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json");
_config = builder.Build();
}
IConfigurationRoot _config;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_config);
services.AddDbContext<ExpenseDb>(ServiceLifetime.Scoped);
}
These all are in my Data project, and for Data.Test I would like to test All method, I realized I must Mock my ExpenseDb so I got Moq from Nuget Package and now I have test class like this :
[TestClass]
public class AccountRepositoryTest
{
private readonly Mock<ExpenseDb> _dbMock = new Mock<ExpenseDb>();
private readonly AccountRepository _repo;
public AccountRepositoryTest()
{
_repo = new AccountRepository(_dbMock.Object);
}
[TestMethod]
public void AllForInvalidUser()
{
var fakeaccount = new Account() { Name="cat2",OpenDate=DateTime.Now,StartBalance=100};
Mock < DbSet < Account >> acMock = DbSetMock.Create(fakeaccount);
var results = _repo.All(Guid.Parse("cf15c6c9-f688-47ee-892e-297e530be053"));
Assert.IsNotNull(results);
}
}
Obviously my test is failed, because I must pass config and options to my ExpenseDb somehow, but I don't know How?!
I searched and I found out all answer are saying "You must have an inteface for your service" but i don't want to create an unnecessary interface.
Since DbContextOptions and config are not being used in the actual test code. You could create a constructor in your db context marked as protected to allow the instantiation of the ExpenseDb object without any params.

Passing connection string to Entity framework at runt time for each call

My Entity framework context is as following
public partial class MyContext : DbContext, IMyContext
{
static MyContext()
{
System.Data.Entity.Database.SetInitializer<MyContext>(null);
}
public MyContext()
: base("Name=MyContext")
{
}
I am resolving it through autofac in the following way
builder.RegisterType(typeof(MainContext)).As(typeof(DbContext)).InstancePerLifetimeScope();
builder.RegisterType<MainContext>().As<IMainContext>().InstancePerRequest();
This db context gets called in repository layer
#region Fields
private readonly IMyContext _context;
#endregion
#region Constructors and Destructors
public EmployeeRepository(IMyContext context)
{
_context = context;
}
#endregion
public void Create(Employee emp)
{
this._context.Employee.Add(emp);
}
Now my issue is , I want to set the connection string dynamically per call. The connection string will be passed through a webapi which i want to pass on to this context. Can anyone help me how can i do that? I am confused about autofac here. Secondly how can i make sure each call sets connection string and does not cache it.
You can use a factory that will build the context and set the connectionstring for you.
public interface IContextFactory
{
IContext GetInstance();
}
public class MyContextFactory : IContextFactory
{
public IContext GetInstance()
{
String connectionString = this.GetConnectionString(HttpContext.Current);
return new MyContext(connectionString);
}
private String GetConnectionString(HttpContext context)
{
// do what you want
}
}
builder.RegisterType<MyContextFactory>()
.As<IContextFactory>()
.InstancePerRequest();
builder.Register(c => c.Resolve<IContextFactory>().GetInstance())
.As<IContext>()
.InstancePerRequest();
If you can't get connectionstring based on HttpContext, you can change contextFactory implementation to expect initialization by WebAPI before creating the instance. For example :
public interface IContextFactory
{
IContext GetInstance();
void Initialize(String connectionString);
}
public class MyContextFactory : IContextFactory
{
private String _connectionString;
public void Initialize(String connectionString)
{
this._connectionString = connectionString;
}
public IContext GetInstance()
{
if (this._connectionString == null)
{
throw new Exception("connectionString not initialized");
}
return new MyContext(this._connectionString);
}
}
At the beginning of your web API call (through attribute for example), you can call the Initialize method. Because the factory is InstancePerRequest you will have one instance for the duration of the request.
By the way, I'm not sure to understand this registration
builder.RegisterType(typeof(MainContext)).As(typeof(DbContext)).InstancePerLifetimeScope();
builder.RegisterType<MainContext>().As<IMainContext>().InstancePerRequest();
It looks buggy because you will have 2 different registration of the same type and not for the same scope, is it intended ? Furthermore, it doesn't sound a good idea to register a DbContext, do you need this registration ?
The following registration looks better :
builder.RegisterType<MainContext>()
.As<IMainContext>()
.As<DbContext>()
.InstancePerRequest();

What's DataService Best practice using Entity Framework and Repository and UnitOfWork Patterns

I'm using EF and MVVM pattern. My question is about the Data Access Layer. in DAL I have the following classes:
MyObjectContext which is technically the standard ObjectContext now, but some Unit-of-work methods will be added to it later.
Repository<TModel> which handles the most needed queries (such as Add, GetAll, ...) on different ObjectSets.
A bunch of DataServices which make use of repositories to provide a higher level of data access for Core.
The project I'm working on is a business application with about 100 EntitySets so far, and there are times when a single interaction of a user can involve up to 20 different EntitySets (updating most of them). I currently add .Include(params string[]) to my queries to prevent ObjectContextDisposedException but it doesn't seem to be a reliable solution.
The question is should I create an instance of MyObjectContext (and therefore Repository) in each of DataService methods (like the following codes, it seems to me that the ability of Unit of work would be useless in this case) or should I create it outside of DataService and pass it to the DataServices through their constructors (or directly to each of the DataService methods) to handle a bunch of database actions (different tables and queries) together. And how?
Here's what MyObjectContext looks like:
public class MyObjectContext : ObjectContext, IUnitOfWork
{
public MyObjectContext()
: base("name=EdmContainer", "EdmContainer")
{
ContextOptions.LazyLoadingEnabled = true;
}
#region IUnitOfWork Members
public void Commit()
{
SaveChanges();
}
#endregion
}
This is how Repository looks like:
public class Repository<TModel>
{
private readonly SoheilEdmContext _context;
public Repository(IUnitOfWork unitOfWork)
{
if (unitOfWork == null)
throw new ArgumentNullException("unitOfWork");
_context = unitOfWork as SoheilEdmContext;
}
public TModel FirstOrDefault(Expression<Func<TModel, bool>> where)
{
return _context.CreateObjectSet<TModel>().FirstOrDefault(where);
}
public void Add(TModel entity)
{
_context.CreateObjectSet<TModel>().AddObject(entity);
}
...
}
And this is how a common DataService looks like:
public class JobDataService : IDataService<Job>
{
#region IDataService<Job> Members
public Job GetSingle(int id)
{
Job model = null;
using (var context = new MyObjectContext())
{
var repos = new Repository<Job>(context);
model = repos.FirstOrDefault(x => x.Id == id);
}
return model;
}
public IEnumerable<Job> GetAll()
{
using (var context = new MyObjectContext())
{
var repos = new Repository<Job>(context);
var models = repos.GetAll();
return models;
}
}
public IEnumerable<Job> GetActives()
{
throw new NotImplementedException();
}
public int AddModel(Job model)
{
using (var context = new MyObjectContext())
{
var repos = new Repository<Job>(context);
repos.Add(model);
context.SaveChanges();
}
}
public void UpdateModel(Job model)
{
throw new NotImplementedException();
}
public void DeleteModel(Job model)
{
using (var context = new MyObjectContext())
{
var repos = new Repository<Job>(context);
var model = repos.FirstOrDefault(x => x.Id == model.Id);
if (model == null) return;
repos.Delete(model);
context.SaveChanges();
}
}
#endregion
}
Any kind of idea or insight would be appreciated.
You can create an instance of MyObjectContext in each service, like JobDataService, however, it makes your code messy and it is hard to maintain. Create instance of MyObjectContext outside of DataService is better. What you have now, if you have 100 EntitySets, you have to create 100 DataServices. That is because the use of "Repository Pattern" and "UnitOfWork" here is not efficient. I would suggest doing the following:
ObjectContext
public class MyObjectContext : ObjectContext
{
public MyObjectContext() : base("name=EdmContainer", "EdmContainer")
{
ContextOptions.LazyLoadingEnabled = true;
}
#region IUnitOfWork Members
public void Commit()
{
SaveChanges();
}
#endregion
}
Generic Repository
public interface IRepository<TModel> where TModel : class
{
void Add(TModel entity);
IEnumerable<TModel> GetAll();
// Do some more implement
}
public class Repository<TModel> : IRepository<TModel> where TModel : class
{
private readonly ObjectContext _context;
public Repository(ObjectContext context)
{
_context = context;
}
public virtual void Add(TModel entity)
{
_context.CreateObjectSet<TModel>().AddObject(entity);
}
public virtual IEnumerable<TModel> GetAll()
{
return _context.CreateObjectSet<TModel>();
}
}
UnitOfWork
public interface IUnitOfWork : IDisposable
{
IRepository<Job> Jobs { get; }
IRepository<User> Users { get;}
void Commit();
}
public class UnitOfWork : IUnitOfWork
{
private readonly SoheilEdmContext _context;
private readonly IRepository<Job> _jobRepository;
private readonly IRepository<User> _userRepository;
public UnitOfWork(SoheilEdmContext context)
{
_context = context;
_jobRepository = new Repository<Job>(_context);
_userRepository = new Repository<User>(_context);
}
public IRepository<Job> Jobs{get { return _jobRepository; }}
public IRepository<User> Users{get { return _userRepository; }}
public void Commit(){_context.Commit();}
public void Dispose()
{
if (_context != null)
{
_context.Dispose();
}
GC.SuppressFinalize(this);
}
JodDataSerivce
public interface IDataService
{
IEnumerable<Job> GetAll();
}
public class DataService : IDataService
{
private readonly IUnitOfWork _unitOfWork;
public DataService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public IEnumerable<Job> GetAll()
{
return _unitOfWork.Jobs.GetAll();
}
}
Here I used interface for implementing everything, if you want to do the same, you need to use IoC Container. I used the "Simple Injector", you can find it here:
Simple Injector
One more suggestion, if you feel like you have too many I/O operations to implement, like database access, querying data, etc., you should consider using Asynchronous. Below is a good video on Asynchronous.
How to Build ASP.NET Web Applications Using Async