Caliburn.Micro Bootstrapper 'BuildUp' 方法在使用 Simple Injector 时抛出异常

Caliburn.Micro Bootstrapper 'BuildUp' method throws exception when Simple Injector is used

我对内置的 CM SimpleContainer 没有任何问题,但今天我需要去 Simple Injector

当我通过 cal:Message.Attach 调用异步方法时,Bootstrapper 的 BuildUp 方法引发异常:

An exception of type 'SimpleInjector.ActivationException' occurred in SimpleInjector.dll but was not handled in user code

Additional information: The constructor of type SequentialResult contains the parameter with name 'enumerator' and type IEnumerator<IResult> that is not registered. Please ensure IEnumerator<IResult> is registered, or change the constructor of SequentialResult.

这是我的引导程序 class:

    protected override void Configure()
    {
        _container.RegisterSingleton<IEventAggregator, EventAggregator>();
        _container.RegisterSingleton<IWindowManager, WindowManager>();

        _container.Verify();
    }

    protected override object GetInstance(Type service, string key)
    {
        var instance = _container.GetInstance(service);

        if (instance != null)
            return instance;

        throw new InvalidOperationException("Could not locate any instances.");
    }

    protected override IEnumerable<object> GetAllInstances(Type service)
    {
        IServiceProvider provider = _container;
        Type collectionType = typeof(IEnumerable<>).MakeGenericType(service);
        var services = (IEnumerable<object>)provider.GetService(collectionType);
        return services ?? Enumerable.Empty<object>();
    }

    protected override void BuildUp(object instance)
    {
        var registration = _container.GetRegistration(instance.GetType(), true);
        registration.Registration.InitializeInstance(instance);
    }

    protected override IEnumerable<Assembly> SelectAssemblies()
    {
        return new[] { Assembly.GetExecutingAssembly() };
    }  

XAML的一部分:

    <Border Grid.Row="2" Padding="10" Background="#F0F0F0" BorderBrush="#DFDFDF" BorderThickness="0,1,0,0">
        <StackPanel Orientation="Horizontal">
            <Button IsCancel="True" Content="{Resx Key=Close}" />
            <Button IsDefault="True" MinWidth="{Resx Key=CheckOrUpdateBtnWidth, DefaultValue='115'}" Margin="8,0,0,0"
                    cal:Message.Attach="CheckUpdateAsync" />
        </StackPanel>
    </Border>

部分虚拟机:

    public async Task CheckUpdateAsync()
    {
        IsUpdateDownloading = true;

        try
        {
            await Task.Run(async () =>
            {
                Cts = new CancellationTokenSource();

                var http = new HttpClient();
                HttpResponseMessage rm = await http.GetAsync(UpdateInfo.DownloadUri, HttpCompletionOption.ResponseHeadersRead, Cts.Token);

                long size = rm.Content.Headers.ContentLength.GetValueOrDefault();

                var downloader = FileDownloader.Create(UpdateInfo.DownloadUri);

                byte[] data = downloader.Download(Cts.Token);
                downloader.ValidateHash(data, CloudManager.UpdateInfo.Sha256);
            });
        }
        catch (OperationCanceledException) { }
        catch (Exception ex)
        {
            Logger.Error(ex);
            throw;
        }
        finally
        {
            IsUpdateDownloading = false;
            ProgressValue = 0;
        }
    }

我做错了什么?

//this was an example from Simple Injector.
var repositoryAssembly = typeof(SqlUserRepository).Assembly;   

var registrations =
from type in repositoryAssembly.GetExportedTypes()
where type.Namespace == "MyComp.MyProd.BL.SqlRepositories"
where type.GetInterfaces().Any()
select new { Service = type.GetInterfaces().Single(), Implementation = type };

foreach (var reg in registrations) {
    container.Register(reg.Service, reg.Implementation, Lifestyle.Transient);
}

虽然我知道带有 SelectedAssemblies 覆盖的 CM "suppose" 是所有与程序集相关的东西的全部,但我推测 GetAllInstances 中的几行没有获得 CM 所需的一切。我认为您需要扩展以包括 type.GetIntefaces().Any()。我希望这足以满足 SequentialResult 的 CTOR 需求。

我从未使用过 Simple Injector,但我说它的工作方式非常相似,并且可能对开放泛型有更好的处理方式。我认为您应该在 Configure 中做更多注册的地方缺少一些东西。我假设到那时为止,在 XAML 的 "throws" 喷油器问题的那一部分之前没有其他任何东西死亡或失败。这似乎不是 CM 问题,而是一个简单的喷油器配置。

使用 Message.Attach() 将在 C.M 内部使用。使用 C.M 中的 CoRoutines. Looking at the source。我看到这段代码:

 public static Func<IEnumerator<IResult>, IResult> CreateParentEnumerator = 
     inner => new SequentialResult(inner);

即如果未覆盖,则使用默认 SequentialResult<IResult>

弗瑟隆:

 var enumerator = CreateParentEnumerator(coroutine);
 IoC.BuildUp(enumerator);

这是异常的来源。 BuildUp 在内部调用,当您直接调用容器以查找注册时,简单注入器将抛出 ActivationException.

在大多数情况下,您根本不需要实施 BuildUp 方法。当您将 Simple Injector 用作 DI 容器时,我想不出为什么要使用此方法的任何原因。我一般不会实现这个方法。

BuildUp 是一种通常需要的方法,如果您需要注入(使用 属性 注入)到无法使用普通简单注入器管道创建的组件中。您可以在 Simple Injector 文档中阅读详细信息 here

在这种情况下,我认为在这种情况下您不需要构建 SequentialResult。您无需在此 C.M 中注入任何内容。默认 class.

所以这里的问题是您需要 class 什么 BuildUp 吗?您的应用程序设计通常应该只使用在您在 Simple Injector 中注册的应用程序本身中定义的 classes,并且可以使用 GetInstance().

直接解析

如果答案是 'no',您永远不会在外部 classes 中注入依赖项,完全删除 BuildUp() 方法。

如果您需要 BuildUp() 其他 classes,这只有在您覆盖开箱即用的默认值 PropertyInjectionBehaviour. If you don't override this, making a call to registration.InitializeInstance doesn't make sense at all, because Simple Injector doesn't know what to inject, because Simple Injector only supports Explicit Property Injection 时才有意义。您可以在覆盖默认 PropertyInjectionBehaviour.

时创建某种隐式行为

总而言之,我认为您应该完全删除 BuildUp 方法,因为当前的实现不执行任何操作。 InitializeInstance 不会注入任何东西。