1
votes

I have a Prism application where I have three modules:

  • SharedServiceModule(I am using SharedServices to communicate between modules)
  • ModuleA
  • ModuleB

SharedServiceModule just has interface and its implementation CommonService:

public interface ICommonService
{
    string SomeStorage { get; set; }        
}

public class CommonService : ICommonService
{
    string fooStorage;
    public string FooStorage
    {
        get
        {
           return fooStorage;
        }
        set
        {
            fooStorage = value;
            OnPropertyChanged("FooStorage");                                
        }
    }
}

What I want is to create communication between modules using Shared Services. So I assign «ModuleAValue» at ViewModelA of ModuleA and then I want to read this value in ViewModelB of ModuleB. Let's see details.

I create a single instance of ICommonService in ViewModelA of ModuleA and assign a value "ModuleAValue" to FooStorage:

Method of ViewModelA:

unityContainer = new UnityContainer();
unityContainer.RegisterType<ICommonService, CommonService>(new ContainerControlledLifetimeManager());
IMyService someFoo = unityContainer.Resolve<ICommonService>();
someFoo.FooStorage = "ModuleAValue";//value is "ModuleAValue" in FooStorage

Then I want to read this data in viewModelB of ModuleB. But value of FooStorage is not 'Module A', but just empty value:

Method of ViewModelB:

IUnityContainer unityContainer=new UnityContainer//creation of UnityContainer in ModuleB
ICommonService someFoo = unityContainer.Resolve<CommonService>();
string str=someFoo.FooStorage;//value is empty in 
FooStorage, but it should be "ModuleAValue"    

My Bootstrapper is:

public class Bootstrapper:UnityBootstrapper
{
    protected override DependencyObject CreateShell()
    {
        return Container.Resolve<Shell>();
    }

    protected override void InitializeShell()
    {
        base.InitializeShell();
        App.Current.MainWindow = (Window)Shell;
        App.Current.MainWindow.Show();
    }

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
        Container.RegisterType<IShellViewModel, ShellViewModel>();
        RegisterTypeIfMissing(typeof(IMyService), typeof(MyService), true);
    }

    protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
    {
        RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
        mappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
        return mappings;
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        ModuleCatalog catalog = new ModuleCatalog();
        catalog.AddModule(typeof(ModuleAModule));
        catalog.AddModule(typeof(ModuleBModule));
        return catalog;
    }
}

What am I doing wrong? In my view, Unity always creates new instance of CommonService. What am I doing wrong while taking the same instance from Unity container?

Any help will be greatly appreciated!

2
Is the unityContainer in ModuleA is the same container in ModuleB? Can you show how you get the container to ModuleB?Yacoub Massad
You are creating a new container in ModuleB. This behavior is expected.Yacoub Massad
Because he is resolving CommonService not ICommonService. Try to resolve ICommonService and you would get an error.Yacoub Massad
@YacoubMassad you are right! To avoid error I'm resolving CommonService. How can I resolve 'ICommonService'?StepUp
You need to use the same container instance. I am not really sure that you are going about this in the correct way. Can you explain what you are trying to do? In general, you shouldn't use the container from your classes.Yacoub Massad

2 Answers

2
votes

Your app's bootstrapper creates an UnityContainer for you, see UnityBootstrapper.cs:

protected virtual IUnityContainer CreateContainer()
{
    return new UnityContainer();
}

The bootstrapper should also register the container as viewmodel factory:

protected override void ConfigureContainer()
{
    base.ConfigureContainer();

    ViewModelLocationProvider.SetDefaultViewModelFactory( type => Container.Resolve( type ) );
}

In your module definition class, you can have this 'global' container injected as dependency:

public class ClientModule : IModule
{
    private readonly IUnityContainer _unityContainer;

    public ClientModule( IUnityContainer unityContainer )
    {
        _unityContainer = unityContainer;
    }
}

In your module's initialization, you register types or instances with this container:

public void Initialize()
{
    // Register services
    _unityContainer.RegisterType<IGameClock, LocalGameClock>( new ContainerControlledLifetimeManager() );
    _unityContainer.RegisterType<IWorldStateService, LocalWorldStateService>( new ContainerControlledLifetimeManager() );
}

In your views (in the xaml), you can now use ViewModelLocator.AutoWireViewModel="True" to automatically create viewmodels for your views. The ViewModelLocationProvider will use the 'global' container to resolve the viewmodels (as defined above), so all viewmodels will receive the same instance of our, say, IGameClock.

Ancilliary piece of advice: you don't want to call Resolve directly yourself. Using it this way defeats the whole purpose of using unity in the first place. It's better to have your dependencies injected as constructor parameters, and to only use the container at the highest level, i.e. in module initialization. And you should never need to create more than one container, lest you know exactly what you're doing.

1
votes

You shouldn't create the new container instance. Usually there should be only one container instance in your application. If you are using prism your view model should be created from a container also (if prism is responsible for view model creation than it is created from container ). In such case just create the constructor in your view model with parameter of type ICommonService like this:

public ViewModelA(ICommonService service) { ... }

then during creation of the ViewModel the same instance of the service will be injected to that ViewModel.

And usually common services are registered in the Shell during application startup. But if you want you can also register a service in a module, just use the same unity container that was created during application start. In the viewmodel use constructor with parameter of type IUnityContainer.