ASP.NET MVC 模型和数据对象映射实践
在使用 MVC 开发项目的过程中遇到了个问题,就是模型和数据实体之间的如何快捷的转换?是不是可以像 Entity Framework 的那样 EntityTypeConfiguration,或者只需要少量的代码就可以把数据实体对象转换成一个 Model 对象(当时还不知道有 AutoMapper 这种东西),所以自己尝试写了一个简单的实现。
1、初步尝试
EntityTypeConverter 类主要用把数据实体转换成 Model
/// <summary>
/// 提供一组实体对象模型转换的方法。
/// </summary>
/// <typeparam name="TEntityInfo">指定的实体信息对象类型。</typeparam>
public static class EntityTypeConverter<TEntityInfo>
{
/// <summary>
/// 将指定的实体对象转换成 <see cref="BaseModel"/> 类型的对象模型。
/// </summary>
/// <typeparam name="BaseModel">指定类型的模型对象。</typeparam>
/// <param name="tEntityInfo">指定的实体信息。</param>
/// <returns><see cref="BaseModel"/> 类型的对象模型。</returns>
public static BaseModel ConverterToModel<BaseModel>(TEntityInfo tEntityInfo) where BaseModel : new()
{
if (tEntityInfo == null)
throw new ArgumentNullException("tEntityInfo"); BaseModeltEntity = new BaseModel();
foreach (var prop in typeof(BaseModel).GetProperties())
{ if (!prop.CanRead || !prop.CanWrite)
continue; if (!CommonHelper.GetCustomTypeConverter(prop.PropertyType).CanConvertFrom(typeof(string)))
continue;
string fieldName = String.Empty;
// 验证是否有别名
var customAttribute = prop.CustomAttributes.Where(att => att.AttributeType == typeof(ModelFieldAliasAttribute)).FirstOrDefault();
if (customAttribute != null)
{
var constructorArgument = customAttribute.ConstructorArguments[0];
fieldName = constructorArgument.Value.ToString();
}
else
{
fieldName = prop.Name;
}
PropertyInfo property = typeof(TEntityInfo).GetProperty(fieldName);
if (property != null)
{
dynamic value = property.GetValue(tEntityInfo, null);
prop.SetValue(tEntity, value, null);
}
}
return tEntity;
} /// <summary>
/// 将指定的实体对象转换成 <see cref="BaseModel"/> 类型的对象模型列表。
/// </summary>
/// <typeparam name="BaseModel">指定类型的模型对象。</typeparam>
/// <param name="tEntityList">指定的实体信息列表。</param>
/// <returns><see cref="BaseModel"/> 类型的对象模型列表。</returns>
public static List<BaseModel> ConverterToList<BaseModel>(IList<TEntityInfo> tEntityList) where BaseModel: new()
{
List<BaseModel> modelList = new List<BaseModel>();
if (tEntityList != null && tEntityList.Count > 0)
{
foreach (var item in tEntityList)
{
modelList.Add(ConverterToModel<BaseModel>(item));
}
}
return modelList;
}
}
辅助类基于 TypeConverter 实现的方法,用于泛型字段的转换
public class GenericListTypeConverter<T> : TypeConverter
{
protected readonly TypeConverter typeConverter; public GenericListTypeConverter()
{
typeConverter = TypeDescriptor.GetConverter(typeof(T));
if (typeConverter == null)
throw new InvalidOperationException("No type converter exists for type " + typeof(T).FullName);
} protected virtual string[] GetStringArray(string input)
{
if (!String.IsNullOrEmpty(input))
{
string[] result = input.Split(',');
Array.ForEach(result, s => s.Trim());
return result;
}
else
return new string[0];
} public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{ if (sourceType == typeof(string))
{
string[] items = GetStringArray(sourceType.ToString());
return items.Any();
} return base.CanConvertFrom(context, sourceType);
} public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
string[] items = GetStringArray((string)value);
var result = new List<T>();
Array.ForEach(items, s =>
{
object item = typeConverter.ConvertFromInvariantString(s);
if (item != null)
{
result.Add((T)item);
}
}); return result;
}
return base.ConvertFrom(context, culture, value);
} public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
string result = string.Empty;
if (((IList<T>)value) != null)
{
//we don't use string.Join() because it doesn't support invariant culture
for (int i = 0; i < ((IList<T>)value).Count; i++)
{
var str1 = Convert.ToString(((IList<T>)value)[i], CultureInfo.InvariantCulture);
result += str1;
//don't add comma after the last element
if (i != ((IList<T>)value).Count - 1)
result += ",";
}
}
return result;
} return base.ConvertTo(context, culture, value, destinationType);
}
}
自定义属性 ModelFieldAlias 类用于标识字段别名。
/// <summary>
/// 表示一个模型转换对象字段类型的特性。
/// </summary>
[AttributeUsage(ValidTargets, AllowMultiple = false, Inherited = false)]
public class ModelFieldAliasAttribute : Attribute
{
/// <summary>
/// 指定此属性可以应用特性的应用程序元素。
/// </summary>
internal const AttributeTargets ValidTargets = AttributeTargets.Field | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Parameter; /// <summary>
/// 模型的字段别名。
/// </summary>
private string _fieldAlias; /// <summary>
/// 初始化 <see cref="ModelFieldAliasAttribute"/> 类的新实例。
/// </summary>
public ModelFieldAliasAttribute()
{ } /// <summary>
/// 使用指定的筛选类型初始化 <see cref="ModelFieldAliasAttribute"/> 类的新实例。
/// </summary>
/// <param name="fieldAlias">指定模型的字段别名。</param>
public ModelFieldAliasAttribute(string fieldAlias)
{
_fieldAlias = fieldAlias;
} /// <summary>
/// 获取或设置模型的字段别名。
/// </summary>
public string FieldAlias
{
get { return _fieldAlias; }
set { _fieldAlias = value; }
} }
使用方法:
// 集合对象
List<ChannelNavigationInfo> channelNavigationList = new List<ChannelNavigationInfo>();
List<ChannelNavigationModel> channelNavigationModelList = new List<ChannelNavigationModel>();
channelNavigationModelList = EntityTypeConverter<ChannelNavigationInfo>.ConverterToList<ChannelNavigationModel>(channelNavigationList); // 单独对象
ChannelNavigationInfo channelNavigation = new ChannelNavigationInfo();
ChannelNavigationModel channelNavigationModel = new ChannelNavigationModel();
channelNavigationModel = EntityTypeConverter<ChannelNavigationInfo>.ConverterToModel<ChannelNavigationModel>(channelNavigation);
由于系统中大多数情况下都是对数据查询的业务,很少有插入和更新,所以没有发现这种实现的弊端,后来开发管理系统的时候,需要大量的数据插入和更新操作,发现这种方法没法做反向映射和转换。
2、AutoMapper
AutoMapper 是用来解决对象之间映射转换的类库。对于我们开发人员来说,写对象之间互相转换的代码是一件极其浪费生命的事情,AutoMapper 能够帮助我们节省不少时间。
知道这个类库是在研究 nopCommerce 这个项目的时候看到的,使用 AutoMapper 创建映射非常简单。
Mapper.CreateMap<Order, OrderDto>();//创建映射关系Order –> OrderDto
OrderDto dto = Mapper.Map<OrderDto>(order);//使用Map方法,直接将order对象装换成OrderDto对象
AutoMapper能够自动识别和匹配大部分对象属性:
如果源类和目标类的属性名称相同,直接匹配,目标类型的 CustomerName 可以匹配源类型的 Customer.Name,目标类型的 TotalRecords 可以匹配源类型的 GetTotalRecords() 方法。
AutoMapper 还支持自定义匹配规则:
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
// 属性匹配,匹配源类中 WorkEvent.Date 到 EventDate
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.WorkEvent.Date))
.ForMember(dest => dest.SomeValue, opt => opt.Ignore())//忽略目标类中的属性
.ForMember(dest => dest.TotalAmount, opt => opt.MapFrom(src => src.TotalAmount ?? 0))//复杂的匹配
.ForMember(dest => dest.OrderDate, opt => opt.UserValue<DateTime>(DateTime.Now)); //固定值匹配
为了方便使用,可以把扩展方法统一在一个类中实现。
/// <summary>
/// 提供一组对象映射相关的扩展方法。
/// </summary>
public static class MappingExtensions
{
#region City... /// <summary>
/// 执行从 <see cref="City"/> 对象到 <see cref="CityModel"/> 对象的映射。
/// </summary>
/// <param name="entity">指定的 <see cref="City"/> 对象。</param>
/// <returns><see cref="CityModel"/> 对象。</returns>
public static CityModel ToModel(this City entity)
{
return Mapper.Map<City, CityModel>(entity);
} /// <summary>
/// 执行从 <see cref="CityModel"/> 对象到 <see cref="City"/> 对象的映射。
/// </summary>
/// <param name="model">指定的 <see cref="CityModel"/> 对象。</param>
/// <returns><see cref="City"/> 对象。</returns>
public static City ToEntity(this CityModel model)
{
return Mapper.Map<CityModel, City>(model);
} /// <summary>
/// 执行从 <see cref="CityModel"/> 对象到 <see cref="City"/> 对象的映射。
/// </summary>
/// <param name="model">指定的 <see cref="CityModel"/> 模型对象。</param>
/// <param name="destination">指定的 <see cref="City"/> 实体对象。</param>
/// <returns><see cref="City"/> 对象。</returns>
public static City ToEntity(this CityModel model, City destination)
{
return Mapper.Map(model, destination);
} #endregion
}
最后,在 Global.cs 文件中程序启动前,调用该方法。
AutoMapperConfiguration.Configuration();
ASP.NET MVC 模型和数据对象映射实践的更多相关文章
- ASP.NET MVC 5 - 将数据从控制器传递给视图
在我们讨论数据库和数据模型之前,让我们先讨论一下如何将数据从控制器传递给视图.控制器类将响应请求来的URL.控制器类是给您写代码来处理传入请求的地方,并从数据库中检索数据,并最终决定什么类型的返回结果 ...
- ASP.NET没有魔法——ASP.NET MVC 模型绑定
在My Blog中已经有了文章管理功能,可以发布和修改文章,但是对于文章内容来说,这里缺少最重要的排版功能,如果没有排版的博客很大程度上是无法阅读的,由于文章是通过浏览器查看的,所以文章的排版其实与网 ...
- [转] ASP.NET MVC 模型绑定的功能和问题
摘要:本文将与你深入探究 ASP.NET MVC 模型绑定子系统的核心部分,展示模型绑定框架的每一层并提供扩展模型绑定逻辑以满足应用程序需求的各种方法. 同时,你还会看到一些经常被忽视的模型绑定技术, ...
- [转]ASP.NET MVC 5 - 将数据从控制器传递给视图
在我们讨论数据库和数据模型之前,让我们先讨论一下如何将数据从控制器传递给视图.控制器类将响应请求来的URL.控制器类是给您写代码来处理传入请求的地方,并从数据库中检索数据,并最终决定什么类型的返回结果 ...
- ASP.NET MVC 4 (一)路径映射
原文:ASP.NET MVC 4 (一)路径映射 正如ASP.NET MVC名字所揭示的一样,是以模型-视图-控制设计模式构建在ASP.NET基础之上的WEB应用程序,我们需要创建相应的程序类来协调处 ...
- ASP.NET MVC模型绑定的6个建议(转载)
ASP.NET MVC模型绑定的6个建议 发表于2011-08-03 10:25| 来源博客园| 31 条评论| 作者冠军 validationasp.netmvc.netasp 摘要:ASP.NET ...
- 【ASP.NET MVC系列】数据验证和注解
[01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作篇)(下) [04]浅谈ASP. ...
- ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(下篇)
上一篇<ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(上篇)>文章介绍了ASP.NET MVC模型绑定的相关组件和概念,本章将介绍Controller在执行时是如何通过这 ...
- ASP.NET MVC - 模型验证
ASP.NET MVC - 模型验证(Model verification) 模型验证原理浅析 模型验证用到了模型绑定器.模型验证器(System.Web.Mvc.DataAnnotationsMod ...
随机推荐
- HDOJ 1524 A Chess Game
A Chess Game Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tota ...
- PHP访问,增删改查,小结
PHP访问数据,增,删,改,查 增: 1,add.php 显示页面,利用 <form> 表单添加数据,数据添加到 name 中. 2,addchuli.php 处理页面,定义变量接受 $_ ...
- cocos2d 如何优化内存使用
如何优化内存使用 内存优化原理 为优化应用内存使用,开发人员首先应该知道什么最耗应用内存,答案就是纹理! 纹理几乎会占据90%应用内存.所以尽量最小化应用的纹理内存使用,否则应用很有可能会因为低内存而 ...
- 从零开始写一个武侠冒险游戏-8-用GPU提升性能(3)
从零开始写一个武侠冒险游戏-8-用GPU提升性能(3) ----解决因绘制雷达图导致的帧速下降问题 作者:FreeBlues 修订记录 2016.06.23 初稿完成. 2016.08.07 增加对 ...
- dex和odex相互转换
一.dex和odex dex是安卓dalvik虚拟机的可执行文件,可以在导出的apk文件里用解压缩软件直接打开.odex是经过优化过的dex.odex一种是从apk程序中提取出来的,与apk文件存放在 ...
- 配置oss bucket cors
到bucket中属性中选择跨越设置,点击添加规则会看到以下界面: 对应的输入如上即可.
- Tornaod框架
Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效 ...
- 如何在Linux上实现文件系统的自动检查和修复?
Linux文件系统有可能在各种各样的情况下受到损坏,比如系统崩溃.突然断电.磁盘断开,或者文件节点 (i-node)不小心被覆盖等等,因此需要定期检查文件系统,而说到检查和修复Linux文件系统,fs ...
- Java锁之自旋锁详解
锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类 ...
- 百度编辑器ueditor每次编辑后多一个空行的解决办法
用ueditor进行编辑文章时,每次编辑后文章前面都会多出一个空行. <script id="editor" type="text/plain" styl ...