Reputation: 47
I have a Person
class which contains a property that lazy loads (custom made lazy loading) the person address data through accessing the Item
property. I would want it to be mapped to a POCO class. How could it be done?
In addition, is it possible to be mapped only if it has data (checking the HasData
property) and mapped as null
if there isn’t data?.
These are the source classes:
public class SourcePerson
{
public string Name { get; set; }
public MyLazyLoadingObject<SourceAddress> Address;
}
public class SourceAddress
{
public string City { get; set; }
public string Country { get; set; }
}
This is the custom lazy loading class (simplified):
public class MyLazyLoadingObject<T>
{
private int? _id;
private T _object;
public T Item
{
get
{
if (!_object.IsReaded)
{
_object.Read();
}
return _object;
}
}
public bool HasData
{
get
{
return _id.HasValue;
}
}
// Other non-relevant properties and methods
}
These are the destination classes:
public class DestinationPerson
{
public string Name { get; set; }
public DestinationAddress Address;
}
public class DestinationAddress
{
public string City { get; set; }
public string Country { get; set; }
}
Upvotes: 2
Views: 1296
Reputation: 205579
Couldn't find conventional way of setting up conversion from MyLazyLoadingObject<T>
to T
and then T
to some TDestination
without code repetition.
But custom IObjectMapper
implementation with some manual expression building does the job.
Here is the class that builds the mapping expression:
public class MyLazyLoadingObjectMapper : IObjectMapper
{
public bool IsMatch(TypePair context)
{
return context.SourceType.IsGenericType && context.SourceType.GetGenericTypeDefinition() == typeof(MyLazyLoadingObject<>);
}
public Expression MapExpression(TypeMapRegistry typeMapRegistry, IConfigurationProvider configurationProvider, PropertyMap propertyMap, Expression sourceExpression, Expression destExpression, Expression contextExpression)
{
var item = Expression.Property(sourceExpression, "Item");
Expression result = item;
if (item.Type != destExpression.Type)
{
var typeMap = configurationProvider.ResolveTypeMap(item.Type, destExpression.Type);
result = Expression.Invoke(typeMap.MapExpression, item, destExpression, contextExpression);
}
// source != null && source.HasData ? result : default(TDestination)
return Expression.Condition(
Expression.AndAlso(
Expression.NotEqual(sourceExpression, Expression.Constant(null)),
Expression.Property(sourceExpression, "HasData")
),
result,
Expression.Default(destExpression.Type)
);
}
}
All you need is to register it to the MapperRegistry
:
AutoMapper.Mappers.MapperRegistry.Mappers.Add(new MyLazyLoadingObjectMapper());
and of course create the regular type maps (which I guess you already did):
cfg.CreateMap<SourceAddress, DestinationAddress>();
cfg.CreateMap<SourcePerson, DestinationPerson>();
Upvotes: 2
Reputation: 47
I've achieved it this way:
cfg.CreateMap<SourcePerson, DestinationPerson>().ForMember(t => t.Address, o => o.MapFrom(s => (s.Address.HasData)? s.Address.Item : null));
Upvotes: 1