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 ...
随机推荐
- 神秘代码让iPhone微信闪退的解决方法
14号晚,很多人的微信朋友圈中出现了这样几句话“听说苹果手机点全文就会闪退”,下方有好几行空白,需要点击“全文”才能看到,但是一旦你是在iPhone手机微信上点击“原文”后就直接闪退了,而用Andro ...
- dedecms删除没有文章的标签
要批量的删除织梦TAG标签,那我们就只能在数据库里做修改了. 登录数据库,在数据库里执行以下SQL语句: delete FROM dede_tagindex where typeid not in ( ...
- Nginx 开启PATHINFO支持ThinkPHP框架实例
ThinkPHP支持通过PATHINFO和URL rewrite的方式来提供友好的URL,只需要在配置文件中设置 'URL_MODEL' => 2 即可.在Apache下只需要开启mod_rew ...
- =============Python安装与使用================
用文本编辑器写Python程序,然后保存为后缀为.py的文件,就可以用Python直接运行这个程序了. Python的交互模式和直接运行.py文件有什么区别呢? 直接输入python进入交互模式,相当 ...
- php面试题之五——PHP综合应用(高级部分)
五.PHP综合应用 1.写出下列服务的用途和默认端口(新浪网技术部) ftp.ssh.http.telnet.https ftp:File Transfer Protocol,文件传输协议,是应用层的 ...
- FineUI第八天----下拉列表控件
下拉列表控件 3.模拟树的下拉列表: 其他的控件都跟Asp.net的差不多.
- [BZOJ1659][Usaco2006 Mar]Lights Out 关灯
[BZOJ1659][Usaco2006 Mar]Lights Out 关灯 试题描述 奶牛们喜欢在黑暗中睡觉.每天晚上,他们的牲口棚有L(3<=L<=50)盏灯,他们想让亮着的灯尽可能的 ...
- json串转对象
// 引入相应的包 //json-lib-2.2-jdk15.jar import net.sf.json.JSONArray;import net.sf.json.JSONObject; 1. // ...
- HM必修3
高中数学必修三 笔记与拓展 算法初步 算法是按照一定规则解决固定问题,通过对输入的某种变换产生结果. 素性测试 检验一个数是否为素数. 试除法 一个数是素数的充分必要条件是它因数个数为二.显然1和它本 ...
- wget批量下载
wget -i download.txt 这样就会把download.txt里面列出的每个URL都下载下来. wget -c http://the.url.of/incomplete/file 使用断 ...