65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (17投票s)

2008年6月5日

CPOL

2分钟阅读

viewsIcon

266411

downloadIcon

3354

本文的目的是描述我用于在 .NET 应用程序中实现存储库模式的技术。

引言

本文的目的是描述我用于在 .NET 应用程序中实现存储库模式的技术。 我将简要描述存储库模式和 LINQ-to-SQL; 但是,如果您不熟悉这些技术,您应该在其他地方研究它们。 我实现的目的是

  • 它必须是一个通用设计,可以重复用于许多项目
  • 它必须促进领域驱动设计
  • 它必须促进单元测试和一般测试
  • 它必须允许领域模型避免对基础设施的依赖
  • 它必须提供强类型查询

仓储模式

根据 Martin Fowler 的说法,存储库模式提供了“查询构造代码集中的映射层之上的抽象层”,以“最大限度地减少重复的查询逻辑”。 实际上,它通常是数据访问服务的集合,其分组方式与领域模型类相似。

通过经由接口访问存储库,存储库模式有助于打破领域模型和数据访问代码之间的依赖关系。 这对于单元测试非常宝贵,因为可以隔离域模型。

我通过为每个需要专门数据访问方法(除了标准的创建、读取、更新和删除之外)的域模型实体定义一个存储库类来实现存储库模式。 如果实体不需要专门的数据访问方法,那么我将使用该实体的通用存储库。 存储库类包含其对应的域模型实体所需的专门数据访问方法。

以下类图显示了一个示例实现,其中包含两个域实体类:ShapeVertexShape 有一个专门的存储库 (IShapeRepository)。 Vertex 没有专门的存储库,因此它只会使用通用存储库 (IRepository<Vertex>)。

Repository Diagram

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();
    }

}
© . All rights reserved.