概述

这里有许多的解决方案,来实际对象间数据的复制。最常见的应用场景就是生成DTO对象。让我们假设你有一个数据访问层。在这一层,你通过一些工具,访问了数据库。比如LINQ to SQL、Entity Framework或者其他的ORM框架工具。这些工具能够将查询的结果转换为对象。但是这些对象中,包含了很多的技术细节。它可能基于一些特定的类、特性、属性或其他的特有设置。数据访问层直接抛出这种对象,有时并不是很好的选择。有时,甚至无法做到。 一个常见的方法是使用DTO

假如我们建了下面的表:

 

CREATE TABLE [dbo].[Customers](
    [CustomerID] uniqueidentifier NOT NULL PRIMARY KEY,
    [ContactName] [nvarchar](30) NULL,
    [Address] [nvarchar](60) NULL,
    [Phone] [nvarchar](24) NULL
)

 



在数据访问层,我们通过LINQ to SQL访问数据库。 Visual Studio设计器会生成customers表的实体对象:

 

[Table(Name="dbo.Customers")]
public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged
{
    private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
    private string _CustomerID;
    private string _ContactName;
    private string _Address;
    private string _Phone;
     
#region Extensibility Method Definitions
...
#endregion
     
    public Customer()
    {
        OnCreated();
    }
     
    [Column(Storage="_CustomerID", DbType="NChar(5) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]
    public string CustomerID
    {
        get
        {
            return this._CustomerID;
        }
        set
        {
            if ((this._CustomerID != value))
            {
                this.OnCustomerIDChanging(value);
                this.SendPropertyChanging();
                this._CustomerID = value;
                this.SendPropertyChanged("CustomerID");
                this.OnCustomerIDChanged();
            }
        }
    }
     
    [Column(Storage="_ContactName", DbType="NVarChar(30)")]
    public string ContactName
    {
        get
        {
            return this._ContactName;
        }
        set
        {
            if ((this._ContactName != value))
            {
                this.OnContactNameChanging(value);
                this.SendPropertyChanging();
                this._ContactName = value;
                this.SendPropertyChanged("ContactName");
                this.OnContactNameChanged();
            }
        }
    }
    ...
    public event PropertyChangingEventHandler PropertyChanging;
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected virtual void SendPropertyChanging()
    {
        if ((this.PropertyChanging != null))
        {
            this.PropertyChanging(this, emptyChangingEventArgs);
        }
    }
     
    protected virtual void SendPropertyChanged(String propertyName)
    {
        if ((this.PropertyChanged != null))
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

 


我们会发现,这个对象中,包含了许多的信息,是不需要暴露出去的。为了解决这个问题,我们创建了一个自定义的DTOCustomer类:

 

public class DTOCustomer
{
    public Guid CustomerId { get; set; }
    public string ContactName { get; set; }
    public string Address { get; set; }
    public string Phone { get; set; }
}

 


现在,数据访问层方法看起来像下面的样子:

 

public DTOCustomer GetCustomer(Guid customerId)
{
    using (var dc = new DataContext())
    {
        var customer = dc.Customers.Where(c => c.CustomerID == customerId).Single();
        return new DTOCustomer // <-- this is our DTO class which is visible from outside
        {
            CustomerID = customer.CustomerID,
            ContactName = customer.ContactName,
            Address = customer.Address,
            Phone = customer.Phone
        };
    }
}

 


这个解决方案有以下问题:
  • 这种单调重复的写法,很让人郁闷
  • 很容易产生BUG。如果你在Customer 表中新增了一个字段,那么,你需要在DTOCustomer中新增一个属性,同时在映射方法中写入映射代码,如果少写了,就会抛出错误。

这个问题可以使用EmitMapper类库来解决

 

public DTOCustomer GetCustomer(Guid customerId)
{
    using (var dc = new DataContext())
    {
        var customer = dc.Customers.Where(c => c.CustomerID == customerId).Single();
        return ObjectMapperManager.DefaultInstance.GetMapper<Customer, DTOCustomer>().Map(customer);
    }
}

 


Emit Mapper是一个非常强大的工具。你能够自定义数据来源,类型转换,要转换为的对象。 Emit Mapper 类库没有使用System.Data命名空间,但是能够定义策略来实现DbDatareader 到Entity的映射。你能够在DbDatareader的字段上打特性,或者映射DTO的部分属性,或者自定义自己的想要的逻辑

能够使用EmitMapper来跟踪对象的更改:

 

public class A
{
    public string f1 = "f1";
    public int f2 = 2;
    public bool f3 = true;
}
...
var tracker = new ObjectsChangeTracker();
var a = new A();
tracker.RegisterObject(a);
a.f2 = 3;
string[] changes = tracker.GetChanges(a);
Assert.IsTrue( changes[0] == "f2");

 


能够自动生成新增或者更新的SQL语句

 

using (DbConnection connection = CreateConnection())
{
    DBTools.InsertObject(
        connection,
        new Customer
        {   
            CustomerID = Guid.NewGuid(),
            ContactName = "John Smith",
            Address = "Some Street 15",
            Phone = "1234567890"
        },
        "Customers",
        DbSettings.MSSQL
    );
}

 


你要意识到,Emit Mapper也能作为一个“超级ORM”工具,你能够基于他开发一套适用于你的项目的数据访问层工具类库。有时,他比使用ORM工具更加的高效。

 

posted @ 2014-11-05 15:26  争世不悔  阅读(191)  评论(0编辑  收藏  举报