AutoMapper在.Net(asp.net MVC)项目下的应用以及IDataReader转List说明
AutoMapper在.Net(asp.net MVC)项目下如何配置和应用的呢?我们首先说配置初始化
AutoMapper在.Net(asp.net MVC)项目下的应用
首先在应用启动时要注册映射的文件,直接在Global.asax中的Application_Start类中注册即可 这里我们将注册的具体方法写在了 AutoMapperUtil.RegisterAutoMapper工具类中:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//注册AutoMapper配置
AutoMapperUtil.LoadConfig();
}
工具类如下:
/// <summary>
/// 自动映射工具
/// </summary>
public static class AutoMapperUtil
{
/// <summary>
/// 从配置文件加载
/// </summary>
/// <param name="sectionName">节点名称</param>
public static void LoadConfig(string sectionName = "autoMapper")
{
var config = ConfigurationManager.GetSection(sectionName) as NameValueCollection;
if (config != null)
{
var assemlies = config.Keys
.Cast<string>()
.Select(e => config[e])
.Where(e => !string.IsNullOrEmpty(e))
.ToArray();
Mapper.Initialize(cfg => {
//支持datareader to list
cfg.AddDataReaderMapping();
cfg.AddProfiles(assemlies);
});
}
Mapper.AssertConfigurationIsValid();
}
}
这里要说明**册的方式也有多种,AddProfiles有多个重载
最简单的方式就是通过
Mapper.Initialize(cfg =>
{
cfg.AddProfile<ModelMapperProfile>();
});
public class ModelMapperProfile : Profile
{
CreateMap<Book, BookView>()
}
本人使用的是 void AddProfiles(params Assembly[] assembliesToScan); 这个方式注册程序集下的Profiles,其原理是注册程序集后,程序会自动化扫面程序集下所有继承Profile的类里的映射信息进行注册,代码如下:
public static void LoadConfig(string sectionName = "autoMapper")
{
var config = ConfigurationManager.GetSection(sectionName) as NameValueCollection;
if (config != null)
{
var assemlies = config.Keys
.Cast<string>()
.Select(e => config[e])
.Where(e => !string.IsNullOrEmpty(e))
.ToArray();
Mapper.Initialize(cfg => {
cfg.AddProfiles(assemlies);
});
}
Mapper.AssertConfigurationIsValid();
}
配置文件:
<configSections>
<section name="autoMapper" type="System.Configuration.NameValueSectionHandler, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
<autoMapper>
<!--键为程序集简称,可随意命名,不重复即可,值为程序集名称-->
<add key="ProxyService" value="CSP.RE.Repository" />
</autoMapper>
这样我的映射类就可以创建在CSP.RE.Repository下任何位置。
到这里注册就完成了,在进行model关系映射时会使用一些通用的帮助类
///协变性:派生程度较大类型分配(赋值)给派生程度较小类型
/// <summary>
/// 映射到目标类型数据
/// </summary>
/// <typeparam name="TMapperType"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static TDestination MapTo<TDestination>(this object value) where TDestination : class
{
if (value == null)
return default(TDestination);
return Mapper.Map<TDestination>(value);
}
/// <summary>
/// 映射到目标类型数据集合
/// </summary>
/// <typeparam name="TDestination">目标类型</typeparam>
/// <param name="values">源类型集合</param>
/// <returns></returns>
public static IEnumerable<TDestination> MapTo<TDestination>(this IEnumerable values) where TDestination : class
{
if (values == null) return new List<TDestination>();
return Mapper.Map<IEnumerable<TDestination>>(values);
}
/// <summary>
/// 将 DataTable 转为实体对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static IEnumerable<TDestination> MapTo<TDestination>(this DataTable dt) where TDestination : class
{
if (dt == null || dt.Rows.Count == 0)
return new List<TDestination>();
return Mapper.Map<IEnumerable<TDestination>>(dt.CreateDataReader());
}
/// <summary>
/// 将 DataSet 转为实体对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="ds"></param>
/// <returns></returns>
public static IEnumerable<TDestination> MapTo<TDestination>(this DataSet ds) where TDestination : class
{
if (ds == null || ds.Tables.Count == 0 || ds.Tables[0].Rows.Count == 0)
return new List<TDestination>();
return Mapper.Map<IEnumerable<TDestination>>(ds.Tables[0].CreateDataReader());
}
这里就不在多说
IDataReader转List说明
在项目中我需要将datatable以及dataset转成List ,查了下资料 automapper官网有独立的项目支撑其实现AutoMapper.Data,需要在nuget中安装AutoMapper.Data包
然后注册的地方有点变化:
public static void LoadConfig(string sectionName = "autoMapper")
{
var config = ConfigurationManager.GetSection(sectionName) as NameValueCollection;
if (config != null)
{
var assemlies = config.Keys
.Cast<string>()
.Select(e => config[e])
.Where(e => !string.IsNullOrEmpty(e))
.ToArray();
Mapper.Initialize(cfg => {
cfg.AddProfiles(assemlies);
});
}
Mapper.AssertConfigurationIsValid();
}
变成:
public static void LoadConfig(string sectionName = "autoMapper")
{
var config = ConfigurationManager.GetSection(sectionName) as NameValueCollection;
if (config != null)
{
var assemlies = config.Keys
.Cast<string>()
.Select(e => config[e])
.Where(e => !string.IsNullOrEmpty(e))
.ToArray();
Mapper.Initialize(cfg => {
//支持datareader to list
cfg.AddDataReaderMapping();
cfg.AddProfiles(assemlies);
});
}
Mapper.AssertConfigurationIsValid();
}
这里多了一个cfg.AddDataReaderMapping(); 这个就是注册支持datareader to list的
dome和映射关系注册:
public class DevicewareDto
{
public string DeviceNumber{ get; set; }
public string SIMCCID { get; set; }
}
Mapper.Initialize(cfg =>
{
cfg.AddDataReaderMapping();
cfg.CreateMap<IDataReader, DevicewareDto>();
});
DataTable dt = new DataTable();
dt.Columns.Add("DeviceNumber", typeof(string));
dt.Columns.Add("SIMCCID", typeof(string));
for (int i = 0; i < 10; i++)
{
var newRow = dt.NewRow();
newRow["DeviceNumber"] = "DeviceNumber";
newRow["SIMCCID"] = "SIMCCID";
dt.Rows.Add(newRow);
}
var list = Mapper.Map<IEnumerable<DevicewareDto>>(dt.CreateDataReader());
----------------------------------------------------------------------------------------------------------
Mapper.Initialize(cfg =>
{
cfg.AddDataReaderMapping();
cfg.CreateMap<IDataReader, DevicewareDto>();
});
Mapper.AssertConfigurationIsValid();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add("DeviceNumber", typeof(string));
dt.Columns.Add("SIMCCID", typeof(string));
for (int i = 0; i < 10; i++)
{
var newRow = dt.NewRow();
newRow["DeviceNumber"] = "DeviceNumber";
newRow["SIMCCID"] = "SIMCCID";
dt.Rows.Add(newRow);
}
ds.Tables.Add(dt);
var list = Mapper.Map<IEnumerable<DevicewareDto>>(ds.CreateDataReader());
但是有两个问题
注册映射关系
在控制台程序中 cfg.CreateMap<IDataReader, DevicewareDto>();加不加这句注册的代码都不会有问题没问题的,但是在mvc中加了是会报错的,错误提示是从IDataReader到DevicewareDto关系映射时没有指定FNumber和SIMCCID的映射关系,去掉之后能完成映射且不报错,这个点感觉很诡异 暂时没有分析出原因,后续会继续分享。
同名与不同名属性之间的映射
目前只能支持类的属性和列名是相同之间映射 不然是映射不出来值的,看了好多方案还是没有找到合适的解决方案 即使使用ForMember去指定映射关系(列名)也是不可的 只能转换出来同名属性的值, 那就换了种思路去解决,使用DataRow来实现datatable转List
注册信息如下:
CreateMap<DataRow, KnowledgeOverview>()
.ForMember(x => x.Title, opt => opt.MapFrom(src => src["Title"]))
.ForMember(x => x.AbStract, opt => opt.MapFrom(src => src["Summary"]))
.ForMember(x => x.Authors, opt => opt.MapFrom(src => src["Creator"]))
.ForMember(x => x.KeyWords, opt => opt.MapFrom(src => src["KeyWords"]))
.ForMember(x => x.Organizations, opt => opt.MapFrom(src => src["Contributor"]))
.ForMember(x => x.Fund, opt => opt.MapFrom(src => src["Fund"]))
.ForMember(x => x.Source, opt => opt.MapFrom(src => src["Source"]))
.ForMember(x => x.PublishDate, opt => opt.MapFrom(src => src["DATE"]));
这样就可以实现属性名和列名不同的值映射 但是也存在一些问题 就是即使是同名列也要使用ForMember()指定映射关系 不然就出不来值,这个大家要注意 !!!
补充说明关于 同名与不同名属性之间的映射的问题
通过研究官方的issus发现 有网友使用了IDataRecord实现了通过ForMember只要指定不同命属性就可以完成映射,在控制台尝试了一下确实是可以的,代码如下:
public class DevicewareDto
{
public string FNumber { get; set; }
public string SIMCCID { get; set; }
}
Mapper.Initialize(cfg =>
{
cfg.AddDataReaderMapping();
cfg.CreateMap<IDataRecord, DevicewareDto>()
.ForMember(dest => dest.FNumber, opt => opt.MapFrom(src => (string)src["DeviceNumber"]));
});
Mapper.AssertConfigurationIsValid();
DataSet ds = new DataSet();
DataTable dt = new DataTable();
dt.Columns.Add("DeviceNumber", typeof(string));
dt.Columns.Add("SIMCCID", typeof(string));
for (int i = 0; i < 10; i++)
{
var newRow = dt.NewRow();
newRow["DeviceNumber"] = "DeviceNumber"+ i;
newRow["SIMCCID"] = "SIMCCID"+ i;
dt.Rows.Add(newRow);
}
ds.Tables.Add(dt);
var list = Mapper.Map<IEnumerable<DevicewareDto>>(ds.CreateDataReader());
//DataTable dt = new DataTable();
//dt.Columns.Add("DeviceNumber", typeof(string));
//dt.Columns.Add("SIMCCID", typeof(string));
//for (int i = 0; i < 10; i++)
//{
// var newRow = dt.NewRow();
// newRow["DeviceNumber"] = "DeviceNumber";
// newRow["SIMCCID"] = "SIMCCID";
// dt.Rows.Add(newRow);
//}
//var list = Mapper.Map<IEnumerable<DevicewareDto>>(dt.CreateDataReader());
以为一切皆大欢喜 结果在MVC中依然无法通过映射关系检查,不过也断有了突破