C# AutoMapper:通过构造函数注入映射到目标类型(Prism、Unity、EntityFramework)

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(6.3.0)、AutoMapper(9.0.0)和AutoMapper.EF6(2.0.0)的WPF Prism.Unity(7.2.0.1367)应用程序时遇到了一个小问题,我自己似乎无法解决:

我有一个
傻瓜视图模型
,它从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;
    }
}