使用 LINQ-to-SQL 实现 Repository 模式





4.00/5 (17投票s)
本文的目的是描述我用于在 .NET 应用程序中实现存储库模式的技术。
引言
本文的目的是描述我用于在 .NET 应用程序中实现存储库模式的技术。 我将简要描述存储库模式和 LINQ-to-SQL; 但是,如果您不熟悉这些技术,您应该在其他地方研究它们。 我实现的目的是
- 它必须是一个通用设计,可以重复用于许多项目
- 它必须促进领域驱动设计
- 它必须促进单元测试和一般测试
- 它必须允许领域模型避免对基础设施的依赖
- 它必须提供强类型查询
仓储模式
根据 Martin Fowler 的说法,存储库模式提供了“查询构造代码集中的映射层之上的抽象层”,以“最大限度地减少重复的查询逻辑”。 实际上,它通常是数据访问服务的集合,其分组方式与领域模型类相似。
通过经由接口访问存储库,存储库模式有助于打破领域模型和数据访问代码之间的依赖关系。 这对于单元测试非常宝贵,因为可以隔离域模型。
我通过为每个需要专门数据访问方法(除了标准的创建、读取、更新和删除之外)的域模型实体定义一个存储库类来实现存储库模式。 如果实体不需要专门的数据访问方法,那么我将使用该实体的通用存储库。 存储库类包含其对应的域模型实体所需的专门数据访问方法。
以下类图显示了一个示例实现,其中包含两个域实体类:Shape
和 Vertex
。 Shape
有一个专门的存储库 (IShapeRepository
)。 Vertex
没有专门的存储库,因此它只会使用通用存储库 (IRepository<Vertex>
)。
LINQ-to-SQL
LINQ 是一种强类型的数据查询方式。 LINQ-to-SQL 是 LINQ 的一种方言,允许查询 SQL Server 数据库。 它还包括对象/关系映射以及用于从数据库架构生成域模型类的工具。 LINQ 是对象/关系映射工具的一个极好的补充,因为它有助于实现强类型查询,例如
IList<Shape> threeSidedShapes =
_genericShapeRepository.FindAll(shape =>
shape.NumberOfSides == 3).Take(5).ToList();
IRepository<T>
泛型接口 IRepository<T>
定义了每个存储库上需要的方法。
public interface IRepository<T> where T : class
{
/// <summary>
/// Return all instances of type T.
/// </summary>
/// <returns></returns>
IEnumerable<T> All();
/// <summary>
/// Return all instances of type T that match the expression exp.
/// </summary>
/// <param name="exp"></param>
/// <returns></returns>
IEnumerable<T> FindAll(Func<T, bool> exp);
/// <summary>Returns the single entity matching the expression.
/// Throws an exception if there is not exactly one such entity.</summary>
/// <param name="exp"></param><returns></returns>
T Single(Func<T, bool> exp);
/// <summary>Returns the first element satisfying the condition.</summary>
/// <param name="exp"></param><returns></returns>
T First(Func<T, bool> exp);
/// <summary>
/// Mark an entity to be deleted when the context is saved.
/// </summary>
/// <param name="entity"></param>
void MarkForDeletion(T entity);
/// <summary>
/// Create a new instance of type T.
/// </summary>
/// <returns></returns>
T CreateInstance();
/// <summary>Persist the data context.</summary>
void SaveAll();
}
Repository<T>
IRepository<T>
由一个泛型存储库基类 Repository<T>
实现。 Repository<T>
是一个基本实现,它为所有实体提供数据访问功能。 如果实体 (T
) 不需要专门的存储库,则其数据访问将通过 Repository<T>
完成。
public class Repository<T> : IRepository<T>
where T : class
{
protected IDataContextFactory _dataContextFactory;
/// <summary>
/// Return all instances of type T.
/// </summary>
/// <returns></returns>
public IEnumerable<T> All()
{
return GetTable;
}
/// <summary>
/// Return all instances of type T that match the expression exp.
/// </summary>
/// <param name="exp"></param>
/// <returns></returns>
public IEnumerable<T> FindAll(Func<T, bool> exp)
{
return GetTable.Where<T>(exp);
}
/// <summary>See IRepository.</summary>
/// <param name="exp"></param><returns></returns>
public T Single(Func<T, bool> exp)
{
return GetTable.Single(exp);
}
/// <summary>See IRepository.</summary>
/// <param name="exp"></param><returns></returns>
public T First(Func<T, bool> exp)
{
return GetTable.First(exp);
}
/// <summary>See IRepository.</summary>
/// <param name="entity"></param>
public virtual void MarkForDeletion(T entity)
{
_dataContextFactory.Context.GetTable<T>().DeleteOnSubmit(entity);
}
/// <summary>
/// Create a new instance of type T.
/// </summary>
/// <returns></returns>
public virtual T CreateInstance()
{
T entity = Activator.CreateInstance<T>();
GetTable.InsertOnSubmit(entity);
return entity;
}
/// <summary>See IRepository.</summary>
public void SaveAll()
{
_dataContextFactory.SaveAll();
}
public Repository(IDataContextFactory dataContextFactory)
{
_dataContextFactory = dataContextFactory;
}
#region Properties
private string PrimaryKeyName
{
get { return TableMetadata.RowType.IdentityMembers[0].Name; }
}
private System.Data.Linq.Table<T> GetTable
{
get { return _dataContextFactory.Context.GetTable<T>(); }
}
private System.Data.Linq.Mapping.MetaTable TableMetadata
{
get { return _dataContextFactory.Context.Mapping.GetTable(typeof(T)); }
}
private System.Data.Linq.Mapping.MetaType ClassMetadata
{
get { return _dataContextFactory.Context.Mapping.GetMetaType(typeof(T)); }
}
#endregion
}
IShapeRepository 和 ShapeRepository
通常需要为实体类提供更专门的存储库。 如果我们的域包含一个 shape 实体,我们可能希望有一个 ShapeRepository
,其中包含一个 RetrieveByNumberOfSides(int sideCount)
方法。 这样的类将作为专门的接口 IShapeRepository
公开给消费者
public interface IShapeRepository : IRepository<Shape>
{
Shape RetrieveByNumberOfSides(int sideCount)
}
public class ShapeRepository : Repository<Shape>, IShapeRepository
{
public Shape RetrieveByNumberOfSides(int sideCount)
{
return FindAll(shape => shape.NumberOfSides == sideCount);
}
}
用法
我们现在有了一个功能齐全、解耦的存储库实现。 一个类可能会按以下方式使用存储库
public class ApplicationService
{
private IRepository<Shape> _genericShapeRepository;
private IShapeRepository _specializedShapeRepository;
public ApplicationService(IRepository<Shape> genericShapeRepository,
IShapeRepository specializedShapeRepository)
{
_genericShapeRepository = genericShapeRepository;
_specializedShapeRepository = specializedShapeRepository;
}
public void DoSomethingWithTheGenericRepository()
{
IList<Shape> threeSidedShapes =
_genericShapeRepository.FindAll(shape => shape.NumberOfSides == 3).ToList();
_genericShapeRepository.MarkForDeletion(threeSidedShapes[0]);
_genericShapeRepository.SaveAll();
}
public void DoSomethingWithTheSpecializedRepository()
{
IList<Shape> threeSidedShapes =
_specializedShapeRepository.RetrieveByNumberOfSides(3).ToList();
_specializedShapeRepository.MarkForDeletion(threeSidedShapes[0]);
_specializedShapeRepository.SaveAll();
}
}