C# AutoMapper:通过构造函数注入映射到目标类型(Prism、Unity、EntityFramework)
我在使用EntityFramework(6.3.0)、AutoMapper(9.0.0)和AutoMapper.EF6(2.0.0)的WPF Prism.Unity(7.2.0.1367)应用程序时遇到了一个小问题,我自己似乎无法解决: 我有一个C# AutoMapper:通过构造函数注入映射到目标类型(Prism、Unity、EntityFramework),c#,dependency-injection,automapper,prism,unity-container,C#,Dependency Injection,Automapper,Prism,Unity Container,我在使用EntityFramework(6.3.0)、AutoMapper(9.0.0)和AutoMapper.EF6(2.0.0)的WPF Prism.Unity(7.2.0.1367)应用程序时遇到了一个小问题,我自己似乎无法解决: 我有一个傻瓜视图模型,它从EntityFramework加载FooEntitys的列表,并使用AutoMapper将它们映射到FooViewModels。FooEntity与BarEntity有一对多的关系,该关系变成了FooViewModel中BarViewM
傻瓜视图模型
,它从EntityFramework加载FooEntity
s的列表,并使用AutoMapper将它们映射到FooViewModel
s。FooEntity
与BarEntity
有一对多的关系,该关系变成了FooViewModel
中BarViewModel
的可观察集合。到现在为止,一直都还不错。现在我想让FooViewModel
访问DbContext,并将依赖项添加到FooViewModel
的Ctor中,这时AutoMapper开始抱怨“FooViewModel需要一个具有0个参数或仅可选参数的构造函数”
这是FooViewModel
(使用INotifyPropertyChanged,所有属性实际上都是完整属性,但我缩短了它以获得更好的可读性):
。。。但问题依然存在。然后我发现了这个帖子:
所以我创建了一个类型转换器:
public class FooConverter : ITypeConverter<Foo, FooViewModel>
{
private readonly IUnityContainer _container;
private readonly IMapper _mapper;
public FooConverter(IUnityContainer container, IMapper mapper)
{
_container = container;
_mapper = mapper;
}
public FooViewModel Convert(Foo source, FooViewModel destination, ResolutionContext context)
{
FooViewModel result = new FooViewModel(_container.Resolve<FooBarContext>());
result.FooId = source.FooId;
result.Name = source.Name;
result.Desc = source.Desc;
result.Bars = _mapper.Map<ObservableCollection<BarViewModel>>(source.Bars);
return result;
}
}
公共类FooConverter:ITypeConverter
{
专用只读IUnityContainer\u容器;
专用只读IMapper\u映射器;
公共FooConverter(IUnityContainer容器、IMapper映射器)
{
_容器=容器;
_映射器=映射器;
}
公共FooViewModel转换(Foo源、FooViewModel目标、ResolutionContext上下文)
{
FooViewModel结果=新的FooViewModel(_container.Resolve());
result.FooId=source.FooId;
result.Name=source.Name;
result.Desc=source.Desc;
result.bar=\u mapper.Map(source.bar);
返回结果;
}
}
我的应用程序又开始工作了!但是:在我看来,我好像错过了什么。在我的TypeConverter中“手动”分配所有属性只是为了在Ctor中获得一个依赖项,这感觉像是忽略了使用AutoMapper的要点。它感觉像是一个单行程序,比如
ConstructionServicesUsing(x=>IUnityContainer.Resolve(x))
应该告诉映射程序从DI容器获取所有构造函数依赖项,而不使用任何类型转换器。另外,如果我在我的FooViewModel
Ctor中添加了另一个依赖项,我将不得不更改类型转换器。如果我开始向BarViewModel
添加依赖项,我需要更改Foo
TypeConverter并创建Bar
TypeConverter。它必须比那简单!(?)老实说,我一直想知道AutoMapper的实际用途是什么。此外,如果您可以自动从一个模型转换到另一个视图模型,则表明您做错了什么,或者您的应用程序非常简单(在这种情况下,您可以将该模型用作视图模型)。为什么ObservableCollection
?只有从数据库更新集合(EF不更新)或从视图模型更新集合(只有在将更改保存到数据库时才有意义,而AutoMapper不能这样做)时,这才有意义。此外,new FooViewModel(_container.Resolve())
完全违背了使用依赖项注入容器的目的,是一种反模式(请参阅)。从ViewModel更新ObservableCollection,因为每个FooView都有一个按钮,该按钮绑定到一个可以更改ViewModel状态的DelegateCommand。是的,我知道新的FooViewModel(_container.Resolve())
是一种不好的做法,这就是我发布这个问题的原因。无论如何,这不是关于这个应用程序本身,它实际上只是我在家里创建的一个微不足道的示例应用程序。我想知道通常如何解析AutoMapper映射中的依赖项,我觉得这应该是很容易做到的。您应该将FooBarContext
注入解析程序/转换器。
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
IUnityContainer _container = containerRegistry.GetContainer();
MapperConfiguration mapperConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile<FooProfile>();
cfg.ConstructServicesUsing(x => _container.Resolve(x));
});
IMapper mapper = mapperConfig.CreateMapper();
containerRegistry.RegisterInstance(mapper);
}
public class FooConverter : ITypeConverter<Foo, FooViewModel>
{
private readonly IUnityContainer _container;
private readonly IMapper _mapper;
public FooConverter(IUnityContainer container, IMapper mapper)
{
_container = container;
_mapper = mapper;
}
public FooViewModel Convert(Foo source, FooViewModel destination, ResolutionContext context)
{
FooViewModel result = new FooViewModel(_container.Resolve<FooBarContext>());
result.FooId = source.FooId;
result.Name = source.Name;
result.Desc = source.Desc;
result.Bars = _mapper.Map<ObservableCollection<BarViewModel>>(source.Bars);
return result;
}
}