AutoMapper使用笔记
AutoMapper是一个.NET的对象映射工具。项目地址:https://github.com/AutoMapper/AutoMapper。帮助文档:https://github.com/AutoMapper/AutoMapper/wiki主要用途领域对象与DTO之间的转换、数据库查询结果映射至实体对象。使用笔记场景1:源类型BlogEntry,目标类型BlogPostD
AutoMapper是一个.NET的对象映射工具。
项目地址:https://github.com/AutoMapper/AutoMapper。
帮助文档:https://github.com/AutoMapper/AutoMapper/wiki
主要用途
领域对象与DTO之间的转换、数据库查询结果映射至实体对象。
使用笔记
场景1:源类型BlogEntry,目标类型BlogPostDto,指定属性进行映射(BlogEntry.ID对应于BlogPostDto.PostId)。
代码:
AutoMapper.Mapper.CreateMap<BlogEntry, BlogPostDto>() .ForMember(dto => dto.PostId, opt => opt.MapFrom(entity => entity.ID));
场景2:IDataReader映射至实体类
代码:
using (IDataReader reader = _db.ExecuteReader(command)) { if (reader.Read()) { return AutoMapper.Mapper.DynamicMap<BlogConfig>(reader); } }
场景3:列表类型之间的映射,比如:源类型List<BlogSite>,目标类型List<BlogSiteDto>
代码如下:
AutoMapper.Mapper.CreateMap<BlogSite, BlogSiteDto>();
var blogSiteDto = AutoMapper.Mapper.Map<List<BlogSite>, List<BlogSiteDto>>(blogSite);
注:必须要先通过CreateMap建立BlogSite与BlogSiteDto的映射关系。
场景4:在映射时为目标实例的属性指定值
代码如下:
var blogSiteDto = new BlogSiteDto(); AutoMapper.Mapper.CreateMap<BlogEntry, BlogPostDto>() .ForMember(dto => dto.BlogSiteDto, opt => opt.UseValue(blogSiteDto));
注:BlogSiteDto是BlogPostDto的一个属性。
补充:
AutoMapper的配置(比如AutoMapper.Mapper.CreateMap<BlogSite, BlogSiteDto>();)建议放在程序启动时,比如Global.asax的Application_Start, BootStrapper。
上篇随笔写的是我们在新版博客后台开发中用上了新式武器——Entity Framework,该武器火力猛,威力大,但使用中发现在某些场景下显得不够灵活,后来不得不引进轻量级常规武器——AutoMapper。
我们遇到的场景是一个复杂的实体类,有很多属性,数据库操作是一个跨数据库查询,查询的字段远远少于实体类的属性。
对于跨数据库查询,我们没有找到通过LINQ to Entities实现的方法,于是就用DbSet.SqlQuery调用存储过程进行查询,代码如下:
using (BlogDbContext context = new BlogDbContext()) { string sql = string .Format( " EXEC [blog_Entry_Get] @BlogID={0},@EntryID={1} " , blogId, entryId); BlogEntry entry = context.BlogEntries.SqlQuery(sql).Single(); }
虽然不能使用LINQ进行查询,但我们不想在这里抛弃这个新式武器,不能发射导弹,可以用一下机关枪嘛。于是,如上面的代码所示,用SqlQuery进行查询,用Entity Framework完成查询结果与实体类的数据映射。
结果发现,Entity Framework是依赖于实体类的属性进行映射的。如果把Entity Framework比作机关枪,那实体类的属性就是子弹,每颗子弹只能攻击唯一对应的目标,在射击过程中,只要有一颗子弹攻击的目标不存在,机枪就会卡壳(子弹决定目标?)。也就是Entity Framework会在IDataReader中查找每个实体类属性对应的值,而我们的应用场景却是“查询的字段远远少于实体类的属性”,这时,Entity Framework成为了一堆废铁(这个说法不妥,可以通过modelBuilder.Entity<BlogEntry>().Ignor忽略不需要映射的字段,但是,如果不同的查询返回的字段不同,这个方法就不管用了)。
为什么不由目标决定子弹?出现什么目标,用什么子弹,既节省子弹,又不会卡壳。也就是根据查询结果给对应的实体类属性赋值。难道这个新式武器也有设计缺陷,没有考虑到这样的应用场景?还是我们不会使用?
翻来覆去地摆弄它,还是没搞定,只能换武器...
数据库查询换成了旧式武器Enterprise Library,并引进了新的轻量级常规武器AutoMapper进行查询结果与实体类的映射(而且是开源的)。
“轻量级”果然名不虚转,简单易用,针对性强,我们用它轻松解决了问题,代码如下:
SqlCommand command = (SqlCommand)_sqldb.GetStoredProcCommand( " [blog_Entry_Get] " ); command.Parameters.AddWithValue( " @BlogID " , blogId); command.Parameters.AddWithValue( " @EntryID " , entryId); using (IDataReader reader = _sqldb.ExecuteReader(command)) { if (reader.Read()) { BlogEntry entry = AutoMapper.Mapper.DynamicMap < BlogEntry > (reader); } }
今天试用了,以后试用emitmapper
但感觉缺少一个功能entity to SqlParamentCollement
//todo:可考虑使用下面的方法替换上面相关类
public static TResult ExecuteScalar2<TResult, TEntity>(Database DB, DbCommand dbCommand, TEntity entity) where TEntity : class
{
DbParameter[] dbParameterArr = GetDbParameters<TEntity>(entity);
dbCommand.Parameters.AddRange(dbParameterArr);
object result = DB.ExecuteScalar(dbCommand);
if (result == DBNull.Value)
return default(TResult);
return (TResult)Convert.ChangeType(result, typeof(TResult));
}
public static List<T> ConvertToList2<T>(IDataReader dr) where T : class, new()
{
List<T> list = null;
var properties = GetProperties<T>();
var columnNames = GetColumnNames(dr);
while (dr.Read())
{
if (list == null)
{
list = new List<T>();
}
T o = new T();
foreach (var p in properties)
{
var columnName = p.Name;
if (columnNames.Contains(columnName))
{
if (!Convert.IsDBNull(dr[columnName]))
{
object value = dr[columnName];
if (value.GetType() != p.PropertyType)
{
value = Convert.ChangeType(value, p.PropertyType);
}
p.SetValue(o, value, null);
}
}
}
list.Add(o);
}
return list;
}
public static T ConvertToEntity2<T>(IDataReader dr) where T : class,new()
{
T o = new T();
PropertyInfo[] properties = GetProperties<T>();
var columnNames = GetColumnNames(dr);
foreach (var p in properties)
{
var columnName = p.Name;
if (columnNames.Contains(columnName))
{
if (!Convert.IsDBNull(dr[columnName]))
{
object value = dr[columnName];
if (value.GetType() != p.PropertyType)
{
value = Convert.ChangeType(value, p.PropertyType);
}
p.SetValue(o, value, null);
}
}
}
return o;
}
private static PropertyInfo[] GetProperties<T>() where T : class
{
Type t = typeof(T);
string key = string.Format("DbHelper_GetProperties_{0}", t.FullName);
MemoryCache cache = MemoryCache.Default;
if (cache[key] == null)
{
PropertyInfo[] properties = t.GetProperties();
cache.Add(key, properties, MemoryCache.InfiniteAbsoluteExpiration);
return properties;
}
return cache[key] as PropertyInfo[];
}
private static DbParameter[] GetDbParameters<T>(T entity) where T : class
{
IList<DbParameter> parameterList = new List<DbParameter>();
PropertyInfo[] properties = GetProperties<T>();
foreach (var p in properties)
{
SqlParameter sqlParameter = new SqlParameter();
sqlParameter.ParameterName = string.Format("@{0}", p.Name);
sqlParameter.Value = p.GetValue(entity, null);
sqlParameter.DbType = GetDbType(p.PropertyType);
parameterList.Add(sqlParameter);
}
return parameterList.ToArray();
}
private static List<string> GetColumnNames(IDataReader dr)
{
List<string> list = new List<string>();
for (int i = 0; i < dr.FieldCount; i++)
{
var fileName = dr.GetName(i);
list.Add(fileName);
}
return list;
}
private static DbType GetDbType(Type t)
{
string typeName = t.Name.ToLower();
DbType dbType;
switch (typeName)
{
case "guid":
dbType = DbType.Guid;
break;
case "short":
dbType = DbType.Int16;
break;
case "byte":
dbType = DbType.Byte;
break;
case "int":
dbType = DbType.Int32;
break;
case "long":
dbType = DbType.Int64;
break;
case "dateTime":
dbType = DbType.DateTime;
break;
case "double":
dbType = DbType.Double;
break;
case "decimal":
dbType = DbType.Decimal;
break;
case "float":
dbType = DbType.Decimal;
break;
case "bool":
dbType = DbType.Boolean;
break;
default:
dbType = DbType.String;
break;
}
return dbType;
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)