Mapper支持两者:“从xml映射”和“unflattening”

时间:2015-08-06 22:41:47

标签: c# xml mapping automapper valueinjecter

我知道两个工具:Value injector和Automapper。他们每个人目前都支持我真正需要的功能之一。 :)背景:我正在使用存储过程从DB加载数据。关系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; }
    }

我有存储过程,它将在以下模式中返回此结构:

    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)和本文:http://www.codeproject.com/Articles/706992/Using-AutoMapper-with-Complex-XML-Data

  2. Unflattening。看起来,AutoMapper不支持基于约定的unflattening。真的吗 ?我看到,有可能将一些字符串(例如“City”)声明为Unflattened对象并且一切都应该有效 - 但值注入器将此作为基于对话的标准提供: objectToIll.InjectFrom&lt; UnflatLoopInjection&gt;(对象) - 没有必要声明哪些属性[特定名称]会被声明为什么?这也适用于automapper吗?

  3. 如果没有,那么也许我应该专注于价值注入器。如果是这样 - 1)点的问题仍然有效(希望它像在automapper中一样容易解决)

    佩妮为你的想法!

    @@更新

    我已经改变了Dto定义,将界面添加到City(因为这是我的情况)

2 个答案:

答案 0 :(得分:1)

我会将问题发布到我的问题 - 也许它会帮助某人。我已经从Automapper搬到了Valueinjecter。使用自定义AddMap()和使用XmlSerializer(这里没有魔法)

完成Xml绑定

但事实证明,“Autor”类上的接口会出现问题(和deflattening)你不能简单地创建接口实例,这就是问题所在。我已经修改了稍微有点值的Tunnelier类来处理这种情况。

首先,我试图用某种方式覆盖这个约定(如果你找到属性ISomething,剪切“我”添加“Dto”但它不是优雅的解决方案。所以我最终得到自定义:Inteface +类映射(所以我我指出:如果你看到ISomething接口,创建SomethingDto的实例)这是修改后的代码(也许这可以添加到valueinjecter实现?)主要的“Mapper”类不是部分所以我为那些定义了新类映射:

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类:

    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;
            });

答案 1 :(得分:0)

新版本已发布3.1

您现在可以为UnflatLoopInjection

指定激活器参数

查看此unit test