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

本文关键字:XAML 访问 MainViewModel Autofac 注入 服务 使用 | 更新日期: 2023-09-27 18:12:23

我正试图直接从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}">

我的定位器是在我的应用程序中创建的。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>();

可能还可以,因为我所有的"记录器"程序集都只实现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有关,而这个参数是我的数据库记录器类(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);

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

    // 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还有一个CCD_ 3扩展。

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

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