How to Implement Castle Windsor IScopeAccessor to provide NHibernate Session Per ViewModel using LifestyleScoped - nhibernate

I am looking for some help creating an implementation of IScopeAccessor, or finding a new solution, that will allow me to provide an NHibernate session per ViewModel.
I know that Windsor now supports scoped lifestyles as seen (here). However the example creates the special scope with a using block and calling container.resolve within the using.
_container.Register(Component.For<A>().LifestyleScoped());
using (_container.BeginScope())
{
var a1 = _container.Resolve<A>();
var a2 = _container.Resolve<A>();
Assert.AreSame(a1, a2);
}
I can't think of a way to make this work because I don't want to pass around the container and I want the scope to be tied to the ViewModel that gets created, which will happen dynamically as they are needed.
As an alternative it looks like I can create an implementation of IScopeAccessor which, according to Krzysztof Koźmic (here) would allow me to
"... provide any scope you like. Scope is an abstract term here and it can be anything."
Unfortunately I cannot find an implementation of IScopeAccessor that isn't specific to a web based scenario and I am struggling to understand exactly what I need to do to turn "anything" into a valid scope.
I have found an example of exactly what I want to do using Ninject (http://www.emidee.net/index.php/2010/08/23/ninject-use-one-database-session-per-view-model/):
Bind<ISession>().ToMethod(ctx =>
{
var session = ctx.Kernel.Get<....>().BuildSessionFactory().OpenSession();
return session;
})
.InScope(context =>
{
var request = context.Request;
if (typeof(IViewModel).IsAssignableFrom(request.Service))
return request;
while ((request = request.ParentRequest) != null)
if (typeof(IViewModel).IsAssignableFrom(request.Service))
return request;
return new object();
});
In Ninject, the InScope indicates that any instances created by the binding should be reused as long as the object returned by the call back remains alive. Essentially, this call back returns the root level ViewModel (since ViewModels can be nested).
Any thoughts on how I can do the same thing or get the same result using Windsor?

The problem seems to be the place of creation.
If it's all about dependencies of viewmodels being constructed, you could maybe use boud lifestyle, as described in What's new...
Or you could alternatively use your own scope accessor, that is sensitive to viewmodels. for example like this:
public class ViewModelScopeAccessor : IScopeAccessor
{
private IDictionary<Guid, ILifetimeScope> scopes = new Dictionary<Guid, ILifetimeScope>();
private ILifetimeScope defaultScope;
public ViewModelScopeAccessor()
: this(new DefaultLifetimeScope())
{ }
public ViewModelScopeAccessor(ILifetimeScope defaultScope)
{
this.defaultScope = defaultScope;
}
public ILifetimeScope GetScope(CreationContext context)
{
var creator = context.Handler.ComponentModel.Implementation;
var viewModel = creator as IViewModel;
if (viewModel != null)
{
ILifetimeScope scope;
if (!scopes.TryGetValue(viewModel.UID, out scope))
{
scope = new DefaultLifetimeScope();
scopes[viewModel.UID] = scope;
}
return scope;
}
else
{
return defaultScope;
}
}
public void Dispose()
{
foreach (var scope in scopes)
{
scope.Value.Dispose();
}
defaultScope.Dispose();
scopes.Clear();
}
}
for the following viewmodel interface :
public interface IViewModel
{
string DisplayName { get; }
Guid UID { get; }
}
You of course could compare the viewmodels in other ways, it's just an example.
The drawback of both, the bound lifestyle and that scope accessor, is, that it won't work, if you use a typed factory inside your viewmodel, to lazily construct objects, since the scope accessor has no idea, from which object/method its factory method was called. But I think is is a general .NET issue, since a method does actually never know, from where it has been called.
So, you could then use your own factories, that produce only one instance per factory instance and make them scoped to your viewmodels too.
Hope this helps.

Related

AsyncLocal<T> doesn't reach controllers

I don't quite fully understand this situation, where AsyncLocal instance is set at a certain point in the AuthenticationHandler, but does not reach the controller, when it is injected into the constructor.
I've made it similar to how IHttpContextAccessor works, but still nowhere near. However, if I set the AsyncLocal from a Middleware, it reaches the controller. Also, setting the HttpContext.Items property from AuthenticationHandler works just fine.
Question: How is HttpContext able to retain Items property contents all the way, and is ASP.NET runtime disposing the captured ExecutionContext of my DomainContextAccessor for some security reason because of where it is being set at?
I've made a sample app to demo this use case. I'd really appreciate someone shedding the light on this problem.
You already have a good answer on "how should I fix this?" Here's more of a description of why it's behaving this way.
AsyncLocal<T> has the same semantics as logging scopes. Because it has those same semantics, I always prefer to use it with an IDisposable, so that the scope is clear and explicit, and there's no weird rules around whether a method is marked async or not.
For specifics on the weird rules, see this. In summary:
Writing a new value to an AsyncLocal<T> sets that value in the current scope.
Methods marked async will copy their scope to a new scope the first time it's written to (and it's the new scope that is modified).
I've made it similar to how IHttpContextAccessor works, but still nowhere near.
I don't recommend copying the design of IHttpContextAccessor. It works... for that very specific use case. If you want to use AsyncLocal<T>, then use a design like this:
static class MyImplicitValue
{
private static readonly AsyncLocal<T> Value = new();
public static T Get() => Value.Value;
public static IDisposable Set(T newValue)
{
var oldValue = Value.Value;
Value.Value = newValue;
return new Disposable(() => Value.Value = oldValue);
}
}
usage:
using (MyImplicitValue.Set(myValue))
{
// Code in here can get myValue from MyImplicitValue.Get().
}
You can wrap that into an IMyImplicitValueAccessor if desired, but note that any "setter" logic should be using the IDisposable pattern as shown.
AsyncLocal instance is set at a certain point in the AuthenticationHandler, but does not reach the controller
That's because your AuthenticationHandler sets the value but doesn't call the controller after setting that value (and it shouldn't).
However, if I set the AsyncLocal from a Middleware, it reaches the controller.
That's because middleware is calls the next middleware (eventually getting to the controller). I.e., middleware is structured like this:
public async Task InvokeAsync(HttpContext context)
{
using (implicitValue.Set(myValue))
{
await _next(context);
}
}
So the controllers are in the scope of when that AsyncLocal<T> value was set.
How is HttpContext able to retain Items property contents all the way
Items is just a property bag. It doesn't have anything to do with AsyncLocal<T>. It exists because it's a property on HttpContext, and it persists because the same HttpContext instance is used throughout the request.
is ASP.NET runtime disposing the captured ExecutionContext of my DomainContextAccessor for some security reason because of where it is being set at?
Not exactly. The AsyncLocal<T> is being set just fine; it's just that the controllers are not called within the scope of that AsyncLocal<T> being set.
So what must be happening is there is a execution context change which wipes that value out. It works with in the middleware because your controller is in the same execution context as your middleware.
Change your code to this:
private static void DomainContextChangeHandler(AsyncLocalValueChangedArgs<DomainContextHolder> args)
{
Trace.WriteLine($"ThreadContextChanged: {args.ThreadContextChanged}");
Trace.WriteLine($"Current: {args.CurrentValue?.GetHashCode()}");
Trace.WriteLine($"Previous: {args.PreviousValue?.GetHashCode()}");
Trace.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId}");
}
Now you can see when the context changes.
Here is something you could do:
private static void DomainContextChangeHandler(AsyncLocalValueChangedArgs<DomainContextHolder> args)
{
if (args.ThreadContextChanged && (args.PreviousValue != null) && (args.CurrentValue == null))
{
Trace.WriteLine(
"***** Detected context change with a previous value but setting current " +
"value to null. Resetting value to previous.");
_domainContextCurrent.Value = args.PreviousValue;
return;
}
Trace.WriteLine($"ThreadContextChanged: {args.ThreadContextChanged}");
Trace.WriteLine($"Current: {args.CurrentValue?.GetHashCode()}");
Trace.WriteLine($"Previous: {args.PreviousValue?.GetHashCode()}");
Trace.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId}");
}
But, that kinda defeats the purpose of using AsyncLocal as your backing store.
My suggestion is you drop the AsyncLocal and use normal class-scoped storage:
namespace WebApp.Models
{
public interface IDomainContextAccessor
{
DomainContext DomainContext { get; set; }
}
public sealed class DomainContextAccessor : IDomainContextAccessor
{
public DomainContext DomainContext { get; set; }
}
}
And inject it as scoped instead of singleton:
services.AddScoped<IDomainContextAccessor, DomainContextAccessor>();
It will do exactly what you want without any kludges -- AND, the future you (or devs) will absolutely understand what's going on and why it is the way it is.
No middleware, no AsyncLocal funny-business. It just works.
Your answer is here:
.net core AsyncLocal loses its value
In your DomainContextAccessor class when you set new value in this line: _domainContextCurrent.Value = new DomainContextHolder { Context = value };
you create NEW ExecutionContext in current thread and child threads.
So I suppose that mvc runs like this:
Middleware thread => you set value => some child thread with Controller execution which sees parent changes
But for UserAuthenticationHandler it feels it works like this:
Some controller factory creates controller with injected IDomainContextAccessor (1 context) => mvc executes auth handler in child task where you set value and create 2 context. But it's value does not go UP to parent (where controller 1 context exists) because you create new context when you set value. Even more your code gets parents context, gets reference to its value and makes property Context = null, so you will get null in Controller.
So to fix this you need to change your code:
public class DomainContext
{
private static AsyncLocal<DomainContext> _contextHolder = new AsyncLocal<DomainContext>();
public static DomainContext Current
{
get
{
return _contextHolder.Value;
}
}
public Job JobInfo { get; set; }
public static void InitContext()
{
_contextHolder.Value = new DomainContext();
}
}
//using in middleware:
DomainContext.InitContext();
//using in auth handler:
DomainContext.Current.JobInfo = ...
In example above you don't change DomainContext reference in _contextHolder.Value;
It remains the same but you only change value of JobInfo in it later in auth handler

confusion over using transient or scoped or singleton in .NetCore

Hey Guys i'm very new in software development,I still no idea when to use which,whats the meaning of service lifetime!it may seem stupid but please help me,i have an interface :
public interface IAccessInfo
{
public IEnumerable<AccessInfo> getResult();
}
what it supposed to do is to returns me the information about my Turbines;here is the implementation of it :
public class AcessInfoData:IAccessInfo
{
private DbContextClass db;
public AcessInfoData(DbContextClass context)
{
db = context;
}
public IEnumerable<AccessInfo> getResult()
{
var turbines = (from c in db.accessinf
where s.user_id == "i0004912"
select new AccessInfo
{
InfoType = c.type,
TurbineId = c.m_plc_id.ToString(),
TurbineIP = c.turbine_ip.ToString(),
TurbineIdSorting = c.turbine_id,
Blade = c.blade,
Certification = c.certification,
}).Distinct();
return turbines;
}
}
it gets an instance of my DB and gets the data;and in my controller i use it like this:
public class AcessInfoController : ControllerBase
{
private IAccessInfo _acess;
public AcessInfoController(IAccessInfo access)
{
_acess = access;
}
[HttpGet]
public IActionResult Index()
{
var rsult = _acess.getResult();
return Ok( rsult);
}
}
now in the Startup i registered it :
services.AddScoped<IAccessInfo, AcessInfoData>();
it works,but if you sk me why i user Scoped and not Singleton or transient i have no idea why,really,any one can make it clear for me?
I will try to explain a little about the mentioned cases:
scoped : For all needs of an object during the life of an operation (such as a request from the client) a single instance of the object is created. (It means that only one instance of the object is sent for all requirements during life time of a request)
Singleton: Creates only one instance of object and sends it for all requirements in the application scope.(For all needs everywhere in the program, only one instance of the object is sent, a bit like static objects).
Transient: Ioc container, makes an instance of object whenever code needs it, that is, it makes an instance for each requirement anywhere in the program and at any time, which means that if the program needs an object 3 times, it makes an independent instance for each.
Instance: In this case, each time an object is needed, only one instance of it is provided to the program, which you defined it in the startup section. (when defining it in the startup section, you specify how to create an instance).
I hope to reduce some of the ambiguities.

NInject: Create instances per user/session on convention binding

In summary:
I've undefined of unknowed IProducerPlugin implementations on several assemblies located on a plugins folder.
I've a Core object stores a list of current registered users.
Core is Composition Root.
So, I need:
To create as many IProducerPlugin inherited class objects as the number of registered users.
When a new user is un/registered I need to create / release these objects.
In order to register my "plugins":
this.Kernel.Bind(b => b.FromAssembliesMatching("*")
.SelectAllClasses()
.InheritedFrom(typeof(Extensibility.IProducerPlugin))
.BindAllInterfaces());
I'm not quite figuring out how to implement this.
Could you help me please?
I'll appreciate a LOT your help.
DI containers in general and Ninject in special are not suitable to add and remove new bindings to the container during runtime. Some, like Autofac, don't even allow adding bindings once the container is created.
Ninject allows adding new bindings at any time, but you cannot, ever, remove them (*from some use cases there's Rebind, but that's not the same).
kernel.Release(object) is not removing the binding, it's only removing all references to the object that it holds.
For example:
var foo = new object();
kernel.Bind<object>().ToConstant(foo);
to allow garbage collecting of foo you can do one of the following:
kernel.Release(foo);
kernel.Dispose(); kernel = null;
and exactly this is what kernel.Release(...) is for. Maybe you could also Release a singleton and thus force ninject to create a new one on the next request. But i don't know whether this really works, and if it does, it certainly is quite an unexpected hack.
So what you should do is manage the list/dictionary yourself. You can bind and inject the list/dictionary/manager what ever you call it using ninject, but you cannot have ninject manager the list itself.
I've managed to do something like that similar using this a IBindingGenerator interface method...
I've used .BindWith<>() binding method...
this.Kernel.Bind(b => b.FromAssembliesMatching("*")
.SelectAllClasses()
.InheritedFrom(typeof(Extensibility.IProducerPlugin))
.BindWith<PluginBindingGenerator<Extensibility.IProducerPlugin>>()
);
I've implemented a IBindingGenerator:
public class PluginBindingGenerator<T> : IBindingGenerator
{
public System.Collections.Generic.IEnumerable<Ninject.Syntax.IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, Ninject.Syntax.IBindingRoot bindingRoot)
{
if (type != null && !type.IsAbstract && type.IsClass && typeof(T).IsAssignableFrom(type))
{
Ninject.Syntax.IBindingWhenInNamedWithOrOnSyntax<object> syntax = bindingRoot.Bind(typeof(Extensibility.IProducerPlugin)).ToProvider(new PluginProvider());
yield return (Ninject.Syntax.IBindingWhenInNamedWithOrOnSyntax<object>)syntax;
}
}
}
public class PluginProvider : IProvider<object>
{
private System.Collections.Generic.Dictionary<Domain.Identity.ClientIdentity, Extensibility.IProducerPlugin> plugins;
And then, the provider:
public PluginProvider()
{
this.plugins = new System.Collections.Generic.Dictionary<Domain.Identity.ClientIdentity, Extensibility.IProducerPlugin>();
}
public object Create(IContext ctx)
{
//... I don't know what to do here...
return objects;
}
public Type Type
{
get { throw new NotImplementedException(); }
}
}

Looking for a Ninject scope that behaves like InRequestScope

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 Disposed 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");
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>().
As discussed in the other answer, InCallScope is not a good approach to solving this problem.
For now I'm dumping some code that works against the latest NuGet Unstable / Include PreRelease / Instal-Package -Pre editions of Ninject.Web.Common without a clear explanation. I will translate this to an article in the Ninject.Extensions.NamedScope wiki at some stagehave started to write a walkthrough of this technique in the Ninject.Extensions.NamedScope wiki's CreateNamedScope/GetScope article.
Possibly some bits will become Pull Request(s) at some stage too (Hat tip to #Remo Gloor who supplied me the outline code). The associated tests and learning tests are in this gist for now), pending packaging in a proper released format TBD.
The exec summary is you Load the Module below into your Kernel and use .InRequestScope() on everything you want created / Disposed per handler invocation and then feed requests through via IHandlerComposer.ComposeCallDispose.
If you use the following Module:
public class Module : NinjectModule
{
public override void Load()
{
Bind<IHandlerComposer>().To<NinjectRequestScopedHandlerComposer>();
// Wire it up so InRequestScope will work for Handler scopes
Bind<INinjectRequestHandlerScopeFactory>().To<NinjectRequestHandlerScopeFactory>();
NinjectRequestHandlerScopeFactory.NinjectHttpApplicationPlugin.RegisterIn( Kernel );
}
}
Which wires in a Factory[1] and NinjectHttpApplicationPlugin that exposes:
public interface INinjectRequestHandlerScopeFactory
{
NamedScope CreateRequestHandlerScope();
}
Then you can use this Composer to Run a Request InRequestScope():
public interface IHandlerComposer
{
void ComposeCallDispose( Type type, Action<object> callback );
}
Implemented as:
class NinjectRequestScopedHandlerComposer : IHandlerComposer
{
readonly INinjectRequestHandlerScopeFactory _requestHandlerScopeFactory;
public NinjectRequestScopedHandlerComposer( INinjectRequestHandlerScopeFactory requestHandlerScopeFactory )
{
_requestHandlerScopeFactory = requestHandlerScopeFactory;
}
void IHandlerComposer.ComposeCallDispose( Type handlerType, Action<object> callback )
{
using ( var resolutionRoot = _requestHandlerScopeFactory.CreateRequestHandlerScope() )
foreach ( object handler in resolutionRoot.GetAll( handlerType ) )
callback( handler );
}
}
The Ninject Infrastructure stuff:
class NinjectRequestHandlerScopeFactory : INinjectRequestHandlerScopeFactory
{
internal const string ScopeName = "Handler";
readonly IKernel _kernel;
public NinjectRequestHandlerScopeFactory( IKernel kernel )
{
_kernel = kernel;
}
NamedScope INinjectRequestHandlerScopeFactory.CreateRequestHandlerScope()
{
return _kernel.CreateNamedScope( ScopeName );
}
/// <summary>
/// When plugged in as a Ninject Kernel Component via <c>RegisterIn(IKernel)</c>, makes the Named Scope generated during IHandlerFactory.RunAndDispose available for use via the Ninject.Web.Common's <c>.InRequestScope()</c> Binding extension.
/// </summary>
public class NinjectHttpApplicationPlugin : NinjectComponent, INinjectHttpApplicationPlugin
{
readonly IKernel kernel;
public static void RegisterIn( IKernel kernel )
{
kernel.Components.Add<INinjectHttpApplicationPlugin, NinjectHttpApplicationPlugin>();
}
public NinjectHttpApplicationPlugin( IKernel kernel )
{
this.kernel = kernel;
}
object INinjectHttpApplicationPlugin.GetRequestScope( IContext context )
{
// TODO PR for TrgGetScope
try
{
return NamedScopeExtensionMethods.GetScope( context, ScopeName );
}
catch ( UnknownScopeException )
{
return null;
}
}
void INinjectHttpApplicationPlugin.Start()
{
}
void INinjectHttpApplicationPlugin.Stop()
{
}
}
}

Create an instance of ISession per ViewModel

here is my problem: I'm building a desktop application, with the following tools:
Caliburn
Ninject
NHibernate
All my view models and repositories are instanciated with Ninject. My repositories all need an ISession in their constructor.
I'd like to follow ayende's advice concerning the ViewModels: each ViewModel opens a new session.
Is it possible to configure Ninject to open a new session when a ViewModel is created, and use this session inside the repositories used by this view model?
I had a look to the InScope function of Ninject, as well as the ICurrentSessionContext interface in NHibernate, but I don't know how to model all of that to get what I want...
Did someone make something like that before?
Thanks in advance
Mike
I solved a similar scenario leveraging the ViewModel lifecycle: I created an ISessionAware interface (with a SetSession method) to be implemented by repositories, then I initialized the repositories through ISessionAware in the OnInitialize method of the ViewModel (which is enforced by Caliburn when the VM is managed by a ScreenConductor).
Using reflection to inspect the properties holding the repositories, I could put all the infrastructure on a BaseDataVM class.
Using a scope in the container would be more elegant, I think, but I don't know Ninject.
I have a very similar project (except I'm not using Caliburn) and have been trying to figure out how to do this as well. I did come up with one method that works well for constructor injection using Ninject's InScope() method.
I have a static class called IoC that wraps access to Ninject's kernel. Since the dependencies are all injected into the constructor, the context is only relevant when the object is being created. So it doesn't matter what is supplied for context, but a Guid feels like the safe choice. Program.OpenSession() is a static method to open a new ISession.
public static class Ioc
{
private static readonly IKernel _kernel;
static IoC()
{
_kernel = new StandardKernel();
_kernel.Load(new ContextModule());
}
private static object _context;
public static T ResolveInContext<T>(object context)
{
_context = context;
var result = _kernel.Get<T>();
_context = null;
return result;
}
private class ContextModule : NinjectModule
{
public override void Load()
{
Bind<ISession>().ToMethod(x => Program.OpenSession()).InScope(x => _context);
Bind<frmCompanyViewer>().ToSelf().InScope(x => _context);
}
}
}
Usage is:
var frm = IoC.ResolveInContext<frmCompanyViewer>(Guid.NewGuid());
The form's constructor signature is:
public frmCompanyViewer(ISession session, ICompanyRepository companyRepository)
I verified that with InScope on the bindings, the same ISession that is used to construct frmCompanyViewer is also used to construct companyRepository. If I remove InScope then two ISessions are used.
Edited to add: This would also work, see comments. This should be made thread safe for a real application. I changed the method name to ConstructInContext to clarify that the context only applies during object construction.
public static T ConstructInContext<T>()
{
_context = Guid.NewGuid();
var result = _kernel.Get<T>();
_context = null;
return result;
}
We have this with AOP, in unhaddins.
Is called "Conversation per Business Transaction".
search in google
here you have ;)
http://groups.google.com/group/unhaddins/browse_thread/thread/29eca74a83df5faf/d9fab4062d4cb4c4?lnk=gst&q=ninject#d9fab4062d4cb4c4
Well, I've found a solution thanks to the ninject group.
The solution here is to use the function InScope when I bind ISession, and browse in the IContext variable to inspect the services. If one service in the request hierarchy is assignable to the base class of my view models, I use the context as scope.
So the first time an ISession will be injected in the constructor of my ViewModel, a new scope is used. And all subsequent calls to ISession inside the constructor of the ViewModel will be resolved with the same scope. And then only one session is created for my ViewModel.
Here is the code:
Bind<ISession>().ToMethod(ctx =>
{
var session = ctx.Kernel.Get<INHibernateSessionFactoryBuilder>()
.GetSessionFactory()
.OpenSession();
session.FlushMode = FlushMode.Commit;
return session;
})
.InScope(ctx =>
{
var request = ctx.Request;
if (request.Service is IScreen)
return request;
while ((request = request.ParentRequest) != null)
if (typeof(IScreen).IsAssignableFrom(request.Service))
return request;
return new object();
});
And the constructor of the viewmodel must contains all the injected dependencies which rely on the ISession:
[Inject]
public PlayersManagementViewModel(ISession session, IPlayersRepository playersRepository)
{
}
Hope that helps