نگاشت خودکار اشیاء توسط AutoMapper و Reflection - ایده شماره 1
نویسنده: محمد جواد ابراهیمی
تاریخ: ۱۳۹۷/۱۰/۲۹ ۸:۵۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public class PostDto : BaseDto<PostDto, Post, long>
{
public string Title { get; set; }
public string Text { get; set; }
public int CategoryId { get; set; }
public string CategoryName { get; set; } //=> Category.Name
} public abstract class BaseDto<TDto, TEntity, TKey>
where TDto : class, new()
where TEntity : BaseEntity<TKey>, new()
{
[Display(Name = "ردیف")]
public TKey Id { get; set; }
public TEntity ToEntity()
{
return Mapper.Map<TEntity>(CastToDerivedClass(this));
}
public TEntity ToEntity(TEntity entity)
{
return Mapper.Map(CastToDerivedClass(this), entity);
}
public static TDto FromEntity(TEntity model)
{
return Mapper.Map<TDto>(model);
}
protected TDto CastToDerivedClass(BaseDto<TDto, TEntity, TKey> baseInstance)
{
return Mapper.Map<TDto>(baseInstance);
}
} public abstract class BaseEntity<TKey>
{
public TKey Id { get; set; }
}
public class Post : BaseEntity<long>
{
public string Title { get; set; }
public string Text { get; set; }
public int CatgeoryId { get; set; }
public Category Category { get; set; }
} var postDto = new PostDto(); var post = postDto.ToEntity();
var post = // finded by id var updatePost = postDto.ToEntity(post);
var postDto = PostDto.FromEntity(post);
public static class AutoMapperConfiguration
{
public static void InitializeAutoMapper()
{
Mapper.Initialize(configuration =>
{
configuration.ConfigureAutoMapperForDto();
});
//Compile mapping after configuration to boost map speed
Mapper.Configuration.CompileMappings();
}
public static void ConfigureAutoMapperForDto(this IMapperConfigurationExpression config)
{
config.ConfigureAutoMapperForDto(Assembly.GetEntryAssembly());
}
public static void ConfigureAutoMapperForDto(this IMapperConfigurationExpression config, params Assembly[] assemblies)
{
var dtoTypes = GetDtoTypes(assemblies);
var mappingTypes = dtoTypes
.Select(type =>
{
var arguments = type.BaseType.GetGenericArguments();
return new
{
DtoType = arguments[0],
EntityType = arguments[1]
};
}).ToList();
foreach (var mappingType in mappingTypes)
config.CreateMappingAndIgnoreUnmappedProperties(mappingType.EntityType, mappingType.DtoType);
}
public static void CreateMappingAndIgnoreUnmappedProperties(this IMapperConfigurationExpression config, Type entityType, Type dtoType)
{
var mappingExpression = config.CreateMap(entityType, dtoType).ReverseMap();
//Ignore mapping to any property of source (like Post.Categroy) that dose not contains in destination (like PostDto)
//To prevent from wrong mapping. for example in mapping of "PostDto -> Post", automapper create a new instance for Category (with null catgeoryName) because we have CategoryName property that has null value
foreach (var property in entityType.GetProperties())
{
if (dtoType.GetProperty(property.Name) == null)
mappingExpression.ForMember(property.Name, opt => opt.Ignore());
}
}
public static IEnumerable<Type> GetDtoTypes(params Assembly[] assemblies)
{
var allTypes = assemblies.SelectMany(a => a.ExportedTypes);
var dtoTypes = allTypes.Where(type =>
type.IsClass && !type.IsAbstract && type.BaseType != null && type.BaseType.IsGenericType &&
(type.BaseType.GetGenericTypeDefinition() == typeof(BaseDto<,>) ||
type.BaseType.GetGenericTypeDefinition() == typeof(BaseDto<,,>)));
return dtoTypes;
}
} public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
AutoMapperConfiguration.InitializeAutoMapper();
} public static IEnumerable<Type> GetDtoTypes(params Assembly[] assemblies)
{
var allTypes = assemblies.SelectMany(a => a.ExportedTypes);
var dtoTypes = allTypes.Where(type =>
type.IsClass && !type.IsAbstract && type.BaseType != null && type.BaseType.IsGenericType &&
(type.BaseType.GetGenericTypeDefinition() == typeof(BaseDto<,>) ||
type.BaseType.GetGenericTypeDefinition() == typeof(BaseDto<,,>)));
return dtoTypes;
} public static void ConfigureAutoMapperForDto(this IMapperConfigurationExpression config, params Assembly[] assemblies)
{
var dtoTypes = GetDtoTypes(assemblies);
var mappingTypes = dtoTypes
.Select(type =>
{
var arguments = type.BaseType.GetGenericArguments();
return new
{
DtoType = arguments[0],
EntityType = arguments[1]
};
}).ToList();
foreach (var mappingType in mappingTypes)
config.CreateMappingAndIgnoreUnmappedProperties(mappingType.EntityType, mappingType.DtoType);
} public static void CreateMappingAndIgnoreUnmappedProperties(this IMapperConfigurationExpression config, Type entityType, Type dtoType)
{
var mappingExpression = config.CreateMap(entityType, dtoType).ReverseMap();
//Ignore mapping to any property of entity (like Post.Categroy) that dose not contains in dto (like PostDto.CategoryName)
//To prevent from wrong mapping. for example in mapping of "PostDto -> Post", automapper create a new instance for Category (with null catgeoryName) because we have CategoryName property that has null value
foreach (var property in entityType.GetProperties())
{
if (dtoType.GetProperty(property.Name) == null)
mappingExpression.ForMember(property.Name, opt => opt.Ignore());
}
}