摘要 在上一篇文章中,我们比较了ADO.net和其他Data Provider,以提高应用程序数据访问层的性能 。下面我们的主题将是采用一些常用的设计模式来使用ORM(通常是N-hibernateEF)建立数据访问层---UnitOfWorkRepository设计模式和SOA

其实ORM不应该属于这个系列的范围,因为它不会帮助改善我们的第一个应用程序的性能。ORM并不适合快速数据交易系统(要求有很好的实时性)


我们经常会遇到一个数据库事务,在不同的数据表上有操作需要,UnitOfWork结合Repository模式是通用的设计方法,例如:

我们需要建立一个订单服务,其中有一个方法名为 CreateNewOrder

  • 它接收参数类型为OrderInfo 一个值对象(其中引用另一个名为CustomerInfo的父对象)
  • 该方法将在一个事务中保存新的order和新的Customer(如果它是一个新的Customer,并构造它们的关联关系。 


Unit of work



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 FrameworkObjectContext 来实现简单的UnitOfWork的功能

这些类之间的关系比较简单,

  1. OrderService创建一个新的OrderEntities实例来模拟和启动UnitOfWork
  2. UnitOfWork实例要传递给2repository (CustomerRepositoryOrderRepository)
  3. OrderService的实现依赖于两个repository的公开方法.
  4. UnitOfWork模式是通过EF中的object context来实现的
  5. Repository的功能封装了EF中的object context的基本CRUD操作
  6. 整个设计利用了代理设计模式(proxy design pattern)
  7. OrderService不存在也不应该对objectConext的直接引用和调用,在上图中我们也可以看到这点(在代码中我们的确做了一个Object Context的实例化,以后会解决这个问题)

核心代码

View Code
   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.");
        }
    }

 

测试结果:

结论

目前我们这个对UnitOfWorkRepository模式的实现存在一些问题。

  1. 对于每个domain object,我们需要创建一个新的repository,并且repository的大部分代码的看起来都一样,复用性非常差。我们设计的Repository最好能支持泛型的domain object,将基本实现(CRUD)放在基类中以便具体的repository继承和扩展。
  2. UnitOfWork实现和EFobject context耦合太紧,依赖于一个具体的object context实例。 如果我们要实现N-hibernate​​UnitOfWork,那么也许我们需要重写很多代码 
  3. 实现的UnitOfWork过于简单,需要扩展去支持更多的数据操作,例如批处理,分页查询等。
  4. UnitOfWork对事务的实现应该和UnitOfWork的代码应该分离。(用于以后其他类似数据库甚至是非数据库的事务的扩展)
  5. 缺乏对多数据库事务的支持。

接下来,我们将针对上面的问题推出优化后的设计和实现。使得整个架构具有更好的扩展性。

---P.S. 运行程序,请

(1)运行数据库脚本script.sql

(2)改变app.config文件中的连接字符串 

程序代码下载:

https://files.cnblogs.com/huyq2002/UnitOfWorkAnalysis.zip

posted on 2011-08-16 16:00  胡以谦  阅读(3799)  评论(1编辑  收藏  举报