2
votes

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.

2
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).Erik Funkenbusch
@ErikFunkenbusch , 10x i'll check into that.eran otzap
How and when is ChildViewModel initialized in the parent?Wiktor Zychla
@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 ?eran otzap
Does ViewModelA also depend on IService? Is IAService a typo?Mark Seemann

2 Answers

4
votes

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.

4
votes

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.