Можно ли упростить родовой класс? - PullRequest
1 голос
/ 23 октября 2011

Коллеги, В нашем проекте мы используем AutoMapper для отображения моделей.

У нас есть модель:

public class Location
{
   public string Address { get; set; }
}

public class Person
{
   public string Name { get; set;}
   public Collection<Location> Locations { get; set; }
}

также у нас есть вид модели:

public class PersonView
{
   public string Name { get; set; }
   public string Location { get; set;}
}

Для сопоставления модели с моделью представления мы можем определить что-то вроде следующего:

Mapper.CreateMap<Person, PersonView>(d=>d.Name, opt=>opt.FromMap(s=>s.Name);
Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>s.Locations.First().Address);

НО: если Locations не содержит элементов или имеет значение null, мы получим исключение.

С другой стороны, мы можем определить функцию для получения значения:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>
{
    var item = s.Locations.FirstOrDefault();
    if(item == null)
    {
       return string.Empty;
    }

    return item.Address;
});

Это выражение трудно читать. И я пытаюсь создать IValueResolver для упрощения отображения.

public class CollectionItemResolver<TSource, TSourceItem, TResult>
    where TSource : class
    where TSourceItem : class
{
    private readonly Func<TSource, IEnumerable<TSourceItem>> _sourceSelector;
    private readonly Func<TSourceItem, TResult> _selector;
    private readonly TResult _defaultValue;

    public CollectionItemResolver(Func<TSource, IEnumerable<TSourceItem>> source, Func<TSourceItem, TResult> selector)
        : this(source, selector, default(TResult))
    {
    }

    public CollectionItemResolver(Func<TSource, IEnumerable<TSourceItem>> source, Func<TSourceItem, TResult> selector, TResult defaultValue)
    {
        _sourceSelector = source;
        _selector = selector;
        _defaultValue = defaultValue;
    }

    public TResult Resolve(TSource source)
    {
        var items = _sourceSelector(source);

        if (items == null)
        {
            return _defaultValue;
        }

        var item = items.FirstOrDefault();
        if (item == null)
        {
            return _defaultValue;
        }

        var value = _selector(item);
        return value;
    }
}

А затем используйте что-то вроде этого:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.ResolveUsing(
    new CollectionItemResolver<Person, Location, string>(p=>p.Locations, i=>i.Address)));

Можно ли упростить универсальный распознаватель? Например, не определить тип вложенного элемента?

new CollectionItemResolver<Person, string>(p=>p.Locations, i=>i.Address)));

Спасибо

1 Ответ

1 голос
/ 24 октября 2011

Как насчет этого:

Mapper.CreateMap<Person, PersonView>(d=>d.Address, opt=>opt.FromMap(s=>s.Locations.Select(loc=>loc.Address).FirstOrDefault());

Купи путь, Automapper знает, как конвертировать Null в string.Empty

PS, надеюсь, у вас есть коллекция Locations всегда не нулевая. Но если нет, то я предлагаю использовать это расширение :

public static IEnumerable<TSource> NullToEmpty<TSource>(
    this IEnumerable<TSource> source)
{
    if (source == null)
        return Enumerable.Empty<TSource>();

    return source;
}

Тогда результат будет примерно таким: OPT => opt.FromMap (s => s.Locations.NullToEmpty () Выбрать (LOC => loc.Address) .FirstOrDefault ().);

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...