Winform开发的利器autoMapper

很多年没有在这儿写技术博客了。有些生疏,朋友们见谅。

引子

估计有不少写.Net的朋友也是和我一样,平时主要以WebForm开发位置,遇到WinForm开发的时候,总会遇到一些本来在WebForm下面很熟练的问题,在WinForm下面却麻烦重重了。

我也刚开始写WinForm,时间不长。希望在这个过程中遇到的一些问题和解决办法,可以一一拿出来和朋友们分享。

场景

有如下一个及其简单的场景:

1、主界面(Main)中有一项需要在另外一个Item编辑窗口(ItemEdit)中编辑;

2、用户可以在窗口中对Item中的一些属性就行修改,保存后回到主窗口,这时主窗口在不刷新数据库的情况下,可以显示用户最新的编辑内容;

3、用户也可能编辑部分内容后放弃了继续编辑,此时主窗口的内容应该保持不变。

非AutoMapper的一些解决办法

为实现上述需求,通常,编辑界面的构造函数中,添加一个参数用于传递待编辑对象即可。如

public ItemEdit(Entity entity){}

 

上述需求如果在WebForm中很容易实现。因为WebForm的界面之间在不使用浏览器脚本的情况下,是相互独立的,没有通信。但对于WinForm,在实现场景需求3的时候,由于Entity是引用类型,如Entity被绑定到控件,用户一旦编辑后,实体将被同步修改,即便用户取消,在主界面中实体也已经被修改了。

 

一种相对ugly的解决办法是,在ItemEdit中,对象不绑定到控件,而在加载时手动为控件赋值,同时,在用户确定保存的时候,再从控件中依次取值,并依次赋值到Entity中。如

ContractedBlock.gif ExpandedBlockStart.gif Load时为控件赋值
 
     
private TextBox tbxName = new TextBox();
private void ItemEdit_Load( object sender, EventArgs e)
{
tbxName.Text
= _entity.Name;
}
private Button btvSave = new Button();
private void btnSave_Click( object sender, EventArgs e)
{
_entity.Name
= tbxName.Text;
Save(_entity);
}

 


在使用WinForm的时候,更常用的方式是采用控件绑定的方式。如:

ContractedBlock.gif ExpandedBlockStart.gif Save时从控件取值
 
     
private TextBox tbxName = new TextBox();
private Button btvSave = new Button();

private void ItemEdit_Load( object sender, EventArgs e)
{
tbxName.DataBings.Add(
" Text " ,_entity, " Name " );
}

private void btnSave_Click( object sender, EventArgs e)
{
Save(_entity);
}

 


很明显,如果采用第二种方法,_entity的值将是和控件的中绑定的属性同步的。如果要避免这种情况,我们在页面编辑时,需要使用另外一个对象,以避免编辑时的实体与传入的entity存在引用关系。你也可以使用下面的这种方式,——如果在Entity有几十个属性的时候,你也不觉得麻烦的话,——下面的内容你也不需要在遇到了。

Entity _entityForEdit = new Entity();
_entityForEdit.Name = _entity.Name;

AutoMapper的解决办法

在这里,我们采用AutoMapper来觉得这种问题。为了方便,我采用一个简单的测试用例的方式来说明。

ContractedBlock.gif ExpandedBlockStart.gif ItemEdit
 
     
using AutoMapper;
using NUnit.Framework;

namespace RQQuery.Client.Test
{
[TestFixture]
public class AutoMapperTest
{
[Test]
public void MainTest()
{
const string name = " Entity From Main. " ;
var entityForSave
= new Entity {Name = name};
var codeForSave
= entityForSave.GetHashCode();
var itemEditSaved
= new ItemEdit(entityForSave);
itemEditSaved.Edit();
itemEditSaved.Save();
// 期望Name值被修改。
Assert.AreEqual( " Entity is changed. " , entityForSave.Name, " 修改不成功 " );
// 期望引用关系不被破坏。
Assert.AreEqual(codeForSave, entityForSave.GetHashCode(), " 引用关系丢失 " );

var entityForCancel
= new Entity { Name = name };
var codeForCancel
= entityForSave.GetHashCode();
var itemEditCanceled
= new ItemEdit(entityForCancel);
itemEditCanceled.Edit();
itemEditCanceled.Cancel();
// 期望Name值没有被修改。
Assert.AreEqual(name, entityForCancel.Name, " 已被修改 " );
// 期望引用关系不被破坏。
Assert.AreEqual(codeForCancel, entityForSave.GetHashCode(), " 引用关系丢失 " );
}
}

public class Entity
{
public string Name { get ; set ; }
}

public class ItemEdit
{
private readonly Entity _entity;
private readonly Entity _entityForEdit;

public ItemEdit(Entity entity)
{
_entity
= entity;

Mapper.CreateMap
< Entity, Entity > ();

// 使用Mapper.Map<TSource,TDistination>(TEntity entity),获取新的实例。
_entityForEdit = Mapper.Map < Entity, Entity > (_entity);
}

public void Edit()
{
_entityForEdit.Name
= " Entity is changed. " ;
}

public void Save()
{
// 使用Mapper.Map<TSource,TDistination>(TEntity entity, TDistination distinationEntity),
// 将从entity中取值,并复制给distinationEntity。
Mapper.Map < Entity,Entity > (_entityForEdit, _entity);

// you can save _entity here.
// ....
}

public void Cancel()
{
// do nothing.
}
}
}

 


上述MainTest方法,即模拟主界面的操作过程。代码是最好的注释,其余的就不详细解释了。zz_3.gif

在使用AutoMapper的时候,通过Mapper.CreateMap方法,创建实体的映射关系。

Mapper.CreateMap<Entity, Entity>();

我们在这里,将Entity自己映射为Entity。如果不声明这个映射,在使用Mapper.Get的时候,我们也可以获得对象,但AutoMapper将总是创建一个新的实例来完成映射

Mapper.CreateMap第一个参数为源类型,第二个参数为目标类型。在映射时,如果源类型的属性与目标类型中的属性对应,即可完成映射;如果属性不同,可以通过ForMember、Ignore等方法声明对关系进行声明。更多用法的详细说明,可参考文档(我没有找到更详细的,你有的话,可以告诉我。zz_16.gif )。代码是最好的注释,IDE工具智能感知一下,用起来还是比较顺手的。或者google之。

 

----------------------------------------分隔线后面是精华----------------------------------

重点注意

A:在对实体映射赋值时,只带source一个参数的Map方法,AutoMapper将创建新的实例作为返回值。

_entityForEdit = Mapper.Map<Entity, Entity>(_entity);

 

B:在对实体映射赋值时,使用带Distination的Map方法,AutoMapper将source实体的值映射到destination上,并不创建新的实例。此时也有返回值,与destination相同。在这里,我们将_entityForEdit的值直接赋给_entity,以保证引用关系的不被破坏。(如采用A中的方法,则HashCode测试将不通过。)

Mapper.Map<Entity,Entity>(_entityForEdit, _entity);
--EOF--

转载于:https://www.cnblogs.com/workingbird/articles/1804091.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值