質問

I was just finishing my new app, but I got stuck. My app is divided into 2 parts - remote and local. What I need is to register a class for interface based on some async deserialized application settings. Here is the 'idea code'

public class ViewModelLocator
{
    static ViewModelLocator()
    {
        ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

        // It can't work :(((
        if(await SettingsManager.LoadSettings().EnableRemote) // <<<<<<
        {
            SimpleIoc.Default.Register<IMyService, MyRemoteService>();
        }
        else
        {
            SimpleIoc.Default.Register<IMyService, MyLocalService>();
        }

        SimpleIoc.Default.Register<MainViewModel>();
        SimpleIoc.Default.Register<SomeOtherViewModel>();
    }

How could I solve it? The settings are serialized in an XML file in Isolated Storage of my WinRT app. Please, can you think of any suggestions?

役に立ちましたか?

解決

It sort of breaks the IoC pattern to register bindings asynchronously. You'll need to incorporate the async settings into a wrapper class. This wrapper class should be responsible for:

  1. loading the settings
  2. providing the actual implementation of IMyService

If IMyService has synchronous methods, then you'll probably have to change it, either to provide async versions of all the methods, or to provide a Ready property + event, so that consumers will know when it is safe to access. But if it's a service, its methods are probably already async, right?

As an illustration, let's call the wrapper class MyServiceWrapper. It will load the settings when it is first constructed, and pass through all asynchronous methods in IMyService (for example, SomeMethodAsync as below).

public interface IMyService
{
    Task SomeMethodAsync(Action<bool> callback);
}

public class MyServiceWrapper : IMyService
{
    private IMyService _impl = null;
    private IMyService _remote, _local;
    private bool _loaded = false;
    private object _sync = new object();

    public Task SomeMethodAsync(Action<bool> callback)
    {
        // first make sure the settings have been loaded
        await LoadSettings();
        _impl.SomeMethodAsync(callback);
    }

    public MyServiceWrapper(MyRemoteService remoteService, MyLocalService localService)
    {
        _remote = remoteService;
        _local = localService;
        LoadSettings();
    }

    private async Task<bool> LoadSettings()
    {
        if (_loaded)
            return true;
        // lock to prevent multiple threads from loading the settings
        lock (_sync)
        {
            if (_loaded)
                return true;

            if(await SettingsManager.LoadSettings().EnableRemote)
                _impl = _remote;
            else
                _impl = _local;
        }
        return true;
    }
}

Now you can have your DI container register:

  • MyRemoteService -> MyRemoteService
  • MyLocalService -> MyLocalService
  • IMyService -> MyServiceWrapper
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top