概述
这里有许多的解决方案,来实际对象间数据的复制。最常见的应用场景就是生成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工具更加的高效。
浙公网安备 33010602011771号