依赖于另一个配置文件的自动映射器配置文件

时间:2018-06-22 03:43:23

标签: c# automapper

我面临以下问题:

  1. How to map property of type Object with different datatypes to a corresponding classtype?
  2. AutoMapper Object Collections not mapping

这里是一个例子:

namespace Request
{
    public class Source
    {
        [System.Xml.Serialization.XmlElementAttribute("Class1", typeof(Class1))]
        [System.Xml.Serialization.XmlElementAttribute("Class2", typeof(Class2))]
        [System.Xml.Serialization.XmlElementAttribute("Class3", typeof(Class3))]
        public object Item { get; set; }
    }

    public class Class1 { ... }
    public class Class2 { ... }
    public class Class3 { ... }
}

namespace Response
{
    public class Destination
    {
        [System.Xml.Serialization.XmlElementAttribute("Class1", typeof(Class1))]
        [System.Xml.Serialization.XmlElementAttribute("Class2", typeof(Class2))]
        [System.Xml.Serialization.XmlElementAttribute("Class3", typeof(Class3))]
        public object Item { get; set; }
    }

    public class Class1 { ... }
    public class Class2 { ... }
    public class Class3 { ... }
}

AutoMapper不会考虑XmlElementAttribute来映射此类属性。我能够通过反射解决它:

public object Map(object source, Type destinationParentType, string destinationPropertyName)
{
    var prop = destinationParentType.GetProperties(BindingFlags.Public | BindingFlags.Instance).SingleOrDefault(p =>
        string.Compare(p.Name, destinationPropertyName, StringComparison.Ordinal) == 0);

    if (prop == null || prop.CustomAttributes == null) return null;

    var attribute = Attribute.GetCustomAttributes(prop, typeof(XmlElementAttribute))
        .Cast<XmlElementAttribute>().SingleOrDefault(a =>
            string.Compare(a.Type.Name, source.GetType().Name, StringComparison.Ordinal) == 0);

    return attribute == null ? null : AutoMapper.Mapper.Map(source, source.GetType(), attribute.Type);
}

通过这样称呼它:

CreateMap<SourceType, DestinationType>()
    .ForMember(d => d.Item, o => o.ResolveUsing(s => Map(s.Item, typeof(DestinationType), "Item")));

基本上,我将确定source的实际类型并在destination上获得匹配的类型,然后继续进行简单映射。

我现在的问题是我们需要使用DependencyInjection进行此操作,因此我创建了以下配置文件:

主要:

public class MainProfile : AutoMapper.Profile
{
    [Dependency]
    internal AttributeMapper AttributeMapper { get; set; } <--This is not resolved

    public MainProfile()
    {
        CreateMap ...
        CreateMap<SourceType, DestinationType>()
            .ForMember(d => d.Item, o => o.ResolveUsing(s => AttributeMapper.Map(s.Item, typeof(DestinationType), "Item")));
                                                             ^-- this instance is null
    }
}

然后使用XmlElementAttributes映射属性的映射器:

public class AttributeProfile : AutoMapper.Profile
{
    public AttributeProfile()
    {
        CreateMap<Source.Class1, Destination.Class1>();
        CreateMap<Source.Class2, Destination.Class2>();
        CreateMap<Source.Class3, Destination.Class3>();
    }
}

我这样注册他们:

var mainConfig = new MapperConfiguration(c =>
{
    c.AddProfile<MainProfile>();
    //Other Profiles
});

var helperConfig = new MapperConfiguration(c => 
{ 
    c.AddProfile<AttributeProfile>(); 
});

Container.RegisterType<AttributeMapper>();
Container.RegisterInstance(mainConfig.CreateMapper(), new ContainerControlledLifetimeManager());
Container.RegisterInstance("AttributeMapper", helperConfig.CreateMapper(),
    new ContainerControlledLifetimeManager());

然后我的AttributeMapper类是这样的:

public class AttributeMapper
{
    private readonly IMapper _mapper;

    public AttributeMapper([Dependency("AttributeMapper")] IMapper mapper)
    {
        _mapper = mapper;
    }

    public object Map(object source, Type destinationParentType, string destinationPropertyName)
    {
        //Same method from the one above, but I am using _mapper instead of AutoMapper.Mapper.Map    
        return attribute == null ? null : _mapper.Map(source, source.GetType(), attribute.Type);
    }
}

但是我在Object Refence not set to an instance of an object类的AttributeMapper属性中得到了MainProfile

Dependency的个人资料是AutoMapper的问题,这就是为什么我无法进行此工作的原因?还有其他方法可以使用XmlElementAttributes来修复此AutoMapper映射吗?

我愿意寻求其他可以帮助您的方式。

1 个答案:

答案 0 :(得分:0)

感谢Lucian Bargaoanu分享了他的想法。如果可以的话,我会将您的评论标记为答案。

我创建了一个XmlElementAttributeResolver(可以使用更通用的名称),如下所示:

public class XmlElementAttributeResolver : IMemberValueResolver<object, object, object, object>
{
    public object Resolve(object source, object destination, object sourceMember, object destMember, ResolutionContext context)
    {
        var sourceMemberType = sourceMember.GetType();
        var destinationNamespace = destination.GetType().Namespace;
        var destinationMemberType = destination.GetType().Assembly.GetTypes()
            .FirstOrDefault(t => t.Name == sourceMemberType.Name && t.Namespace == destinationNamespace);

        return destinationMemberType == null
            ? null
            : context.Mapper.Map(sourceMember, sourceMemberType, destinationMemberType);
    }
}

在我的MainProfile中使用解析器可以早先删除对我的AttributeMapper的依赖关系:

public class MainProfile : AutoMapper.Profile
{
    //[Dependency]
    //internal AttributeMapper AttributeMapper { get; set; } <--This is not resolved from UnityContainer

    public MainProfile()
    {
        CreateMap ...
        CreateMap<SourceType, DestinationType>()
            .ForMember(d => d.Item, o => o.ResolveUsing<XmlElementAttributeResolver, object>(s => s.Item));
    }
}

并且由于解析器的RuntimeMapper参数中有ResolutionContext,因此我不需要将映射器注入解析器。因此,我的mapper容器变得更简单了:

var config = new MapperConfiguration(c =>
{
    c.AddProfile<MainProfile>();
    //Other Profiles
});

Container.RegisterInstance(config.CreateMapper(), new ContainerControlledLifetimeManager());
相关问题