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 ...
随机推荐
- Common Subsequence(dp)
Common Subsequence Time Limit: 2 Sec Memory Limit: 64 MBSubmit: 951 Solved: 374 Description A subs ...
- Windows程序----初识Windows程序
先来看一些励志名言来激励一下自己吧! 励志名言:每一发奋发奋的背后,必有加倍的赏赐 1.有无目标是成功者与平庸者的根本差别. 2.成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成. 3.当 ...
- Windbg对过滤驱动DriverEntry函数下断点技巧
方法1: 1> 先用DeviceTree.exe查看指定的过滤驱动的Load Address(加载地址) 2> 再用LordPE.EXE查看指定过滤驱动文件的入口点地址 3> 计算过 ...
- (转载)解决ListView中使用EditText所遇到的一些冲突
大家都知道在listView中使用editText,在输入过程中是有冲突的.今天稍微研究了一下这个问题,有一点点小小的心得和大家一起分享下. 首先建立一个最简单的demo,主界面就是一个ListVie ...
- Nginx安装与配置文件解析
导读 Nginx是一款开放源代码的高性能HTTP服务器和反向代理服务器,同时支持IMAP/POP3代理服务,是一款自由的软件,同时也是运维工程师必会的一种服务器,下面我就简单的说一下Nginx服务器的 ...
- Android ADB命令大全(通过ADB命令查看wifi密码、MAC地址、设备信息、操作文件、查看文件、日志信息、卸载、启动和安装APK等)
ADB很强大,记住一些ADB命令有助于提高工作效率. 获取序列号: adb get-serialno 查看连接计算机的设备: adb devices 重启机器: adb reboot 重启到bootl ...
- 【转】php json_encode中文为空的解决办法
转自:http://www.cnblogs.com/oldphper/p/4123415.html 本文列举3个方法,实现json_encode()后的string显示中文问题. 做接口时不需要,但存 ...
- ndk编译protobuf库
ndk_r9编译通过,里面带了自动生成代码的脚本(tool/createPBFile.bat). 下载地址
- Python网络编程(2)——socket模块(2)
目录: 1. 异常 2. 地址族 3. 套接字类型 4. 模块方法 5. Socket对象与实例方法 socket模块提供了Python中的低层网络连接接口,用于操作套接字操作. 异常 socket模 ...
- Java for LeetCode 025 Reverse Nodes in k-Group
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. If ...