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 2089 不要62
不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submis ...
- poj2632 模拟
Crashing Robots Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8388 Accepted: 3631 D ...
- HLG2040二叉树遍历已知前中,求后
二叉树的遍历 Time Limit: 1000 MS Memory Limit: 32768 K Total Submit: 60(34 users) Total Accepted: 34(30 us ...
- 二模 06day2
很长时间没更新有意义的题目了呢,这是一套题撒,于是乎我便开心的边刷题边发题解了撒. 第一题: interval 比较好玩的一题撒, 分分钟过了, 就是模拟贪吃蛇但是没有食物(嗯,只要你判断冲突). 整 ...
- 查看别人的css
ie工具栏的“文件”选项选“另存为”到你本地电脑,存下来有两个文件 一个是空间名称命名的文件夹和html网页,文件加里有三个扩展名为.css的文件
- python中的引用
作为一个python初学者,今天被一个python列表和词典引用的问题折磨了很久,但其实了解了缘由也很简单,记录在此备忘. 首先背书python中的引用对象问题: 1. python不允许程序员选择采 ...
- java常用算法
冒泡排序: //降序 public static int[] bubbleSort(int[] array){ for(int i = 0; i < array.length; i++){ in ...
- codeforces 471B. MUH and Important Things 解题报告
题目链接:http://codeforces.com/problemset/problem/471/B 题目意思:有 n 个 tasks,编号依次为 1 - n,每个 task 都有一定的难度值来评估 ...
- javascript 中string 型数据转换成int类型
var str1 = "1234";var str2 = "1234";number = parseInt(str1); number就是int型 str1+s ...
- Could not create the view: An unexpected exception was thrown. 电脑突然断电,myeclipse非正常关闭,出现错误
电脑突然断电,myeclipse非正常关闭,“Package Explorer”非正常显示,出现错误“Could not create the view: An unexpected exceptio ...