We are using Ninject.MVC5 and Ninject.Extention.Conventions in a multi-tenant web environment with multiple databases, one for each tenant along with a primary EF database. When a user logins in, we find them in the primary database and determine what database they should work with so we can bind all our datacontexts to that DB. (We use EF for the primary database and Linq to SQL for the tenant DB).
Here is the initial bind:
private static void RegisterServices(IKernel kernel)
string TennantConnection= ConfigurationManager.AppSettings["DSN"] ?? "";
kernel.Bind<TenantDB>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument(typeof(string), TennantConnection);
Where TennantConnection is a dummy default connection string initially
Here is the Rebind that is called after the login with the updated connection string
kernel.Rebind<TenantDB>().ToSelf().InRequestScope().WithConstructorArgument(typeof(string), ConfigConnection);
The kernel is injected into the constructor for the rebind class as follows:
public DataContextTennant(IKernel kernel)
All of the rest of the injections are done by convention.
The issue is that when we deploy the site (it happens to be an Azure Cloud app) many of the users get an error of an invalid SQL connection after first login which I believe is due to the rebind. But if they use a private browser session the rebind seems to work both for that session and subsequent sessions.
Although I was unable to resolve it with Ninject, I was able to resolve the issue with Unity. From the controller, after retrieving the tenant connection string, I call this:
public static void RegisterDBTypes(IUnityContainer container, string connection)
{
container.RegisterType<ADataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
container.RegisterType<RDataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
container.RegisterType<PDataContext>(new PerRequestLifetimeManager(), (new InjectionConstructor(connection)));
}
This rebinds the data contexts which flows through to the various services and repositories.
I suggest that Ninject is working as expected. You should look into why the TenantDB is instanciated before login is complete and rebind is done. This should be what's causing your issues.
To do so, you should start with removing your default TenantDB binding:
string TennantConnection= ConfigurationManager.AppSettings["DSN"] ?? "";
kernel.Bind<TenantDB>()
.ToSelf()
.InRequestScope()
.WithConstructorArgument(typeof(string), TennantConnection);
because after all, this binding only results in a TenantDB which is unusable, so why bother? It's just post-poning the issue to later - making it harder to detect.
It's much better to fail fast - have ninject throw an ActivationException! (which happens if there's no binding).
This should help you in finding out under which circumstances TenantDB is instanciated before login is complete.
Edit: Verification that IBindingRoot.Rebind works:
public class Test
{
[Fact]
public void Foo()
{
const string text1 = "Text1";
const string text2 = "Text2";
var kernel = new StandardKernel();
kernel.Bind<string>().ToConstant(text1);
kernel.Get<string>().Should().Be(text1);
kernel.Rebind<string>().ToConstant(text2);
kernel.Get<string>().Should().Be(text2);
}
}
Ninject's rebind works. I think you're making a configuration mistake and when using ninject, your building parts of your object tree - which depend on the DbContext before the Rebind is done. Thus "pre-login DbContext" leaks through to after login.
If i should be mistaken, you should create a minimal verifiable example and post it on Ninject's Issue tracker.
Related
I have a .Net Core application where we are using dependency injection. All of our providers, as we call them, are registered in the Startup.cs file and are set up as services.AddTransient providers. I have a controller that injects one of the providers as a dependency, and that provider in turn injects a few other dependencies.
The problem I am having is that one of the other providers that is injected into the first one, is being called 8 times. I have placed a break point on a few lines in the constructor method for the second dependency, and I can see it's being called 8 times. I went through the dependency chain and could not find a reason for this. I also stepped through the start of the application and noticed there were a number of dependencies that were created, that had nothing to do with the provider I was starting with. Am I using dependency injection incorrectly? I just can't find an explanation as to why it would load a dependency 8 times.
This is part of my startup class, if it helps.
services.AddTransient<IResourceProvider, ResourceProvider>();
services.AddTransient<IStaticListProvider, StaticListProvider>();
services.AddTransient<IPlanActivityLineItemProvider, PlanActivityLineItemProvider>();
services.AddTransient<IPlanRevisionProvider, PlanRevisionProvider>();
services.AddTransient<IProjectImportExportProvider, ProjectImportExportProvider>();
services.AddTransient<IExpandoParserProvider, ExpandoParseProvider>();
services.AddTransient<IProjectPlanProvider, ProjectPLanProvider>();
services.AddTransient<IActualsImportProvider, ActualsImportProvider>();
services.AddTransient<IReportProvider, ReportProvider>();
services.AddTransient<IProjectWorkFlowProvider, ProjectWorkflowProvider>();
EDIT:
Sorry, I didn't provide enough code to help with this. Our controller takes in a dependency, we call that class a Provider. Most of our providers also inject one or more dependencies. The code below is the start of the provider class, along with the constructor method. Hopefully this explains the Provider issue. I placed a break point on one of the lines of the constructor, and as the application was starting, it stopped at this break point 8 times, which leads me to believe that it's instantiating the provider 8 times. And I can't find a reason for this. I followed the dependency chain and it's not like it's being called again in another provider. I don't see a logical explanation for it. When an application starts, is every single service instantiated?
private readonly IURCSRepo _repo;
private readonly IPlanRevisionProvider _planRevisionProvider;
private readonly IExcelFileAndSheetValidationProvider _excelImportValidationProvider;
private readonly IExcelExtensionsProvider _excelExtensionsProvider;
private readonly IEmailProvider _emailProvider;
private readonly string _rpmActuals = "Actuals";
private TableParser<ActualHoursLineItemImportModel> _rpmLineItemTableParser;
private List<RoleDescriptionModel> roleModels = new List<RoleDescriptionModel>();
private List<PlanResourceModel> resources = new List<PlanResourceModel>();
private List<ProjectPlanHeaderModel> projectPlanHeaders = new List<ProjectPlanHeaderModel>();
private List<ChargingStrategyModel> chargingStrategies = new List<ChargingStrategyModel>();
private List<HoursTypeModel> hoursTypes = new List<HoursTypeModel>();
private readonly UserManager<ApplicationUser> _userManager;
public ActualsImportProvider(IURCSRepo uRCSRepo, IPlanRevisionProvider planRevisionProvider, IExcelFileAndSheetValidationProvider excelFileAndSheetValidationProvider, IExcelExtensionsProvider excelExtensionsProvider, IEmailProvider emailProvider, UserManager<ApplicationUser> userManager)
{
_repo = uRCSRepo;
_planRevisionProvider = planRevisionProvider;
_excelImportValidationProvider = excelFileAndSheetValidationProvider;
_excelExtensionsProvider = excelExtensionsProvider;
_rpmLineItemTableParser = new TableParser<ActualHoursLineItemImportModel>(_excelExtensionsProvider);
_emailProvider = emailProvider;
roleModels = _repo.GetAllRoleDescriptions(true).MakeRoleDescriptions();
resources = _repo.GetAllActiveResources().MakePlanResources();
projectPlanHeaders = _repo.GetAllProjectPlanHeaders().MakeProjectPlanHeaderList();
chargingStrategies = _repo.GetAllChargingStrategies().MakeChargingStrategies();
hoursTypes = _repo.GetAllHoursTypes(true).MakeHoursTypes();
_userManager = userManager;
}
Edit 2: I tried to set the services up as AddSingleton and I got the following error. This the repo interface, and it's used on 90% of our providers:
InvalidOperationException: Cannot consume scoped service 'XXXXXXXSystem.Data.Entities.XXXXXXXSystemContext' from singleton 'XXXXXXXSystem.Data.Interfaces.IXXXXRepo'.
I am setting all of these up in the Startup.cs file, under ConfigureServices(IServiceCollection services).
Based on what I have read and what some of you are saying, there seems to be a fundamental problem with how our DI container is set up. I say this because there are so many (10 or 20) providers that load up, when there are only 2 required for the index page. And, as I've said, some are loading multiple times. From what I understand, it should only load what is needed. We are using the Microsoft DI container. Should I be using something like AutoFac instead?
Edit 3: I started going through the break points and looking at the call stack, like someone suggested. We use service filters extensively on our controllers, in order to intercept the request to provide information that's common. We have a base filter, and then some other filters that are more specific to a particular controller. So, the specific filter calls the base filter to get some basic information. I discovered that one of our filters was calling a provider, and that provider called a number of others that have nothing to do with the initial page of the application.
I think this may be boiling down to some poor architecture, in that we have to many providers that are referencing too many others. Following the dependency chain gets confusing, once I looked at the filters.
Does this sound about right?
Transient objects are always different; a new instance is provided to every controller and every service (your case I presume).
Scoped objects are the same within a request, but different across different requests.
Singleton objects are the same for every object and every request.
You could register your services as Singletons so they only get instantiated once:
services.AddSingleton<ISomeService, SomeService>();
tl;dr How can I use Entity Framework in a multithreaded .NET Core API application even though DbContext is not threadsafe?
Context
I am working on a .NET Core API app exposing several RESTful interfaces that access the database and read data from it, while at the same time running several TimedHostedServices as background working threads that poll data regularly from other webservices and store them into the database.
I am aware of the fact that DbContext is not threadsafe. I read a lot of docs, blog Posts and answers here on Stackoverflow, and I could find a lot of (partly contradictory) answers for this but no real "best practice" when also working with DI.
Things I tried
Using the default ServiceLifetime.Scoped via the AddDbContext extension method results in exceptions due to race conditions.
I don't want to work with locks (e.g. Semaphore), as the obvious downsides are:
the code is polluted with locks and try/catch/finally for safely releasing the locks
it doesn't really seem 'robust', i.e. when I forget to lock a region that accesses the DbContext.
it seems redundant and 'unnatural' to artificially syncronize db access in the app when working with a database that also handles concurrent connections and access
Not injecting MyDbContext but DbContextOptions<MyDbContext> instead, building the context only when I need to access the db, using a using statement to immediatelly dispose it after the read/write seems like a lot of resource usage overhead and unnecessarily many connection opening/closings.
Question
I am really puzzled: how can this be achived?
I don't think my usecase is super special - populating the db from a Background worker and querying it from the web API layer - so there should be a meaningful way of doing this with ef core.
Thanks a lot!
You should create a scope whenever your TimedHostedServices triggers.
Inject the service provider in your constructor:
public MyServiceService(IServiceProvider services)
{
_services = services;
}
and then create a scope whenever the task triggers
using (var scope = _services.CreateScope())
{
var anotherService = scope.ServiceProvider.GetRequiredService<AnotherService>();
anotherService.Something();
}
A more complete example is available in the doc
Another approach to create own DbContextFactory and instantiate new instance for every query.
public class DbContextFactory
{
public YourDbContext Create()
{
var options = new DbContextOptionsBuilder<YourDbContext>()
.UseSqlServer(_connectionString)
.Options;
return new YourDbContext(options);
}
}
Usage
public class Service
{
private readonly DbContextFactory _dbContextFactory;
public Service(DbContextFactory dbContextFactory)
=> _dbContextFactory = dbContextFactory;
public void Execute()
{
using (var context = _dbContextFactory.Create())
{
// use context
}
}
}
With factory you don't need to worry about scopes anymore, and make your code free of ASP.NET Core dependencies.
You will be able to execute queries asynchronously, which not possible with scoped DbContext without workarounds.
You always be confident about what data saved when calling .SaveChanges(), where with scoped DbContext there are possibilities that some entity were changed in other class.
I'm in a situation where the classic functionality of vnext's DI container is not enough to provide me with the correct functionality. Let's say I have a DataService that gets data from a database like this:
public class DataService : IDataService, IDisposable {
public List<MyObject> GetMyObjects()
{
// do something to fetch the data...
return myObjects;
}
}
I can then register this service in the DI container during the configuration phase in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(typeof(IDataService), typeof(DataService));
}
This ensures the correct lifecylce of the service (one per request scope), however, I need the service to access a different database when a different request is made. For simplicity reasons, let's say the following scenario applies:
when a request to my Web API is made, the DataService will access the currently logged in user, which contains a claim called Database which contains the information which database to use.
the DataService is then instantiated with the correct database connection.
In order to get the second step to work, I have created a constructor for the DataService like this:
public DataService(IHttpContextAccessor accessor)
{
// get the information from HttpContext
var currentUser = accessor.HttpContext.User;
var databaseClaim = currentUser.Claims.SingleOrDefault(c => c.Type.Equals("Database"));
if (databaseClaim != null)
{
var databaseId = databaseClaim.Value;
// and use this information to create the correct database connection
this.database = new Database(databaseId);
}
}
By using the currently logged in user and his claims, I can ensure that my own authentication middleware takes care of providing the necessary information to prevent attackers from trying to access the wrong database.
Of course adding the IDisposable implementation is required to cleanup any database connections (and gets called correctly using the scope lifecycle).
I can then inject the DataService into a controller like this
public MyController : Controller
{
private IDataService dataService;
public MyController(IDataService dataService)
{
this.dataService = dataService;
}
}
This all works fine so far.
My questions now are:
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Is this method really safe? Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
What you have is fine.
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Not really. You can use delegate registration but it's the same problem.
Is this method really safe?
Yes
Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
Nope. The IHttpContextAcessor uses AsyncLocal (http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html) to provide access to the "current" http context.
Is this a nice way to use the LINQ context during one http request? In almost every request i have some selects from the database and some inserts/updates. It seams to work but I dont know how this will work with heavy traffic to the servers and on load balanced servers, anyone have any opinions/ideas about this way to keep the Context during the entire lifespan of the Request?
public static AccountingDataContext Accounting
{
get
{
if (!HttpContext.Current.Items.Contains("AccountingDataContext"))
{
HttpContext.Current.Items.Add("AccountingDataContext", new AccountingDataContext(ConfigurationManager.ConnectionStrings["SQLServer.Accounting"].ConnectionString));
}
return HttpContext.Current.Items["AccountingDataContext"] as AccountingDataContext;
}
}
This is a generally good idea on some levels. But you probably want to push instantiation back from the Begin_Request event. With the integrated pipeline, you will be initializing a rather expensive DB Context for every single request to your site. Including favicon.ico, all your stylesheets and all your images.
Best, simple implementation of something that only instantiates it when something asks for the context is Ayende's example for NHibernate's ISession; you can just replace it with the appropriate bits to instantiate your L2S context.
I'm using Unity for dependency injection, but the idea is the same:
protected void Application_BeginRequest() {
var childContainer = this.Container.CreateChildContainer();
HttpContext.Current.Items["container"] = childContainer;
this.ControllerFactory.RegisterTypes(childContainer);
}
protected void Application_EndRequest() {
var container = HttpContext.Current.Items["container"] as IUnityContainer;
if (container != null) {
container.Dispose();
}
}
The container is responsible for setting up a number of things, one of which is the data context. Works like a charm. I haven't done load balancing, but can't imagine you'd run into issues there either. The request gets its own context, which is wrapping a single user connecting to a database. No different that using old school ADO .NET for data access.
I am creating custom membership provider for my asp.net application. I have also created a separate class "DBConnect" that provides database functionality such as Executing SQL statement, Executing SPs, Executing SPs or Query and returning SqlDataReader and so on...
I have created instance of DBConnect class within Session_Start of Global.asax and stored to a session. Later using a static class I am providing the database functionality throughout the application using the same single session. In short I am providing a single point for all database operations from any asp.net page.
I know that i can write my own code to connect/disconnect database and execute SPs within from the methods i need to override. Please look at the code below -
public class SGI_MembershipProvider : MembershipProvider
{
......
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
if (!ValidateUser(username, oldPassword))
return false;
ValidatePasswordEventArgs args = new ValidatePasswordEventArgs(username, newPassword, true);
OnValidatingPassword(args);
if (args.Cancel)
{
if (args.FailureInformation != null)
{
throw args.FailureInformation;
}
else
{
throw new Exception("Change password canceled due to new password validation failure.");
}
}
.....
//Database connectivity and code execution to change password.
}
....
}
MY PROBLEM -
Now what i need is to execute the database part within all these overriden methods from the same database point as described on the top. That is i have to pass the instance of DBConnect existing in the session to this class, so that i can access the methods.
Could anyone provide solution on this. There might be some better techniques i am not aware of that. The approach i am using might be wrong. Your suggessions are always welcome.
Thanks for sharing your valuable time.
Understanding the lifecycle of the membership provider would help clear this up.
An instance of the membership provider is spun up when the application is started and remains active for the lifetime of the application AppDomain, which in practice equates to the application lifecycle. e.g. If for some reason the AppDomain cycles, the application is disposed and a new instance is spun up. A new instance of the registered membership provider is spun up on first use.
You need to either instantiate an instance of you data access class within your membership provider implementation or access static methods from within your provider. I prefer to use an instance.
Separating the membership provider from it's data access by creating singletons or stashing it in application is a hack in my opinion and will lead to nothing but pain, sorrow, lost sleep and credibility amongst your peers.
Cheers and good luck.
Dont keep a seperate instance of the DBConnect class in session, you will end up creating a class for each user! This will seriously affect scalability.
You can do one of the following :
Place the class in Application state
Use the singleton pattern
Make the class and all the methods in the class static.
My recommendation is to go for number 3. You dont usually need to create an instance of a class that does database crud operations eg
public static class DBConnect
{
public static ChangePassword(string userId, string password)
{
//Implementation here
}
}
Then you can simply call this code in your provider without creating an instance:
DBConnect.ChangePassword(x,y);