在本系列文章中,将探讨如何开发一个使用WCF进行通信和NHibernate进行数据持久化的WPF客户端。本文将从高层次上讨论架构解决方案的范围,并概述文章的主要内容。
在上一章中,介绍了Repository的基本实现,它需要为域中的每个实体实现。正如所指出的,这个组件对于域层和需要广泛使用这些组件的服务的持久化至关重要。因此,优化这一领域的设计是一个好方法,以便可以在不牺牲业务逻辑与后端持久化实现之间的耦合的情况下,提供全面的服务水平。本章试图证明创建一种新型服务的必要性:仓库定位器。将看到泛型在这种模式中是如何变得不可或缺的。该模式致力于消除重复,并为服务和域实体保持一个简单的外观。
为每个实体提供具体的仓库是一种成本高昂的方式来为业务层提供持久化功能。需要一个更灵活的设计。当前的设计要求实体知道要使用的仓库类型。在上一章中,有:
public static Customer Create(IRepository<customer> repository, CustomerDto operation) {
...
}
尽管使用泛型接口似乎是合适的,但发现在创建内存实现时实现成本很高。如果有数十个实体,这是一种非常昂贵的方法。服务也受到同样问题的困扰,需要持有仓库的实例。
需要为域层和能够以透明方式使用的服务提供一个单一的实现,而不需要为每种实体类型开发单独的实现。还需要一个设计,它可以提供一个机制,用于在需要时对后端进行专门的调用,例如用于报告、性能等不同目的(尽管本章不涵盖这个方面)。提议的模式公开了通用方法,而不是通用类。它作为后端仓库的代理,为服务和实体提供了一个透明的机制来执行持久化调用。
public interface IRepositoryLocator {
#region CRUD operations
TEntity Save<TEntity>(TEntity instance);
void Update<TEntity>(TEntity instance);
void Remove<TEntity>(TEntity instance);
#endregion
#region Retrieval Operations
TEntity GetById<TEntity>(
long id);
IQueryable<TEntity> FindAll<TEntity>();
#endregion
IRepository<T> GetRepository<T>();
}
最后一个方法(GetRepository)在接口中并不真正需要,但对于基础实现至关重要,因此为了清晰起见,将在接口上公开它。
现在可以重构Customer实体以使用RepositoryLocator:
public class Customer : EntityBase {
protected Customer() { }
public virtual string FirstName { get; protected set; }
public virtual string LastName { get; protected set; }
public virtual string Telephone { get; protected set; }
public static Customer Create(IRepositoryLocator locator, CustomerDto operation) {
...
}
}
还用RepositoryLocator替换了服务中的Customer仓库:
在上一章中,定义了RepositoryEntityStore和RepositoryCustomer。希望为内存实现定义一个单一的IRepository实现,这对所有的实体都是有效的。不幸的是,内存实现需要一种生成实体PK的机制,这是NHibernate通过委托给后端数据库来解决的功能。因此,需要在实体中进行一些更改以便于一个通用的外观;声明了一个新的接口:
public interface IEntity {
long Id {
get;
}
}
正如之前提到的,假设所有的实体都有一个数字PK。创建了一个实现这个接口的抽象类:
public abstract class EntityBase : IEntity {
public virtual long Id {
get;
protected set;
}
}
所有实体都继承自这个基类,因此Customer类将看起来像:
讨论了上一章中使用的模式的一些问题,以及需要一个服务来提供持久化功能,而不需要在添加新实体时进行额外的工作。RepositoryLocator是一种简洁的方法,以优雅的方式在业务和持久化层之间提供了一个简单的抽象层。