Having more than one entity context - asp.net-core

Let's say I've extended identity framework dbContext to build my own, and I've our authenticated controller who get injected with the dbContext and fetch an entity related with the current ApplicationUser, entity framework will relate the two entities leading to a server error because of circular references.
We don't want to serialize circular references.
So we create a new dbContext in the method who istantiate a new dbcontext and query the unrelated entities, this will work. But this is not testable, we don't want our controller to strictly depends on the dbContext, we want it to be injected.
So we add a second parameter, in the constructor, unfortunately this will make the system inject the same dbContext twice, not so useful.
We tried to create a class fakeDbContext who inherit from dbContext and add it services and use it, but now we got two dbcontext, who can potentially generate migrations and configurations and errors...
Which is the right way of doing this in the new MVC6 ?
Edit...
I found out that if my controller require an IEnumerable<dbContext> i get all the object registered as service of that type, so just doubling the part in startup.cs where we add the dbContext in the service registration area i get two of them...
The drawback here is that i don't know wich one is the virgin one, it looks like it goes in order of registration, but i have no clue, if this will change.
Edit 2 ...
I've created a TransientDbService class who have just a factory method taking the IserviceProvider, it use it to get the options to construct the dbContext, and then expose it. I've registered it as transient, then in the controller i require this service type.
the drawback here is if i'll ever need a third dbContext i should write more code, more code means errors and maintaning it.
Edit 3 ...
Not having two dbContext at all. The following setting allow me to have no relationships valorized.
Database.ChangeTracker.QueryTrackingBehavior = Microsoft.Data.Entity.QueryTrackingBehavior.NoTracking;
The drawback here is that i can't use my model graph, making everything more complex...
Edit 4 ...
https://github.com/aspnet/DependencyInjection/issues/352

You are right to think that no tracking queries will help in some cases, but other times you'll need to have more than one instance of the DbContext created.
You normally use the AddDbContext<TContext>() method in startup to make sure an instance of your context type is created per request and the right DbContextOptions and service provider get set on it. When you need to deviate from this pattern you have a few options, e.g.:
Include a constructor in your derived DbContext class that takes an IServiceProvider and passes it to the base constructor. Make sure your controller takes IServiceProvider. Once you do this you should be able to create DbContext manually with something like this:
using(var context1 = new MyDbContext(serviceProvider),
var context2 = new MyDbContext(serviceProvider))
{ ...
To avoid having to change the constructor signatures on your derived DbContext type you can take advantage of the DbContextActivator class (it is our Internal namespace), e.g.:
using(var context1 = DbContextActivator.CreateInstance<MyDbContext>(serviceProvider),
var context2 = DbContextActivator.CreateInstance<MyDbContext>(serviceProvider)
{...
Note: If you are still using AddDbContext<MyDbContext>(options => ...) in startup it should pull those options automatically from the service provider. But you can also choose to either include the DbContextOptions as a parameter in the constructor or override the OnConfiguring() method for that.
I am giving you examples that create two separate DbContexts in a using block, but you should also be able to mix those with the regular "per-request" DbContext you would get injected in the controller's constructor.
Besides these options currently available I have created a new issue to track other possible improvements on how to create multiple instance of the same DbContext type in the same request:
https://github.com/aspnet/EntityFramework/issues/4441

Related

why life time of DBContext object is very short

as per microsoft documentation " A DbContext instance is designed to be used for a single unit-of-work. This means that the lifetime of a DbContext instance is usually very short." what life time i choose scoped,singleton or transient? what problem occured if i use DBcontext object as singleton
When you call
services.AddDbContext<MyContext>(options => { });
The DbContext will be registered as a scoped service. So a new instance will be created for each and every request.
This is the most flexible approach, as it allows you to configure a different ConnectionString, based on the tenant that's associated with eg the HttpContext (for example: a different ConnectionString depending on the HttpContext.Request.Host)
Reusing same instance of DbContext will cause bugs: calling SaveChanges will save ALL tracked objects modified by a different consumers of DbContext.
If you worry about performance you may take a look at DbContext pooling

Create new DbContext dynamically when using ASP.Net Core dependency injection

I've configured my DbContext with services.AddDbContext() in the Startup class and constructor injection in my controllers works very well.
By default it's a scoped service, but I have one place in the app where I want to update a single property of an entity in a separate scope of work. So I need to create a new DbContext in the controller, but I'm not sure how. I want it to be created by the DI so I don't have to manually call the constructor and provide all the options needed. Is there a way to do that? Maybe there's a way to get the db context options from the DI? Then I could construct the DbContext easily.
The normal method of injecting a DbContext into your Controller works fine, as long as you are doing a small amount of work during an HTTP request. However, you might want to create a DbContext for a long-running a operation that queries/modifies a lot of records (causing SaveChangesAsync() to get bogged down because DbContext.ChangeTracker is tracking a lot of objects). In that case, you can create a scoped DbContext for each operation ("unit of work"). Here is an example ASP.NET Core Controller method:
/// <summary>
/// An endpoint that processes a batch of records.
/// </summary>
/// <param name="provider">The service provider to create scoped DbContexts.
/// This is injected by DI per the FromServices attribute.</param>
/// <param name="records">The batch of records.</param>
public async Task<IActionResult> PostRecords(
[FromServices] IServiceProvider provider,
Record[] records)
{
// The service scope factory is used to create a scope per iteration
var serviceScopeFactory =
provider.GetRequiredService<IServiceScopeFactory>();
foreach (var record in records)
{
// At the end of the using block, scope.Dispose() will be called,
// releasing the DbContext so it can be disposed/reset.
using (var scope = serviceScopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetService<MainDbContext>();
// Query and modify database records as needed
await context.SaveChangesAsync();
}
}
return Ok();
}
Also, I would recommend switching from AddDbContext() to AddDbContextPool() in Startup.cs to avoid creating/destroying DbContext objects for each request. The DbContextPool will reset the DbContext objects to a clean state after they go out of scope. (In case you were interested, DbContextPool calls DbContext.ResetState() and DbContext.Resurrect(), but I wouldn't recommend calling those directly from your code, as they will probably change in future releases.)
https://github.com/aspnet/EntityFrameworkCore/blob/v2.2.1/src/EFCore/Internal/DbContextPool.cs#L157
Finally, be aware that there are a few pitfalls of creating multiple DbContexts:
Using a large number of DbContexts in parallel may cause the database server to run out of active connections, since many EF database providers open a database connection per DbContext. (Requesting and releasing pooled DbContext objects in a loop should be fine.)
There may be more efficient ways to do the same thing. On my project, I tested and found that running a single "upsert" on a single DbContext was significantly faster than running a SELECT and INSERT/UPDATE on a separate DbContext for each record. There are a number of implementations of upsert for EF Core. For example, here are two that I have used:
FlexLabs.Upsert: https://github.com/artiomchi/FlexLabs.Upsert
EF Extensions BulkMerge: https://entityframework-extensions.net/bulk-merge
One option is to inject IDbContextFactory into your comtroller to create contexts within using blocks.
https://msdn.microsoft.com/en-us/library/hh506876(v=vs.113).aspx

Autofac Multitenant Database Configuration

I have a base abstract context which has a couple hundred shared objects, and then 2 "implementation" contexts which both inherit from the base and are designed to be used by different tenants in a .net core application. A tenant object is injected into the constructor for OnConfiguring to pick up which connection string to use.
public abstract class BaseContext : DbContext
{
protected readonly AppTenant Tenant;
protected BaseContext (AppTenant tenant)
{
Tenant = tenant;
}
}
public TenantOneContext : BaseContext
{
public TenantOneContext(AppTenant tenant)
: base(tenant)
{
}
}
In startup.cs, I register the DbContexts like this:
services.AddDbContext<TenantOneContext>();
services.AddDbContext<TenantTwoContext>();
Then using the autofac container and th Multitenant package, I register tenant specific contexts like this:
IContainer container = builder.Build();
MultitenantContainer mtc = new MultitenantContainer(container.Resolve<ITenantIdentificationStrategy>(), container);
mtc.ConfigureTenant("1", config =>
{
config.RegisterType<TenantOneContext>().AsSelf().As<BaseContext>();
});
mtc.ConfigureTenant("2", config =>
{
config.RegisterType<TenantTwoContext>().AsSelf().As<BaseContext>();
});
Startup.ApplicationContainer = mtc;
return new AutofacServiceProvider(mtc);
My service layers are designed around the BaseContext being injected for reuse where possible, and then services which require specific functionality use the TenantContexts.
public BusinessService
{
private readonly BaseContext _baseContext;
public BusinessService(BaseContext context)
{
_baseContext = context;
}
}
In the above service at runtime, I get an exception "No constructors on type 'BaseContext' can be found with the constructor finder 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'". I'm not sure why this is broken....the AppTenant is definitely created as I can inject it other places successfully. I can make it work if I add an extra registration:
builder.RegisterType<TenantOneContext>().AsSelf().As<BaseContext>();
I don't understand why the above registration is required for the tenant container registrations to work. This seems broken to me; in structuremap (Saaskit) I was able to do this without adding an extra registration, and I assumed using the built in AddDbContext registrations would take care of creating a default registration for the containers to overwrite. Am I missing something here or is this possibly a bug in the multitenat functionality of autofac?
UPDATE:
Here is fully runable repo of the question: https://github.com/danjohnso/testapp
Why is line 66 of Startup.cs needed if I have lines 53/54 and lines 82-90?
As I expected your problem has nothing to do with multitenancy as such. You've implemented it almost entirely correctly, and you're right, you do not need that additional registration, and, btw, these two (below) too because you register them in tenant's scopes a bit later:
services.AddDbContext<TenantOneContext>();
services.AddDbContext<TenantTwoContext>();
So, you've made only one very small but very important mistake in TenantIdentitifcationStrategy implementation. Let's walk through how you create container - this is mainly for other people who may run into this problem as well. I'll mention only relevant parts.
First, TenantIdentitifcationStrategy gets registered in a container along with other stuff. Since there's no explicit specification of lifetime scope it is registered as InstancePerDependency() by default - but that does not really matter as you'll see. Next, "standard" IContainer gets created by autofac's buider.Build(). Next step in this process is to create MultitenantContainer, which takes an instance of ITenantIdentitifcationStrategy. This means that MultitenantContainer and its captive dependency - ITenantIdentitifcationStrategy - will be singletons regardless of how ITenantIdentitifcationStrategy is registered in container. In your case it gets resolved from that standard "root" container in order to manage its dependencies - well, this is what autofac is for anyways. Everything is fine with this approach in general, but this is where your problem actually begins. When autofac resolves this instance it does exactly what it is expected to do - injects all the dependencies into TenantIdentitifcationStrategy's constructor including IHttpContextAccessor. So, right there in the constructor you grab an instance of IHttpContext from that context accessor and store it for using in tenant resolution process - and this is a fatal mistake: there's no http request at this time, and since TenantIdentitifcationStrategy is a singleton it means that there will not ever be one for it! So, it gets null request context for the whole application lifespan. This effectively means that TenantIdentitifcationStrategy will not be able to resolve tenant identifier based on http requests - because it does not actually analyze them. Consequently, MultitenantContainer will not be able to resolve any tenant-specific services.
Now when the problem is clear, its solution is obvious and trivial - just move fetching of request context context = _httpContextAccessor.HttpContext to TryIdentifyTenant() method. It gets called in the proper context and will be able to access request context and analyze it.
PS. This digging has been highly educational for me since I had absolutely no idea about autofac's multi-tenant concept, so thank you very much for such an interesting question! :)
PPS. And one more thing: this question is just a perfect example of how important well prepared example is. You provided very good example. Without it no one would be able to figure out what the problem is since the most important part of it was not presented in the question - and sometimes you just don't know where this part actually is...

Ninject: Construct all classes that inherit from an abstract class that also pass some custom Boolean logic

I am looking to find out how to use Ninject to get all instances that inherit from a base class that also pass some custom Boolean criteria. This is a continuation of a previous question. This is how I am creating a binging for all classes that inherit from MyAbstractClass.
_kernel.Bind(x => { x
.FromThisAssembly()
.SelectAllClasses()
.InheritedFrom<MyAbstractClass>()
.BindAllBaseClasses()
.Configure(syntax => syntax.InSingletonScope());
});
If I have the syntax correct, this will create a singleton scope binding for all classes that inherit from MyAbstractClass. Next I need to get/construct all the classes.
IEnumerable<MyAbstractClass> items = kernel.GetAll<MyAbstractClass>();
The code above will get/create all classes that inherit from MyAbstractClass.
Now I need to add one more step to this process. Based on “access roles” not all classes that inherit from MyAbstractClass should be created because the current user might not have access to functionality for every derived class. So I tried adding a CanLoad Boolean to each derived class and tried the following.
IEnumerable<MyAbstractClass> items = kernel.GetAll<MyAbstractClass>().Where(x => x.CanLoad == true);
While items did contain only the classes I expected, based on my CanLoad logic, I did see the constructor for each derived class being called because of the kernel.GetAll() method. Also it will have to create the class to call its CanLoad method, so that will not work. I thought about making the CanLoad static too. I also thought about a method that would look similar to the following.
public bool CanLoad<T>() where T : MyAbstractClass {
//return true if the current user has the correct access roles to create class of type T
}
So my question is how can I use the Ninject GetAll() method to get all classes that inherit from MyAbstractClass that also return true for the CanLoad Boolean method? I have been trying to use Ninject and Linq and possibly Reflection but I do not have a solution yet.
Edit:
To elaborate on the “permission logic”, at the very beginning our app calls out to a web service to get a list of LDAP groups/roles which are returned as a simple list of string. Each of the derived classes are actually ViewModels and each ViewModel “needs” a minimum role for its functionality to be usable by the current user. I thought I could tie the Ninject/binding/get logic with some custom “permission logic” and only bind/get classes the current user has access to. The list of VMs become an ItemsSource binding for a docking control (and we are considering using creating an ItemsSource binding for the Ribbon control as well). It’s all research at this point.
Add a When constraint to the binding:
.Configure(syntax => syntax
.When(... check permission here...)
.InSingletonScope());
Since you didn't say anything about the logic how you know whether the user has permission, i can't give you more specific information here. But generally speaking, you should add a 'When' constraint to your binding whenever you want to make sure it cannot and will not be instanciated unless a condition is met.

Share POCO types between WCF Data Service and Client Generated by Add Service Reference

I have a WCF Data Service layer that is exposing POCO entities generated by the POCO T4 template. These POCO entities are created in their own project (i.e. Company.ProjectName.Entities) because I'd like to share them wherever possible.
I have a set of interfaces in another project (Company.ProjectName.Clients) that reference these POCO types by adding an assembly reference to the Company.ProjectName.Entities.dll. One of the implementation of these interfaces is a .NET client that I want to consumes the service using the WCF Data Service Client Library.
I've used the Add Service Reference to add service reference. This generated the DataServiceContext client class and the POCO entities that are used by the service. However, these POCO types gemerated by the Add Service Reference utility now have a different namespace (i.e. Company.ProjectName.Clients.Implementation.WcfDsReference).
What that means is that the POCO types defined in the interfaces cannot be used by the types generated by the utility without have to cast or map.
i.e. Suppose I have:
1. POCO Entity: Company.ProjectName.Entities.Account
2. Interface: interface IRepository<Company.ProjectName.Entities.Account>{....}
3. Implementation: ServiceClientRepository : IRepository<Company.ProjectName.Entities.Account>
4. WcfDsReference: Company.ProjectName.Clients.Implementation.WcfDsReference
& Company.ProjectName.Clients.Implementation.WcfDsReference.Account
Let's say I want to create a DataServiceQuery query on the Account, I won't be able to do this:
var client = new WcfDsReference(baseUrl);
var accounts = client.CreateQuery<Company.ProjectName.Entities.Account>(...)
OR: client.AddToAccounts(Company.ProjectName.Entities.Account)
, because the CreateQuery<T>() expects T to be of type & Company.ProjectName.Clients.Implementation.WcfDsReference.Account
What I currently have to do is to pass the correct entity to the CreateQuery method and have to map the results back to the type the interface understands. (Possible with a mapper but doesn't seems like a good solution.)
So the question is, is there a way to get the Add Service Reference utility to generate methods that use the POCO types that are in the Company.ProjectName.Entities namespace?
One solution I am thinking of is to not use the utility to generate the DataServiceContext and other types, but to create my own.
The other solution is to update the IRepository<T> interface to use the POCO types generated by the utility. But this sounds a little bit hacky.
Is there any better solution that anyone has come up with or if there's any suggestion?
Ok, a few hours after starting the bounty I found out why it wasn't working as expected on my end.
It turns out that the sharing process is quite easy. All that needs to be done is mark the model classes with the [DataServiceKey] attribute. This article explains the process quite well, in the 'Exposing another Data Model' section
With that in mind, what I was trying to do is the following:
Placing the model on a separate class library project C, sharing it with both webapplication projects A and B
Create the data service on project A
Add the service reference on project B
Delete the generated model proxies out of the service reference, and update it to use my model classes in project C
Add the DataServiceKey attribute to the models, specifying the correct keys
When I tried this it did not work, giving me the following error:
There is a type mismatch between the client and the service. Type
{MyType} is not an entity type, but the type in the
response payload represents an entity type. Please ensure that types
defined on the client match the data model of the service, or update
the service reference on the client.
This problem was caused by a version mismatch between project C (which was using the stock implementations on the System.Data.OData assemblies) and the client project B that was calling the service (using the Microsoft.Data.OData assemblies in the packages). By matching the version on both ends, it worked the first time.
After all this, one problem remained though: The service reference procedure is still not detecting the models to be shared, meaning proxies are being created as usual. This led me to opt out of the automatic service integration mechanic, instead forcing me to go forward with a simple class of my own to serve as the client to the Wcf Data service. Basically, it's a heavily trimmed version of the normally autogenerated class:
using System;
using System.Data.Services.Client;
using System.Data.Services.Common;
using Model;
public class DataServiceClient : DataServiceContext
{
private readonly Lazy<DataServiceQuery<Unit>> m_units;
public DataServiceClient(Uri _uri)
: base(_uri, DataServiceProtocolVersion.V3)
{
m_units = new Lazy<DataServiceQuery<Unit>>(() => CreateQuery<Unit>("Units"));
}
public DataServiceQuery<Unit> Units
{
get { return m_units.Value; }
}
}
This is simple enough because I'm only using the service in readonly mode. I would still like to use the service reference feature though, potentially avoiding future maintenance problems, as evidenced by the hardcoded EntitySet name in this simple case. At the moment, I'm using this implementation and have deleted the service reference altogether.
I would really like to see this fully integrated with the service reference approach if anyone can share a workaround to it, but this custom method is acceptable for our current needs.