Windsor LifeStyle - Shared instance per Graph
Asked Answered
A

2

2

I have 2 types of ViewModel's

      public class ViewModelA 
      {
          IService service;
          private ViewModelB childViewModel; 

           public ViewModelA(IService service,ViewModelB childViewModel)
           {
               this.service = service;
               this.childViewModel = childViewModel;
           }

           public ViewModelB ChildViewModel
           {
                get { return childViewModel; } 
            } 
      }  

      public class ViewModelB 
      {
          IService serivce;  
          public ViewModelB(IService service)
          {
              this.service = service;
          }  
      } 

I have a Service registered into a Windsor Container :

     public class Service : IService {}

     container.Register(Component.For<IService>()
                  .ImplementedBy<Service >().LifeStyle.Transient); 

I want ViewModelA and ViewModelB to share the same instance of IService.

I Do not wan't all instances of ViewModelA and ViewModelB to share the same instance.

Each Parent/Child Pair would have his own instance , i wan't to achieve this using DependencyInjection can this be done ?

I wan't this to be be done through Dependency Injection since i have an entire hierarchy of ViewModels under A and not just one (B) viewmodel.

VM A -> VM B -> VM C -> VM D ... (and let's say ill go over the all alphabet) all these need to share the same instance of IService.

and another instance of A and it's decedents would share a a different instance of IService.

Avogadro answered 31/7, 2014 at 16:41 Comment(9)
I know virtually nothing about Windsor, but I tend to assume it has similar features as other containers. In other containers, I would probably do this using child containers. You spawn off a new child container for each parent root, then you define the registration to be a singleton (it becomes a singleton per child container then).Maddocks
@ErikFunkenbusch , 10x i'll check into that.Avogadro
How and when is ChildViewModel initialized in the parent?Alexio
@WiktorZychla Let's say for the sake of argument the Parent (A) is injected a new instance of Child (B) in it's constructor, I Edited my question to accommodate the way it is created. are you going to ask me why i just don't expose a setter in ViewModelB and assign it the service through ViewModelA ?Avogadro
Does ViewModelA also depend on IService? Is IAService a typo?Therein
ViewModelA and ViewModelB Depend on IService and yes that's a typo.Avogadro
It sounds like you need Per Graph lifetime - something that Unity and StructureMap has, but Castle Windsor doesn't have out of the box. Is that correct? Why do you need this?Therein
yes , exactly .. i wasn't sure how to call it by nameAvogadro
I thought of creating a custom IScopeAccessor there i would hold a dictionary with of <Conductor,ILifeTimeScope> could this be a correct approach ? , even if so , it only seems like a can reference a MemberInfo describing ViewModelA , and a instance i could relate a scope to ..Avogadro
A
4

What worked for me is using : LifeStyle BoundTo

    container.Register(Component.For<IService>()
            .ImplementedBy<Service>()
            .LifeStyle.BoundTo<ViewModelA>());

Graph :

     public class ViewModelAConductor 
     {
         private List<ViewModelA> rootViewModels = new List<ViewModelA>();
         public ViewModelAConductor()
         {
              ViewModelA a1 = container.Resolvce<ViewModelA>(); 
              rootViewModels.Add(a1);

              ViewModelA a2 = container.Resolvce<ViewModelA>(); 
              rootViewModels.Add(a2); 
         }
     }

     public class ViewModelA
     {
          ViewModelB viewModelB;
          IService service;

          public ViewModelA(IService service,ViewModelB viewModelB) 
          {
              this.service = service;
              this.viewModelB = viewModelB;
          }               
     }

     public class ViewModelB
     {
          ViewModelC viewModelC;
          IService service;

          public ViewModelA(IService service,ViewModelC viewModelC) 
          {
              this.service = service;
              this.viewModelC = viewModelC;
          }               
     }  

     public class ViewModelC
     {
          IService service;

          public ViewModelA(IService service) 
          {
              this.service = service;                 
          }               
     }  

All ViewModels injected under the Graph of a1 have the same instance of IService.

All ViewModels injected under the Graph of a2 have the same instance of IService.

Avogadro answered 3/8, 2014 at 18:49 Comment(0)
T
4

You may be able to use Scoped Lifestyles. Here's an example of some unit tests that seem to do what you want:

[Fact]
public void VMsInSameScopeSharesService()
{
    var container = new WindsorContainer();
    container.Register(Component.For<ViewModelA>().LifestyleTransient());
    container.Register(Component.For<ViewModelB>().LifestyleTransient());
    container.Register(Component
        .For<IService>().ImplementedBy<NullService>().LifestyleScoped());

    using (container.BeginScope())
    {
        var a = container.Resolve<ViewModelA>();

        Assert.Equal(a.service, a.childViewModel.service);
    }
}

[Fact]
public void VMsInDifferentScopesDoNotShareServices()
{
    var container = new WindsorContainer();
    container.Register(Component.For<ViewModelA>().LifestyleTransient());
    container.Register(Component.For<ViewModelB>().LifestyleTransient());
    container.Register(Component
        .For<IService>().ImplementedBy<NullService>().LifestyleScoped());

    IService service1;
    using (container.BeginScope())
    {
        var a = container.Resolve<ViewModelA>();

        service1 = a.service;
    }
    IService service2;
    using (container.BeginScope())
    {
        var a = container.Resolve<ViewModelA>();

        service2 = a.service;
    }

    Assert.NotEqual(service1, service2);
}

However, this is quite an exotic requirement, which makes me wonder why you want it to behave exactly like this, or if you couldn't structure your code in a way that would make this simpler.

Therein answered 3/8, 2014 at 18:10 Comment(4)
chat.stackoverflow.com/rooms/18165/wpf , let me explain it to you in more details'Avogadro
The problem here , is how do i call BeginScope when IService is injected to the constuctor of ViewModelAAvogadro
@eranotzap If you don't have a clear place to do that, then perhaps you should either reconsider your design, or switch composition technology. It sounds like you need Per Graph lifetimes, which are trivial to implement with Pure DI, but which is also built into StructureMap and Unity. If you need Per Graph lifetimes in Castle Windsor, you'll probably need to implement your own custom lifetime.Therein
please see my answer below , BoundTo<T> gave me a per Graph life style for each instance of T and it's nested ViewModels.Avogadro
A
4

What worked for me is using : LifeStyle BoundTo

    container.Register(Component.For<IService>()
            .ImplementedBy<Service>()
            .LifeStyle.BoundTo<ViewModelA>());

Graph :

     public class ViewModelAConductor 
     {
         private List<ViewModelA> rootViewModels = new List<ViewModelA>();
         public ViewModelAConductor()
         {
              ViewModelA a1 = container.Resolvce<ViewModelA>(); 
              rootViewModels.Add(a1);

              ViewModelA a2 = container.Resolvce<ViewModelA>(); 
              rootViewModels.Add(a2); 
         }
     }

     public class ViewModelA
     {
          ViewModelB viewModelB;
          IService service;

          public ViewModelA(IService service,ViewModelB viewModelB) 
          {
              this.service = service;
              this.viewModelB = viewModelB;
          }               
     }

     public class ViewModelB
     {
          ViewModelC viewModelC;
          IService service;

          public ViewModelA(IService service,ViewModelC viewModelC) 
          {
              this.service = service;
              this.viewModelC = viewModelC;
          }               
     }  

     public class ViewModelC
     {
          IService service;

          public ViewModelA(IService service) 
          {
              this.service = service;                 
          }               
     }  

All ViewModels injected under the Graph of a1 have the same instance of IService.

All ViewModels injected under the Graph of a2 have the same instance of IService.

Avogadro answered 3/8, 2014 at 18:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.