http://www.cnblogs.com/huyq2002/archive/2011/08/16/2140721.html
摘要 :在上一篇文章中,我们比较了ADO.net和其他Data Provider,以提高应用程序数据访问层的性能 。下面我们的主题将是采用一些常用的设计模式来使用ORM(通常是N-hibernate和EF)建立数据访问层---UnitOfWork,Repository设计模式和SOA。
其实ORM不应该属于这个系列的范围,因为它不会帮助改善我们的第一个应用程序的性能。ORM并不适合快速数据交易系统(要求有很好的实时性)。
我们经常会遇到一个数据库事务,在不同的数据表上有操作需要,UnitOfWork结合Repository模式是通用的设计方法,例如:
我们需要建立一个订单服务,其中有一个方法名为 CreateNewOrder
- 它接收参数类型为OrderInfo的 一个值对象(其中引用另一个名为CustomerInfo的父对象)
- 该方法将在一个事务中保存新的order和新的Customer(如果它是一个新的Customer),并构造它们的关联关系。
Unit of work-- Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.
A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work.
---Martin Flower
从Martin Flower的说明我们可以理解UnitOfWork的主要用途:
(1)管理事务 。
(2)实现数据的插入,删除和更新。
(3)防止重复更新。通过对保持对象的跟踪,防止多次提交数据库操作,提高性能。
Repository
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects
Repository模式主要用于实现业务层和数据访问层的隔离。这样做的好处是客户端代码(此处指业务层的service代码)不依赖于具体数据存储的实现。并且Repository只需要暴露必须的数据访问给Service层。
下面我们来看利用这两个模式针对我们的需求做的一个简单的实现(使用Entity Framework中ObjectContext 来实现简单的UnitOfWork的功能)
这些类之间的关系比较简单,
- OrderService创建一个新的OrderEntities实例来模拟和启动UnitOfWork
- UnitOfWork实例要传递给2个repository (CustomerRepository和OrderRepository)
- OrderService的实现依赖于两个repository的公开方法.
- UnitOfWork模式是通过EF中的object context来实现的
- Repository的功能封装了EF中的object context的基本CRUD操作
- 整个设计利用了代理设计模式(proxy design pattern)
- OrderService不存在也不应该对objectConext的直接引用和调用,在上图中我们也可以看到这点(在代码中我们的确做了一个Object Context的实例化,以后会解决这个问题)
核心代码
public interface IUnitOfWork
{
void Save();
}
public interface ICustomerRepository
{
Customer GetCustomerByName(string name);
Customer AddCustomer(Customer customer);
}
public interface IOrderRepository
{
Order GetOrderById(int id);
Order AddOrder(Order order);
}
public class CustomerRepository : ICustomerRepository
{
private OrderEntities _context;
public CustomerRepository(IUnitOfWork unitOfWork)
{
_context = unitOfWork as OrderEntities;
}
public Customer GetCustomerByName(string name)
{
return _context.Customers.Where(c => c.Name == name).FirstOrDefault();
}
public Customer AddCustomer(Customer customer)
{
_context.Customers.AddObject(customer);
return customer;
}
}
public class OrderRepository : IOrderRepository
{
private OrderEntities _context;
public OrderRepository(IUnitOfWork unitOfWork)
{
_context = unitOfWork as OrderEntities;
}
public Order GetOrderById(int id)
{
return _context.Orders.Where(c => c.Id == id).FirstOrDefault();
}
public Order AddOrder(Order order)
{
_context.Orders.AddObject(order);
return order;
}
}
public class CustomerInfo
{
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
public class OrderInfo
{
public int Id { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public CustomerInfo CustomerInfo { get; set; }
}
public static class CustomerTranslator
{
public static Customer Translate(CustomerInfo customerInfo)
{
return new Customer { Id = customerInfo.Id, Name = customerInfo.Name, Address = customerInfo.Address };
}
}
public static class OrderTranslator
{
public static Order Translate(OrderInfo orderInfo)
{
return new Order { Id = orderInfo.Id, Quantity = orderInfo.Quantity, Price = orderInfo.Price };
}
}
public class OrderService
{
public void CreateNewOrder(OrderInfo orderInfo)
{
IUnitOfWork unitOfWork = new OrderEntities();
CustomerRepository customerRepository = new CustomerRepository(unitOfWork);
Customer customer = customerRepository.GetCustomerByName(orderInfo.CustomerInfo.Name);
if (customer == null)
{
customer = customerRepository.AddCustomer(CustomerTranslator.Translate(orderInfo.CustomerInfo));
}
OrderRepository orderRepository = new OrderRepository(unitOfWork);
Order order = OrderTranslator.Translate(orderInfo);
order.Customer = customer;
orderRepository.AddOrder(order);
unitOfWork.Save();
}
}
class Program
{
static void Main(string[] args)
{
OrderService orderService = new OrderService();
CustomerInfo c1 = new CustomerInfo();
c1.Name = "Demo Customer";
c1.Address = "Demo Address";
OrderInfo orderInfo = new OrderInfo();
orderInfo.CustomerInfo = c1;
orderInfo.Price = 200;
orderInfo.Quantity = 5;
orderService.CreateNewOrder(orderInfo);
Console.Write("new order saved.");
}
}
测试结果:
结论
目前我们这个对UnitOfWork和Repository模式的实现存在一些问题。
- 对于每个domain object,我们需要创建一个新的repository,并且repository的大部分代码的看起来都一样,复用性非常差。我们设计的Repository最好能支持泛型的domain object,将基本实现(CRUD)放在基类中以便具体的repository继承和扩展。
- UnitOfWork实现和EF的object context耦合太紧,依赖于一个具体的object context实例。 如果我们要实现N-hibernate的UnitOfWork,那么也许我们需要重写很多代码 。
- 实现的UnitOfWork过于简单,需要扩展去支持更多的数据操作,例如批处理,分页查询等。
- UnitOfWork对事务的实现应该和UnitOfWork的代码应该分离。(用于以后其他类似数据库甚至是非数据库的事务的扩展)
- 缺乏对多数据库事务的支持。
接下来,我们将针对上面的问题推出优化后的设计和实现。使得整个架构具有更好的扩展性。
---P.S. 运行程序,请
(1)运行数据库脚本script.sql
(2)改变app.config文件中的连接字符串 。
程序代码下载: