使用 Autofac 和注入服务时从 XAML 页面访问 MainViewModel

Accessing MainViewModel from XAML page when using Autofac and inject service(s)

我正在尝试直接从我的 XAML 页面定义访问我的 MainPageViewModel:

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MyApp"
        mc:Ignorable="d"
        Title="MyApp" Height="350" Width="650" ShowInTaskbar="True"
        WindowState="Maximized"
        DataContext="{Binding Source={StaticResource Locator}, Path=MainViewModel}">

我的定位器是在我的 App.XAML:

中创建的
<Application.Resources>
    <ResourceDictionary>
        <vm:ViewModelLocator x:Key="Locator" />
    </ResourceDictionary>
</Application.Resources>

而我的ViewModelLocator定义如下:

public class ViewModelLocator
{
    private static readonly IContainer Container;

    static ViewModelLocator()
    {
        // load specific logger - Hardcoded for sample sake
        string loggerAssembly = "Logger.Database.dll";

        // register logger database by type i.e. ILogger
        var builder = new ContainerBuilder();
        Assembly assembly = Assembly.LoadFrom(loggerAssembly);
        builder.RegisterAssemblyTypes(assembly).As<ILogger>();            

        // register MainViewModel and Inject ILogger
        // Is this correct? Is this how you inject a service?
        builder.RegisterType<MainViewModel>().As<ILogger>().InstancePerDependency();

        Container = builder.Build();
    }

    public ILogger Logger => Container.Resolve<ILogger>();

    // property to access MainViewModel in XAML
    public MainViewModel MainViewModel => Container.Resolve<MainViewModel>();
}

但是当我 运行 我的应用程序时,它从定位器上的 App.XAML 生成错误,并且它正在尝试访问上面定义的 MainViewModel 属性。我得到的错误是:

异常信息:

None of the constructors found with 
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on 
type 'MyCompany.Logger.Database.Logger' can be invoked with the 
available services and parameters: Cannot resolve parameter 
'System.String connectionString' of constructor 'Void .ctor(System.String)'.

异常堆栈跟踪:

at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance
(IComponentContext context, IEnumerable`1 parameters)
at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters)

而我的 MainPageViewModel 定义如下:

Public class MainPageViewModel()
{
    public MainViewModel(ILogger logger)
    {
    ....
    }
}

关于如何解决这个问题的任何想法?

更新 1:

注意我原来的定义:

builder.RegisterAssemblyTypes(assembly).As<ILogger>();

可能没问题,因为我所有的 "logger" 程序集都只实现了 ILogger 接口,但我最初使用的是 AsImplementedInterfaces()

我现在已经根据@HenkHolterman 的建议更新了我的代码:

builder.RegisterType<MainViewModel>();
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();

但我没有收到以下异常:

None of the constructors found with 'Autofac.Core.Activators.Reflection.
DefaultConstructorFinder' on type 'MyCompany.Logger.Database.Logger' can be
invoked with the available services and parameters:

Cannot resolve parameter 'System.String connectionString' of constructor 
'Void .ctor(System.String)'.

现在根据这个异常的最后一部分,它似乎与参数 connectionString 有关并且这个参数是我的数据库记录器 class (MyCompany.Logger.Database.Logger) 的一部分,所以我已经暂时删除它以测试@HenkHolterman 的建议,现在可以使用,但我仍然有问题。

我在使用数据库记录器时需要传递一个连接字符串。当我使用文件记录器等时,我需要传递一个文件名...

那么如何将参数传递给记录器构造函数。我试过:

    builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().
    WithParameter("ConnectionString", connectionString);

但它仍然调用MyCompany.Logger.Database.Logger中的参数less(即Logger)。

如果我将 connectionString 参数重新添加到 Logger 的构造函数中,它会抛出上述异常。

明天我会继续研究它,如果发现任何问题,我会更新我的答案,但如果在此期间有人有任何建议,我将非常感谢您的帮助。

更新 2

正如@HenkHolterman 强调的那样,参数区分大小写并且必须与构造函数中使用的约定相匹配。

请注意,有两种方法可以使用 Autofac 传递参数。在注册时和解决时。两者的工作方式相同。

所以上面在传递参数时产生错误的代码应该被定义为:

builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces().
WithParameter("connectionString", connectionString);
    // register MainViewModel and Inject ILogger
    // Is this correct? Is this how you inject a service?
    builder.RegisterType<MainViewModel>()
           .As<ILogger>()
           .InstancePerDependency();

不,这是不正确的。您正在将 MainViewModel 注册为 ILogger。递归且可能冲突的注册。

您应该将 MainVm 注册为它自己。我对 AutoFac 不太熟悉,但我认为它很简单:

    builder.RegisterType<MainViewModel>();

但是 iirc 还有一个 .AsSelf() 扩展。

但是builder.RegisterAssemblyTypes(assembly).As<ILogger>();看起来也不对。这会将程序集中的 every 类型注册为 ILogger。你可能想要

   builder.RegisterAssemblyTypes(assembly)
          .Where(...)                     // optional filter
          .AsImplementedInterfaces();