I have a web app and a windows service app.
The web app injects IPersonService into its MVC controllers.
The windows app also uses IPersonService.
The service takes 3 dependencies on IPersonRepo, IAddressRepo, IEmploymentRepo for example.
The implementations of the repositories take a DBContext for Entity Framework use.
In a web app I can register the DBContext as Bind<MyContext>().ToSelf().InRequestScope();
In the windows service its trickier. I could leave it so the DBContext is transient but that seems wrong.
So I thought I could make the services be the scope to determine the life cycyle of the DBContext but am completely unsure how I would go about that to make sure it worked well for a web app and windows service app.
Any help would be greatly appreciated
If it's important that all 3 repos use the same DbContext instance, you can do something like this:
var context = new DbContext(...);
Bind<IPersonRepo>().To<PersonRepo>().WithConstructorArgument("dbContext", context);
Bind<IAddressRepo>().To<AddressRepo>().WithConstructorArgument("dbContext", context);
Bind<IEmploymentRepo>().To<EmploymentRepo>().WithConstructorArgument("dbContext", context);
Like this the same context instance is shared between the repos.
If the repositories are not aware of each other's entities (and changes to these entities), you could simply inject a fresh instance of DbContext in each repo, by binding in transient scope (default behavior):
Bind<MyContext>().ToSelf();
Related
I have multiple hosted services each working with the database. I was getting the DbConcurrencyException, since all the hosted services were using the same instance of the dbContext simultaneously (at the start of the application).
I've resolved this issue by changing the lifetime of the dbContext from scoped to transient. What did I really change by this? Will the application connect and disconnect from the database each time I work with the dbContext? If not, is there any other issue?
IHostedService or BackgroundService are singletons. Also, by default DBContext (when you run AddDbContext) is scoped service. So in each hosted service, you need to open a scope using IServiceScopeFactory. Like this:
using var scope = _scopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>;
Regarding your question about what it has changed when you registered it as transient. Each time you inject and call DBContext it will instantiate a new DbContext. When the service is scoped, during one scope if you are going to call DBContext multiple times, you will get the same instance of DBContext, which will improve performance, so maybe this is what you need?
I have a .Net Core solution which consists of 3 projects like below:
Common : (All EF Data, migrations, Model, DbContext)
WebApi : (Rest api consuming DbContext from Common)
Worker : (Background services that aims to consume DbContext from Common)
I wanted to place all my EF logic and DbContext in Common and consume it from my other two projects.
WebApi is working fine,
but I couldn't use it from my hosted services found in Worker project.
There are 4 background workers and all of them require access to database so I wanted to get access to my DbContext inside them.
So, what is the proper way to reuse a DbContext across multiple projects.
It can be considered that all services need access some common tables. So isolating tables via different Dbcontexts is not an option for me.
This is my Startup.cs in WebApi:
services.AddDbContext<DataContext>(options => options
.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
This is my Program.cs in Worker:
services.AddDbContext<DataContext>(options => options
.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
It throws an error telling me that I can not consume a scoped service from a singleton one.
If you just want to reference a DbContext in your Web API + Background Workers, then I don't understand what the issue is: Just reference the Common project from your worker projects. From your post the issue looks to be that the service injection you are using successfully in the Web API doesn't work with the background worker services. (Assuming Windows Services?)
Start instead with a simpler scenario. Initiate the DbContext inside the service when used rather than inject it.
I.e.
using(var context = new DataContext("DefaultConnection"))
{
// ...
}
Your connection string configuration should be identifying NPGSQL as the provider so as long as that config is all set up in your Services config then the DBContext should be able to configure by connection string name. If that works then there will probably be a different mechanism for injection. From what I could quickly find it seems examples used a service locator pattern to resolve dependencies, I don't know if there are better options these days for Windows Services.
If injection isn't really an option and you have to resort to service locator-like implementations then I would probably consider something like a Lazy Property injection pattern I've used in the past:
public class WorkerService
{
private readonly IContainer _container = null;
public WorkerService(IContainer container)
{
_container = container ?? throw new ArgumentNullException("container");
}
private IDataContextFactory _contextFactory = null;
public IDataContextFactory ContextFactory
{
get { return _contextFactory ?? (_contextFactory = _container.Resolve<IDataContextFactory>()); }
set { _contextFactory = value; }
}
public void Execute()
{
using(var context = ContextFactory.Create()) // returns a DataContext.
{
// do stuff.
}
}
}
Where IContainer represents a contract interface for your given DI framework. (Unity, Autofac, etc.)
Alternatively, a unit of work for scoping a DbContext. Given a Service instance will be long running we don't want to inject or resolve a DbContext, but rather a Context Factory which we can use to receive an initialized DbContext which can be used in a dispose. Normally with web requests the instance is scoped to the request and disposed by the container at the end of the request. With a service we want to ensure the DbContext is disposed regularly. A DI can be set up so that transient instances of the context are returned, but those instances need to be disposed meaning it's not suited for constructor injection, but rather via a service locator. If a single DbContext instance was used and injected in the constructor of a service, it would live until the service stopped which would see that DbContext get slower and slower as time went on due to tracked entities.
I think you're a bit confused here. The thing you want to reuse is the DbContext code + all EF logic. You don't want to (can't) reuse same DbContext instance across projects (apps).
So to reuse the code, you just need to put all of your Model + DBContext in a project. Then in other projects, you can add reference to it. And start using it.
Remove all AddDbContext from Startup.cs/Program.cs.
Put DbContext connectionstring in Common project instead.
Create some CRUDs in Common project. Then all other projects can use Common's CRUD which is connected to same DbContext.
Edit: Due to lots of users mistakenly taking this as a ASP.NET specific question. Please note that my application is not a web application and I'm not using ASP.NET application (I'm using it's funtionality, that is available in .NET Core as well).
Recently, while configuring an Entity Framework DbContext lifetime in a Ninject DI, I have been digging through the .NET Core Dependency Injection, because it already has a functionality for registering DbContext and can be found here. The default context life time is ServiceLifetime.Scoped.
In the code peek, we can read that in the ASP.NET applications, "scoped" means:
scope is created around each server request
namespace Microsoft.Extensions.DependencyInjection
{
//
// Summary:
// Specifies the lifetime of a service in an Microsoft.Extensions.DependencyInjection.IServiceCollection.
public enum ServiceLifetime
{
//
// Summary:
// Specifies that a single instance of the service will be created.
Singleton = 0,
//
// Summary:
// Specifies that a new instance of the service will be created for each scope.
//
// Remarks:
// In ASP.NET Core applications a scope is created around each server request.
Scoped = 1,
//
// Summary:
// Specifies that a new instance of the service will be created every time it is
// requested.
Transient = 2
}
}
I'm trying to achieve a similar functionality in Ninject DI but it's really hard to state what would be the equivalent of scoped life time in Ninject, while speaking about .NET Core application (that isn't a web application!).
Ninject has that InRequestScope method, however it's only available for web applications, so it's really different from the .NET Core DI ServiceLifetime.Scoped setting.
Perhaps I would have to create some sort of a custom scope in Ninject, but still - I'm not really able to state, how to achieve exact the same scoped behaviour as in the .NET Core DI. To do that I need to be aware of how is the scoped life time working in context of a .NET Core application in .NET Core DI. My guess would be that there's one instance of a DbContext being created and is being disposed once the application quits.
Hence my questions:
How is .NET Core DI scope life time setting working and what is it's life cycle?
Is it possible to achieve a similar behaviour in Ninject DI?
How is .NET Core DI scope life time setting working and what is it's
life cycle?
.Net core internally works with class called ServiceScope. When new request is called (e.g. web request) new instance is created, with new service provider included. During request this service provider is used for dependency resolution. After request is finished, scope is disposed and also its service provider with its resolved services.
internal class ServiceScope : IServiceScope, IDisposable
{
private readonly Microsoft.Extensions.DependencyInjection.ServiceProvider _scopedProvider;
public ServiceScope(Microsoft.Extensions.DependencyInjection.ServiceProvider scopedProvider)
{
this._scopedProvider = scopedProvider;
}
public IServiceProvider ServiceProvider
{
get
{
return (IServiceProvider) this._scopedProvider;
}
}
public void Dispose()
{
this._scopedProvider.Dispose();
}
}
Is it possible to achieve a similar behaviour in Ninject DI?
As you have already noticed implementing custom scope is way to go. You can check how to do this in another answer:
Ninject - In what scope DbContext should get binded when RequestScope is meaningless?
EDIT:
Principle of .NET Core DI is the same like any other IOC container. It provides dependencies to your objects (MVC controllers etc.) by DI and controls its lifetime.
If you specify singleton lifetime for your DbContext than only one is
created, provided by DI when requested and hold in memory for
whole application/container lifetime.
If you specify transient you get new
one all the time DbContext is requested.
If you specify scoped,
lifetime of DbContext is bound to some disposable scope, which is created on the beggining of some logical request (http request in case of asp). When DbContext is
requested by DI for the first time, new one is created, hold in memory and you get always the same during
subsequent DI requests until the scope is disposed (with end of http request in case of asp) and DbContext with
it.
You can find similar parallel with TransactionScope. Here all the sqlCommands within the same TransactionScope are enlisted into the same sql transaction util the scope is disposed/committed.
There is extension method called InRequestScope, which is available in Ninject.Web.Common nuget package.
InRequestScope : https://github.com/ninject/Ninject.Web.Common/wiki/InRequestScope
You can correlate .net core and ninject DI methods
from https://github.com/ninject/Ninject/wiki/Object-Scopes
I have 3-layers architecture - asp.net web api, BLL and DAL. I use Ninject as dependency injector for injecting db context and objects between layers. As ORM i use Entity Framework . Injection of db context is processed in DAL. So every time some repository in BLL is instancied, new instance of db context is also created. Im doing it like this:
public class UserRepository : IUserRepository
{
private IChatDbModel _chatDbModel;
public UserRepository(IChatDbModel chatDbModel)
{
this._chatDbModel = chatDbModel;
}
It´s neccesary to say that PerWebRequest, which would solve my problem is not availible in lower layers than web api. Only web api layer has info about http request lifetime, so can use Ninject.Web.Common library.
My question is, is there a way how to share db context for whole request like using of PerWebRequest in this architecture? Or is really neccesary to create new instance of db context for every new instance of repository?
Edit
I forgot to mention that in each layer I´m referencing Ninject library and I´m registering mapping for the specific layer. The method in DAL looks like this:
public static void Register(IKernel kernel)
{
kernel.Bind<IChatDbModel>().To<ChatDbModel>();
}
in BLL it looks like this:
public static void Register(IKernel kernel)
{
kernel.Bind<IUserRepository>().To<UserRepository>();
NinjectDataAccess.Register(kernel);
}
in API it looks like this, it´s located in NinjectWebCommon.cs:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUserLogic>().To<UserLogic>();
NinjectLogic.Register(kernel);
}
so in each layer, I´m not only mapping it´s own objects but also calling register method of the layer lying below if any and with mechanism like this, I can register dependency mapping of each layer without referencing all layers in API, where I should not reference any other layer than BLL, so in my case DAL. If I reference the DAL in API layer, then it would be possible to define the mapping and call PerWebRequest, because I would have the objects, but I´m not and I think this should be avoided by the architecture, or am I wrong?
You can achieve per request instance by registering OnePerRequestHttpModule http module, which internally uses HttpContext lifecycle to track registered types and dispose them at the end of the request/response lifecycle.
After installing Ninject.Web.Common package, in NinjectWebCommon.cs you have to do (it will be added automatically once nuget package is installed)
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule))
And for your type registrations, you can do the following
//register IChatDbModel with per request scope
kernel.Bind<IChatDbModel>().To<ChatDbModel>().InRequestScope();
//register repositories with default transient scope
kernel.Bind<IUserRepository>().To<UserRepository>();
All your repositories will be transient, so everywhere they are injected , a separate instance will be supplied, but your DBContext instance will created and disposed per request.
I am assuming you have added both BAL and DAL reference to web api project, so that web api project has access to IChatDbModel to perform type registration in Ninject kernel.
I have an application in ASP.NET MVC that also have a WCF Service included in the same proyect.
Im using Autofac to manage dependency injection. The problem is that when the application is accessed throught web, I need the dependencies to be instanced per Http request. And when the application is accessed throught WCF, I need the dependencies to be instanced per dependency.
In Castle.Windsor, there is a proyect to manage hybrid lifestyles (in this link).
I need something similar, something like:
builder.Register<UnitOfMeasureService>(x => new UnitOfMeasureService())
.As<IUnitOfMeasureService>().HybridLifetimeInstance();
Are there a workaround to manage the instance lifetime depending on when the application has a HttpContext or not?
Autofac does not have support for custom lifestyle managers.
Autofac lifetimes revolve around scopes, which are nestable and can be optionally tagged with a known ID. That's how instance-per-HTTP-request works: A nested scope "tagged" with a known value ("AutofacWebRequest") is created when a web request comes in. The hierarchy looks like this:
Container (root lifetime scope)
Web Reqeust Scope (tagged "AutofacWebRequest")
Any child scopes you might create in your code
When using InstancePerHttpRequest it's basically the same as InstancePerMatchingLifetimeScope("AutofacWebRequest"). If you resolve the type, it falls back until it finds a scope with that name and then uses the same instance in that tagged scope.
In standard WCF hosting, Autofac resolves everything out of a child scope you can get from the instance context (AutofacInstanceContext.Current.OperationLifetime). You could create a child lifetime scope from that and manually tag it, then resolve your dependencies like this:
var opScope = AutofacInstanceContext.Current.OperationLifetime;
using(var requestScope = opScope.BeginLifetimeScope("AutofacWebRequest"))
{
// Resolve InstancePerHttpRequest items from requestScope
}
However, there's no way to do that automatically and the WCF hosting mechanism isn't currently architected in a way you can "plug in" and do this - if you needed the web request scope automatically created, you'd have to roll your own WCF hosting mechanism based on the Autofac source code.
The other option is to have two different containers - one for your web stuff and one for your WCF stuff - and register the component with a different lifetime in each container.
Beyond that... there's really no way to "switch" lifetimes based on context. A component gets one lifetime declared and has to live with it for that component registry.