2014-12-11 3 views
1

Я создаю простой тип mapper, похожий на AutoMapper, но с более динамичным поведением. Вызывающий абонент может принять решение об отфильтровывании RecordStatus == RecordStatus.Deleted записей при сопоставлении с файловыми моделями сущностей.Ограничение и дисперсия общего типа

Аннотация картостроители:

public interface IMapper<in TIn, out TOut> 
{ 
    TOut Map(TIn input); 
} 

public interface IRecordStatusFilterable 
{ 
    string RecordStatus { get; } 
} 

public abstract class RecordStatusFilterableMapperBase<TIn, TOut> : IMapper<TIn, TOut> 
{ 
    private readonly bool _filterDeletedRecords; 

    protected RecordStatusFilterableMapperBase(bool filterDeletedRecords) 
    { 
     _filterDeletedRecords = filterDeletedRecords; 
    } 

    protected bool FilterDeletedRecords 
    { 
     get { return _filterDeletedRecords; } 
    } 

    public abstract TOut Map(TIn input); 
} 

public class MultiLookupValuesMapper : RecordStatusFilterableMapperBase<IEnumerable<Lookup>, string> 
{ 
    private static readonly Func<Lookup, bool> _predicate = 
     filterable => filterable.RecordStatus == RecordStatus.Active; 

    protected MultiLookupValuesMapper(bool filterDeletedRecords) : base(filterDeletedRecords) 
    { 
    } 

    public override string Map(IEnumerable<Lookup> input) 
    { 
     var inputList = input as IList<Lookup> ?? input.ToList(); 
     if (!inputList.Any()) 
     { 
      return string.Empty; 
     } 

     if (FilterDeletedRecords) 
     { 
      inputList = (IList<Lookup>)inputList.Where(_predicate); 
     } 

     return string.Join(", ", inputList.Select(l => l.Value)); 
    } 
} 

Бетон Картостроители:

public class FooMapper<TRecordStatusFilterable> : RecordStatusFilterableMapperBase<Foo, FooViewModel> 
    where TRecordStatusFilterable : class, IRecordStatusFilterable 
{ 
    private readonly IMapper<IEnumerable<TRecordStatusFilterable>, string> _multiLookupValueMapper; 

    public FooMapper(IMapper<IEnumerable<TRecordStatusFilterable>, string> multiLookupValueMapper, 
        bool filterDeletedRecords) : base(filterDeletedRecords) 
    { 
     _multiLookupValueMapper = multiLookupValueMapper; 
    } 

    public override FooViewModel Map(Foo input) 
    { 
     return new FooViewModel 
     { 
      // Error here 
      BarLookupValues = _multiLookupValueMapper.Map(input.Lookups) 
     }; 
    } 
} 

Entity Framework модели:

public class Foo 
{ 
    public ICollection<Lookup> Lookups { get; set; } 
} 

public class Lookup : IRecordStatusFilterable 
{ 
    public string Value { get; set; } 

    public string RecordStatus { get; set; } 
} 

ViewModels:

public class FooViewModel 
{ 
    // ICollection<Lookup> => string 
    public string BarLookupValues { get; set; } 
} 

я получил ошибку компиляции:

Argument 1: cannot convert from 'System.Collections.Generic.IEnumerable<Lookup>' to 'System.Collections.Generic.IEnumerable<TRecordStatusFilterable>'

Но мой Lookup класс делает выполнить родовое ограничение параметра типа, как он реализует IRecordStatusFilterable. Может кто-нибудь пролить некоторый свет на это?

+0

'Lookup' - это класс, который удовлетворяет ограничению, но не является типом' TRecordStatusFilterable', который является держателем места для любого конкретного типа. Нет никакой гарантии, что 'Lookup' совместим с общим типом аргумента при создании экземпляра. –

+0

Могу я спросить, почему? '_multiLookupValueMapper' имеет тип' IMapper , string> 'и я вызывал' _multiLookupValueMapper.Map' с 'ICollection '. Почему это невозможно? – rexcfnghk

ответ

1

На самом деле много кода не имеет отношения к реальной проблеме. Вот более простой вариант, который, надеюсь, иллюстрирует это лучше:

class MyList<T> 
    where T : class, IConvertible 
{ 
    private List<T> list = new List<T>(); 

    public void Add(string s) 
    { 
     list.Add(s); // error 
    }   
} 

Да T ограничен, и string соответствует ограничениям, но это не значит, что вы можете пойти и добавить string в список произвольных T. Это не будет безопасным.

Если я определил

class Bar : IConvertible { /* left out IConvertible impl */ } 

и сделал var bars = new MyList<Bar>() очевидно, что добавление строки в барах является проблемой для этого кода в обобщенном классе.

У вас только более сложная версия этого, и я не уверен на 100%, что именно вы пытаетесь выразить. Возможно, класс FooMapper не должен быть общим и должен просто взять экземпляр IMapper<IEnumerable<Lookup>, string>.

+0

Извините за нерелевантный код, я думал, что это предоставит больше контекста моей проблеме. Я попытаюсь изменить свою реализацию «FooMapper» как конкретную, а не общую. Благодаря! – rexcfnghk

+0

@rexcfnghk все нормально. Я не хотел, чтобы это было пренебрежительно. Вы предоставили много подробностей. –

Смежные вопросы