Looking for a Ninject scope that behaves like InRequestScope
entity-framework-5, ninject, repository-pattern, unit-of-work
Solution
UPDATE: This approach works against NuGet current, but relies in an anomaly in the `InCallscope` implementation which has been fixed in the current Unstable NuGet packages. I'll be tweaking this answer in a few days to reflect the best approach after some mulling over. NB the high level way of structuring stuff will stay pretty much identical, just the exact details of the `Bind<DbContext>()` scoping will work. (Hint: `CreateNamedScope` in unstable would work or one could set up the Command Handler as `DefinesNamedScope`. Reason I dont just do that is that I want to have something that composes/plays well with `InRequestScope`)
I highly recommend reading the `Ninject.Extensions.NamedScope` integration tests (seriously, find them and read and re-read them)
The `DbContext` is a Unit Of Work so no further wrapping is necessary.
As you want to be able to have multiple 'requests' in flight and want to have a single Unit of Work shared between them, you need to:
Bind<DbContext>()
.ToMethod( ctx =>
new DbContext(
connectionStringName: ConfigurationUtility.GetConnectionString() ))
.InCallScope();
The `InCallScope()` means that:
- for a given object graph composed for a single `kernel.Get()` Call (hence In Call Scope), everyone that requires an `DbContext` will get the same instance.
- the `IDisposable`.`Dispose()` will be called when a `Kernel.Release()` happens for the root object (or a `Kernel.Components.Get<ICache>().Clear()` happens for the root if it is not `.InCallScope()`)
There should be no reason to use `InNamedScope()` and `DefinesNamedScope()`; You don't have long-lived objects you're trying to exclude from the default pooling / parenting / grouping.
If you do the above, you should be able to:
var command = kernel.Get<ICommand>();
try {
command.Execute();
} finally {
kernel.Components.Get<ICache>().Clear( command ); // Dispose of DbContext happens here
}
The Command implementation looks like:
class Command : ICommand {
readonly IAccountRepository _ar;
readonly IBlockedIpRepository _br;
readonly DbContext _ctx;
public Command(IAccountRepository ar, IBlockedIpRepository br, DbContext ctx){
_ar = ar;
_br = br;
_ctx = ctx;
}
void ICommand.Execute(){
_ar.Insert(a);
_br.Insert(b);
_ctx.saveChanges();
}
}
Note that in general, I avoid having an implicit Unit of Work in this way, and instead surface it's creation and `Disposal`. This makes a Command look like this:
class Command : ICommand {
readonly IAccountService _as;
readonly IBlockedIpService _bs;
readonly Func<DbContext> _createContext;
public Command(IAccountService @as, IBlockedIpServices bs, Func<DbContext> createContext){
_as = @as;
_bs = bs;
_createContext = createContext;
}
void ICommand.Execute(){
using(var ctx = _createContext()) {
_ar.InsertA(ctx);
_br.InsertB(ctx);
ctx.saveChanges();
}
}
This involves no usage of `.InCallScope()` on the `Bind<DbContext>()` (but does require the presence of `Ninject.Extensions.Factory`'s `FactoryModule` to synthesize the `Func<DbContext>` from a straightforward `Bind<DbContext>()`.
Problem
On my service layer I have injected an `UnitOfWork` and 2 repositories in the constructor. The Unit of Work and repository have an instance of a `DbContext` I want to share between the two of them. How can I do that with Ninject ? Which scope should be considered ? I am not in a web application so I can't use `InRequestScope`. I try to do something similar... and I am using DI however, I need my UoW to be `Dispose`d and created like this. ``` using (IUnitOfWork uow = new UnitOfWorkFactory.Create()) { _testARepository.Insert(a); _testBRepository.Insert(b); uow.SaveChanges(); } ``` EDIT: I just want to be sure i understand… after look at https://github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScope i though about my current console application architecture which actually use Ninject. Lets say : Class A is a Service layer class Class B is an unit of work which take into parameter an interface (IContextFactory) Class C is a repository which take into parameter an interface (IContextFactory) The idea here is to be able to do context operations on 2 or more repository and using the unit of work to apply the changes. Class D is a context factory (Entity Framework) which provide an instance (keep in a container) of the context which is shared between Class B et C (.. and would be for other repositories aswell). The context factory keep the instance in his container so i don’t want to reuse this instance all the name since the context need to be disposed at the end of the service operaiton.. it is the main purpose of the InNamedScope actually ? The solution would be but i am not sure at all i am doing it right, the services instance gonna be transcient which mean they actually never disposed ? : ``` Bind<IScsContextFactory>() .To<ScsContextFactory>() .InNamedScope("ServiceScope") .WithConstructorArgument( "connectionString", ConfigurationUtility.GetConnectionString()); Bind<IUnitOfWork>().To<ScsUnitOfWork>(); Bind<IAccountRepository>().To<AccountRepository>(); Bind<IBlockedIpRepository>().To<BlockedIpRepository>(); Bind<IAccountService>().To<AccountService>().DefinesNamedScope("ServiceScope"); Bind<IBlockedIpService>().To<BlockedIpService>().DefinesNamedScope("ServiceScope"); ```