使用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);
// 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();