源代码:TypeMapper.zip
背景
项目中,我们会经常用到各种赋值语句,比如把模型的属性赋值给UI,把视图模型的属性拷贝给Entity。如果模型属性太多,赋值也会变成苦力活。所以,框架编程的思维中,出现了”绑定“。绑定不仅可以简化赋值,还可以结合验证,简化绑定过程中的验证。
能实现绑定的框架很多,如AutoMapper,.Net自带的绑定机制,微软官方上还有一个利用绑定的Sample,等。
那些成熟的框架一般功能全面,考虑周全,一般推荐首选。但对于一些小项目个别情况,或许它们就会显得有些笨重了。前些时候读Demo研究了一下Linq Expression , 自己写一个,练练手巩固下知识吧。
代码主要使用了基本的LinqExpression中的赋值操作,代码也较短,可以作为学习示例也可以作为工具使用(可能要加一些功能了)。
介绍
先看下效果:
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" alt="" />
并没什么奇特的。
再看看后台,没有赋值语句,只是简单告诉程序:谁该绑定给谁,怎么绑而已:
public Form1() { InitializeComponent(); var typeMapper = new cp.lib.TypeMapper<ViewModel, Form1>() .AddMapItem(vm => vm.Name, form => form.txtName.Text) .AddMapItem(vm => vm.Amount, form => form.txtAmount.Text, a => a.ToString("#,###")) .AddMapItem(vm => vm.IsValid, form => form.chkIsValid.Checked) .AddMapItem(vm => vm.Type, form => form.cboType.SelectedIndex) .AddMapItem(vm => vm.CreatedTime, form => form.dtpCreateTime.Value); var model = new ViewModel() { Name = "How Are You", Amount = 123456789, IsValid = true, Type = 3, CreatedTime = new DateTime(2000, 10, 10) }; typeMapper.Map(model,this); }
那么,你可能会问了,你写这么长一段“诡异”的代码,有什么好处呢?
1 代码量变少。如果用单纯的赋值语句,和目前代码行数一样。一般而言,我们会实现双向绑定,即界面操作完成后,会把更新的值写回模型,然后重新存储。这意味着那时,你不得不把这些赋值语句反向重一遍。这样写,可以很方便的实现反向赋值。假想20个模型,每个20个属性,可以节省400行的代码量。
2 模块化。模块化后,这块功能有了边界,用以和其它模块交互。一般绑定会和验证,默认值,操作撤销,未保存提醒等结合。单就验证来说,如果加入了验证规则,我们可以在绑定的时候,依次查阅这些规则,自动校验。而不用在页面后重复的“if(xxx.Text==String.Empty{...}”。
3 便于阅读。现在读代码的感觉就是”把xxx的XXx属性绑定到xxx的Text属性上",这比直接读赋值语句要直观一些。虽然赋值语句比较简单,但也要经过人脑的翻译才能形成更直观的自然语言。所以程序风格中,一般提倡方法拆小(方法名可以充当注释)。
4 便于维护。便于维护往往意味着业务改变,代码改变很小甚至不用变。由于现在有专门的类负责在模型和界面间赋值,如果将这些绑定规则外置(放在配置文件中),如果发生改变,那么以后更新配置文件就行了。就算不规则外置,我们也可以把已经模块化的这部分代码放在单独的其它地方(内聚),如果改变绑定,测试也只用测这小部分。
当然,说了那么多,其实我写它的目的比较纯粹:练手,再就是希望能给新鲜码友以帮助。
实现
注释比较详细,就不多说了。
如果要进一步查看怎么使用,可以看下单元测试。
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; namespace cp.lib { public class TypeMapper<T1,T2> where T1:class where T2:class,new() { #region 双向绑定,未实现。 /// <summary> /// (预留属性)是否支持双向绑定。 /// </summary> public bool IsTwoWay { get; set; } public void MapBack(T2 targetObj, T1 sourceObj) { //--todo } public T1 MapBack(T2 targetObj ) { //--todo return default(T1); } #endregion //缓存的已经编译好的(从源对象属性到目标对象属性的)赋值语句。 private List<Action<T1,T2>> _assginExpressions=new List<Action<T1,T2>>(); public TypeMapper() { } /// <summary> /// 使用字典类型创建一个新TypeMapper实例 /// </summary> /// <param name="mapDictionary"></param> public TypeMapper(Dictionary<string,string> mapDictionary) { foreach (var item in mapDictionary) { AddMapItem(item.Key, item.Value); } } /// <summary> /// 按设置好的映射项将源对象的属性或字段拷贝到目标对象。 /// </summary> /// <param name="sourceObj">源对象</param> /// <param name="targetObj">目标对象</param> public void Map(T1 sourceObj,T2 targetObj) { foreach (var action in _assginExpressions) { action(sourceObj, targetObj); } } /// <summary> /// 按设置好的映射项从源对象的属性或字段创建新的目标对象 /// </summary> /// <param name="sourceObj">源对象</param> /// <returns>新的目标对象(T2类型)</returns> public T2 Map(T1 sourceObj) { if (sourceObj == null) { return default(T2); } var targetObj = new T2(); Map(sourceObj, targetObj); return targetObj; } /// <summary> /// 按设置好的映射项将源对象的属性或字段拷贝到目标对象。 /// </summary> /// <param name="sourceObjs">源对象</param> /// <param name="targetObjs">目标对象</param> public void Map(IEnumerable<T1> sourceObjs, IEnumerable<T2> targetObjs) { if (sourceObjs.Count() != targetObjs.Count()) { throw new ArgumentException("sourceObjs和targetObjs数量不一致!"); } var sourceEmumerator = sourceObjs.GetEnumerator(); var targetEnumerator = targetObjs.GetEnumerator(); while (sourceEmumerator.MoveNext()) { targetEnumerator.MoveNext(); var sourceObj = sourceEmumerator.Current; var targetObj = targetEnumerator.Current; Map(sourceObj, targetObj); } } /// <summary> /// 按设置好的映射项从源对象的属性或字段创建新的目标对象 /// </summary> /// <param name="sourceObj">源对象</param> /// <returns>新的目标对象(IEnumerable T2类型)</returns> public IEnumerable<T2> Map(IEnumerable<T1> sourceObjs) { foreach (var sourceObj in sourceObjs) { yield return Map(sourceObj); } } /// <summary> /// 增加映射项。映射项以Expression表式,如x=>x.Name。 /// </summary> /// <exception>targetExp不为MemberAccess将引发参数异常。</exception> /// <returns>返回当前对象</returns> public TypeMapper<T1, T2> AddMapItem<TResult>(Expression<Func<T1,TResult>> sourceExp,Expression<Func<T2,TResult>> targetExp) { if (targetExp.Body.NodeType != ExpressionType.MemberAccess) { throw new ArgumentException("targetExp应该为MemberAccess类型!"); } var assignExp = Expression.Assign(targetExp.Body, sourceExp.Body); var lambda = Expression.Lambda<Action<T1, T2>>(assignExp, sourceExp.Parameters[0], targetExp.Parameters[0]); _assginExpressions.Add(lambda.Compile()); return this; } /// <summary> /// 增加映射项。映射项以Expression表式,如x=>x.Name。 /// </summary> /// <exception>targetExp不为MemberAccess将引发参数异常。</exception> /// <returns>返回当前对象</returns> public TypeMapper<T1, T2> AddMapItem<TResult1, TResult2>(Expression<Func<T1, TResult1>> sourceExp,
Expression<Func<T2, TResult2>> targetExp, Expression<Func<TResult1, TResult2>> formatFunc) { if (targetExp.Body.NodeType != ExpressionType.MemberAccess) { throw new ArgumentException("targetExp应该为MemberAccess类型!"); } var formatExp = Expression.Invoke(formatFunc, sourceExp.Body); var assignExp = Expression.Assign(targetExp.Body, formatExp); var lambda = Expression.Lambda<Action<T1, T2>>(assignExp, sourceExp.Parameters[0], targetExp.Parameters[0]); _assginExpressions.Add(lambda.Compile()); return this; } /// <summary> /// 使用属性(或字段)名增加映射项。 /// </summary> /// <param name="sourceProperty">源对象属性(或字段)名称</param> /// <param name="targetProperty">目标对象属性(或字段)名称</param> /// <returns>返回当前对象</returns> public TypeMapper<T1, T2> AddMapItem(string sourceProperty,string targetProperty) { var parameter1 = Expression.Parameter(typeof(T1),"o1"); var memeber1 = Expression.PropertyOrField(parameter1, sourceProperty); var parameter2 = Expression.Parameter(typeof(T2), "o2"); var memeber2 = Expression.PropertyOrField(parameter2, targetProperty); var assignExp = Expression.Assign(memeber2, memeber1); var lambda = Expression.Lambda<Action<T1, T2>>(assignExp, parameter1, parameter2); _assginExpressions.Add(lambda.Compile()); return this; } /// <summary> /// 按照类型T1,T2中相同名称和类型的属性或字段,自动添加所有映射项。 /// </summary> /// <returns>返回当前项</returns> public TypeMapper<T1, T2> AutoBuildMap() { var p1s = typeof(T1).GetProperties(); var p2s = typeof(T2).GetProperties(); foreach (var p1 in p1s) { foreach (var p2 in p2s) { //目前暂不处理Nullable<int> -> int的映射 if (p1.Name == p2.Name && p1.PropertyType == p2.PropertyType) { AddMapItem(p1.Name, p2.Name); } } } var f1s = typeof(T1).GetFields(); var f2s = typeof(T2).GetFields(); foreach (var f1 in f1s) { foreach (var f2 in f2s) { if (f1.Name == f2.Name && f1.FieldType == f2.FieldType) { AddMapItem(f1.Name, f2.Name); } } } return this; } } }
说明
写Linq表达式,就像你在告诉电脑怎么写代码,如谁赋值给谁,调用什么方法,参数是什么,然后是什么。表达式的操作始终返回表达式,最后使用Compile方法,将其转化为委托(function 或 Action)。
Expression<Func<>>和Func<>参数都可以直接传入Lambda表达式,这说明它们之间存在隐式转换。仔细查看Linq中的一些扩展方法的参数类型,虽然它们是Expression,但我们写法还是Func<>的Lambda。将参数声明为Expression,会将表达式视为一个语句,而非方法,可以更方便的操作表达式的元素。
代码中,先手动构建Linq的赋值表达式,然后将其编译为赋值Action缓存起来,表达式编译后其实和下面的Action等同:
AddMapItem(v=>v.Name,form=>form.txtName.Text); || Action<ViewModel,Form1>( (v,form)=>form.txtName.Text=v.Name ); AddMapItem(v=>v.Amount,form=>form.txtAmount.Text,a=>a.ToString()); || Action<ViewModel,Form1>( (v,form)=>form.txtAmount.Text=(v.Amount).ToString() );
使用Linq表达式构建绑定项,比直接使用属性名的方式好一点点: 因为敲v.Name时有智能提示,如果直接输入"Name",比较费时,如果属性长一点还容易出错。属性变了,也享受不到VS自动重构重命名的好处。当然,直接使用属性名添加绑定项更灵活,如果要将规则放在config文件中,就会更有用一些了。
一般而言,反射赋值的性能相对较低。代码中将那些赋值语句以委托的形式缓存,只是为了优化提高一下性能而已。