映射器同时支持:"mapping from xml" 和 "unflattening"

Mapper supporting both : "mapping from xml" and "unflattening"

r我知道两个工具:Value injector 和 Automapper。他们每个人目前都支持我真正需要的功能之一。 :) 背景:我正在使用存储过程从数据库加载数据。关系 1 到许多被处理为 XML 属性。让我们考虑以下示例

    public class AutorDto
    {
        public string Name { get; set; }
        public List<Post> Posts { get; set; }
        public ICity City { get; set; }
    }
    public interface ICity
    {
        string Name { get; set; }
        string Code { get; set; }
    }
    public class CityDto
    {
        public string Name { get; set; }
        public string Code { get; set; }
    }

    public class PostDto
    {
        public DateTime Date { get; set; }
        public string Content { get; set; }
    }

我有存储过程,它将 return 我在以下模式中使用此结构:

    public class Autor_From_Stored_Procedure
    {
        public string Name { get; set; }
        public string Posts { get; set; }
        public string CityName { get; set; }
        public string CityCode { get; set; }
    }

为了将 Autor_From_Stored_Procedure 映射到 AutorDto,我们必须处理两个主题:

  1. 从XML反序列化(我已经设法使用automapper处理了这个问题。我用过这个主题:Automapper to create object from XML) and this article : http://www.codeproject.com/Articles/706992/Using-AutoMapper-with-Complex-XML-Data

  2. 不平整。 AutoMapper 似乎不支持基于约定的展开。真的吗 ?我看到,可以将一些字符串(例如 "City")声明为 Unflattened 对象,并且一切都应该有效 - 但值注入器将其作为基于对流的标准提供: objectToIll.InjectFrom < UnflatLoopInjection>(object) - 无需声明哪些属性 [具体名称] 会被压缩)这也可以用 automapper 实现吗?

如果不是,那么也许我应该关注价值注入器。如果是这样 - 第 1) 点的问题仍然有效(希望它像在自动映射器中一样容易解决)

对你的想法一分钱!

@@更新

我已经更改了 Dto 定义,将界面添加到 City(我就是这种情况)

我会 post 回答我的问题 - 也许它会对某人有所帮助。我已经从 Automapper 转移到 Valueinjecter。 Xml 绑定是使用自定义 AddMap() 并使用 XmlSerializer(这里没有魔法)

但事实证明,"Autor" class 上的接口会出现问题(并且变平)您不能简单地创建接口实例,这就是问题所在。我稍微修改了 valueinjecter Tunnelier class 来处理这种情况。

首先,我尝试使用某种约定来解决这个问题(如果您发现 属性 ISomething,请剪切 "I" 添加 "Dto" 但这不是优雅的解决方案。所以我结束了使用自定义:Inteface+Class 映射(所以我要指出:如果您看到 ISomething 接口,请创建 SomethingDto 的实例)这是修改后的代码(也许这可以添加到 valueinjecter 实现中?)主要 "Mapper" class 不是部分的,所以我为这些映射定义了新的 class:

namespace Omu.ValueInjecter
{
    public partial class MapperActivations
    {
        public static ConcurrentDictionary<Type, Type> InterfaceActivations = new ConcurrentDictionary<Type, Type>();
        public static void AddInterfaceActivation<Interface, Class>()
        {
            InterfaceActivations.AddOrUpdate(typeof(Interface), typeof(Class), (key, oldValue) => typeof(Class));
        }
    }
}

这里是修改后的valueInjected classes:

    public static class TunnelierCustom
    {
        public static PropertyWithComponent Digg(IList<string> trail, object o)
        {
            var type = o.GetType();
            if (trail.Count == 1)
            {
                return new PropertyWithComponent { Component = o, Property = type.GetProperty(trail[0]) };
            }

            var prop = type.GetProperty(trail[0]);

            var val = prop.GetValue(o);

            if (val == null)
            {
                if (prop.PropertyType.IsInterface)
                {
                    if (MapperActivations.InterfaceActivations.ContainsKey(prop.PropertyType))
                    {
                        val = Activator.CreateInstance(MapperActivations.InterfaceActivations[prop.PropertyType]);
                    }
                    else
                    {
                        throw new Exception("Unable to create instance of: " + prop.PropertyType.Name + ". Are you missing InterfaceActivations bidning? Please add it using MapperActivations.AddInterfaceActivation<Interface, Class>() statement");
                    }
                }
                else
                {
                    val = Activator.CreateInstance(prop.PropertyType);
                }
                prop.SetValue(o, val);
            }

            trail.RemoveAt(0);
            return Digg(trail, val);
        }

        public static PropertyWithComponent GetValue(IList<string> trail, object o, int level = 0)
        {
            var type = o.GetType();

            if (trail.Count == 1)
            {
                return new PropertyWithComponent { Component = o, Property = type.GetProperty(trail[0]), Level = level };
            }

            var prop = type.GetProperty(trail[0]);
            var val = prop.GetValue(o);
            if (val == null) return null;
            trail.RemoveAt(0);
            return GetValue(trail, val, level + 1);
        }
    }

    /// <summary>
    /// performs flattening and unflattening
    /// first version of this class was made by Vadim Plamadeala ☺
    /// </summary>
    public static class UberFlatterCustom
    {
        public static IEnumerable<PropertyWithComponent> Unflat(string flatPropertyName, object target, Func<string, PropertyInfo, bool> match, StringComparison comparison)
        {
            var trails = TrailFinder.GetTrails(flatPropertyName, target.GetType().GetProps(), match, comparison, false).Where(o => o != null);

            return trails.Select(trail => TunnelierCustom.Digg(trail, target));
        }

        public static IEnumerable<PropertyWithComponent> Unflat(string flatPropertyName, object target, Func<string, PropertyInfo, bool> match)
        {
            return Unflat(flatPropertyName, target, match, StringComparison.Ordinal);
        }

        public static IEnumerable<PropertyWithComponent> Unflat(string flatPropertyName, object target)
        {
            return Unflat(flatPropertyName, target, (upn, pi) => upn == pi.Name);
        }

        public static IEnumerable<PropertyWithComponent> Flat(string flatPropertyName, object source, Func<string, PropertyInfo, bool> match)
        {
            return Flat(flatPropertyName, source, match, StringComparison.Ordinal);
        }

        public static IEnumerable<PropertyWithComponent> Flat(string flatPropertyName, object source, Func<string, PropertyInfo, bool> match, StringComparison comparison)
        {
            var trails = TrailFinder.GetTrails(flatPropertyName, source.GetType().GetProps(), match, comparison).Where(o => o != null);

            return trails.Select(trail => TunnelierCustom.GetValue(trail, source));
        }

        public static IEnumerable<PropertyWithComponent> Flat(string flatPropertyName, object source)
        {
            return Flat(flatPropertyName, source, (up, pi) => up == pi.Name);
        }
    }

    public class UnflatLoopCustomInjection : ValueInjection
    {
        protected override void Inject(object source, object target)
        {
            var sourceProps = source.GetType().GetProps();
            foreach (var sp in sourceProps)
            {
                Execute(sp, source, target);
            }
        }

        protected virtual bool Match(string upn, PropertyInfo prop, PropertyInfo sourceProp)
        {
            return prop.PropertyType == sourceProp.PropertyType && upn == prop.Name;
        }

        protected virtual void SetValue(object source, object target, PropertyInfo sp, PropertyInfo tp)
        {
            tp.SetValue(target, sp.GetValue(source));
        }

        protected virtual void Execute(PropertyInfo sp, object source, object target)
        {
            if (sp.CanRead)
            {
                var endpoints = UberFlatterCustom.Unflat(sp.Name, target, (upn, prop) => Match(upn, prop, sp)).ToArray();

                foreach (var endpoint in endpoints)
                {
                    SetValue(source, endpoint.Component, sp, endpoint.Property);
                }
            }
        }
    }

这是示例使用:

       MapperActivations.AddInterfaceActivation<ICity, City>();
       Mapper.AddMap<Auto_From_Stored_Procedure, AutorDto>(src =>
            {
                var res = new User();
                res.InjectFrom<UnflatLoopCustomInjection>(src);
                res.Posts = Mapper.Map<XElement, List<Posts>>(src.PostsXml , "PostDto"); //this is mapping using XmlSerializer, PostsXml is XElement.Parse(Posts) in Autor_From_Stored_Procedure.
                return res;
            });

新版本3.1已发布

您现在可以为 UnflatLoopInjection 指定激活器参数

看看这个unit test