I am building a simple type mapper similar to AutoMapper but with a more dynamic behaviour. The caller can decide to filter RecordStatus == RecordStatus.Deleted
records when mapping from entity framework models.
Abstract mappers:
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));
}
}
Concrete Mappers:
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 model:
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; }
}
I got a compile error:
Argument 1: cannot convert from 'System.Collections.Generic.IEnumerable<Lookup>' to 'System.Collections.Generic.IEnumerable<TRecordStatusFilterable>'
But my Lookup
class does fulfill the generic type parameter constraint as it implements IRecordStatusFilterable
. Can anyone shed some light on this?
Actually a lot of the code is irrelevant to the actual problem. Here is a simpler version that, hopefully, illustrates it better:
class MyList<T>
where T : class, IConvertible
{
private List<T> list = new List<T>();
public void Add(string s)
{
list.Add(s); // error
}
}
Yes T
is constrained, and string
fits the constraints, but that doesn't mean you can go and add string
to a List of arbitrary T's. That wouldn't type safe.
If I defined
class Bar : IConvertible { /* left out IConvertible impl */ }
and made a var bars = new MyList<Bar>()
it is obvious that adding a string to bars is a problem for that code in the generic class.
You've just got a more complex version of this and I'm not 100% sure what exactly you are trying to express. Perhaps the class FooMapper
shouldn't be generic at all and should just take an instance of IMapper<IEnumerable<Lookup>, string>
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.