Картограф, поддерживающий как: отображение из xml, так и распаковку

Я знаю два инструмента: инжектор значений и 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; }
    }

У меня есть хранимая процедура, которая вернет мне эту структуру в следующей схеме:

    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 для создания объекта из XML) и эту статью: http://www.codeproject.com/Articles/706992/Using-AutoMapper-with-Complex-XML-Data

  2. Разглаживание. Кажется, что AutoMapper не поддерживает развертку на основе соглашений. Это правда ? Я видел, что есть возможность объявить некоторую строку (например, «Город») как объект Unflattened, и все должно работать, но инжектор значений предлагает это как стандарт, основанный на конвекции: objectToIll.InjectFrom ‹ UnflatLoopInjection>(object) — без необходимости объявлять какие свойства [конкретные имена] будут сглажены) Возможно ли это также с automapper?

Если нет, то, возможно, мне следует сосредоточиться на инжекторе стоимости. Если да - проблема из 1) пункта остается в силе (надеюсь решается так же легко как и в автомаппере)

Пенни за ваши мысли!

@@Обновлять

Я изменил определения Dto, добавив интерфейс к городу (так как это мой случай)


person Piotr Filipowicz    schedule 06.08.2015    source источник
comment
Я думаю, что для первого шага все, что вам нужно, это десериализатор xml, а не картограф.   -  person Omu    schedule 07.08.2015
comment
@Omu, я пришел к тому же выводу (сейчас я использую ValueInjecter). Тем не менее, у меня есть проблема с базовым сопоставлением объектов (развертыванием). Используя приведенный выше пример: Mapper.AddMap‹Auto_From_Stored_Procedure, AutorDto›(src =› { var res = new AutorDto(); res.InjectFrom‹UnflatLoopInjection›(src); //Пользовательская привязка XML return res; }); свойство City на Autor — это интерфейс (ICity). Я получаю сообщение об ошибке: Не удается создать экземпляр интерфейса. Как с этим справиться?   -  person Piotr Filipowicz    schedule 07.08.2015
comment
UnflatLoopInjection предполагает, что все ваши разворачиваемые свойства могут быть созданы и не имеют параметров конструктора atm, поэтому вы можете не использовать Unflat, изменять типы свойств или захватывать исходный код.   -  person Omu    schedule 07.08.2015
comment
@Omu, еще раз согласен (я выбрал третий вариант). Я модифицировал Tunnelier для поиска свойств, которые являются интерфейсами, а затем для использования (соглашение об именовании классов dto (вырезать I в начале, добавить Dto в конце) - например, ICity и имя класса будет CityDto) класс, взятый из Assembly.GetAssembly( ) Это определенно работает, но это как-то обманчиво. Тем не менее спасибо за совет.   -  person Piotr Filipowicz    schedule 07.08.2015


Ответы (2)


Напишу ответ на свой вопрос - может кому поможет. Я перешел с Automapper на Valueinjecter. Привязка Xml была выполнена с использованием пользовательского AddMap() и XmlSerializer (здесь нет магии)

Но оказывается, что будет проблема с интерфейсом на классе "Автор" (и с дефлаттингом). Вы не можете просто создать экземпляр интерфейса, и в этом была проблема. Я немного изменил класс valueinjecter Tunnelier, чтобы справиться с таким случаем.

Во-первых, я попытался охватить это, используя какое-то соглашение (если вы найдете свойство ISomething, вырежьте «I», добавьте «Dto». Но это не элегантное решение. Так что я закончил с пользовательским: сопоставлением Inteface + Class (поэтому я указываю out: если вы видите интерфейс 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;
            });
person Piotr Filipowicz    schedule 07.08.2015

вышла новая версия 3.1

теперь вы можете указать параметр активатора для UnflatLoopInjection

взгляните на этот модульный тест

person Omu    schedule 16.08.2015