【问题标题】:Async load settings before static constructor静态构造函数之前的异步加载设置
【发布时间】:2014-01-28 00:05:50
【问题描述】:

我刚刚完成我的新应用程序,但我卡住了。我的应用程序分为两部分 - 远程和本地。我需要的是根据一些异步反序列化应用程序设置为接口注册一个类。这是“想法代码”

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>();
    }

我该如何解决?这些设置在我的 WinRT 应用程序的独立存储中的 XML 文件中序列化。请问大家有什么建议吗?

【问题讨论】:

  • 不使用异步加载配置?无论如何知道何时加载设置?如果有,请事先加载它们并等待它完成。

标签: c# mvvm windows-8 windows-runtime mvvm-light


【解决方案1】:

异步注册绑定有点打破了 IoC 模式。您需要将异步设置合并到包装器类中。这个包装类应该负责:

  1. 加载设置
  2. 提供IMyService的实际实现

如果IMyService 有同步方法,那么您可能必须更改它,或者提供所有方法的异步版本,或者提供Ready 属性+事件,以便消费者知道什么时候是安全访问。但如果它是一个服务,它的方法可能已经是异步的了,对吧?

作为说明,我们将包装类称为MyServiceWrapper。它会在第一次构造时加载设置,并通过IMyService中的所有异步方法(例如,SomeMethodAsync,如下所示)。

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;
    }
}

现在您可以注册 DI 容器了:

  • MyRemoteService -> MyRemoteService
  • MyLocalService -> MyLocalService
  • IMyService -> MyServiceWrapper

【讨论】:

  • 您可能想在LoadSettings 中添加try/catch 并通过SettingsLoaded 事件传播任何错误。否则,如果在LoadSettings 内抛出任何东西,则在LoadSettings 之外无法处理。
  • NP,但还是有点混乱,LoadSettings().EnableRemote 上的EnableRemote 是什么?你是说if(await SettingsManager.LoadSettings()) 吗?
  • @Noseratio 我不知道——我是直接从 OP 那里得到的。我们所知道的是,它是某种应用程序/用户设置,将被异步加载,不知何故/出于某种原因。
  • 对不起,如果我打扰了你,我只是不想把你的答案的劫持版本。我可以建议在MyServiceWrapper 构造函数中存储一个任务:this._initTask = LoadSettings(),然后在SomeMethodAsyncawait this._initTask 中简单地等待它。这将解决错误处理和线程安全问题。
  • @Noseratio 在任何情况下,请不要担心“劫持” - 如果您可以发布更好的答案,请随时借用/修改我的。无论哪种方式,我认为我们中的任何一个都不会从中获得太多的代表。 :)
猜你喜欢
  • 2014-02-25
  • 2013-08-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-19
  • 2019-06-13
  • 2011-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多