1

我继承了一个系统,该系统使用 Castle Windsor IRepository 模式从 DAL 中抽象出来,即 LinqToSQL。

我可以看到的主要问题是 IRepository 只实现了 IEnumerable。因此,即使是最简单的查询也必须从数据表中加载所有数据,才能返回单个对象。

目前使用情况如下

using (IUnitOfWork context2 = IocServiceFactory.Resolve<IUnitOfWork>())
       {
           KpiFormDocumentEntry entry = context2.GetRepository<KpiFormDocumentEntry>().FindById(id, KpiFormDocumentEntry.LoadOptions.FormItem);

这使用 lambda 过滤,就像这样

public static KpiFormDocumentEntry FindById(this IRepository<KpiFormDocumentEntry> source, int id, KpiFormDocumentEntry.LoadOptions loadOptions)
    {
        return source.Where( qi => qi.Id == id ).LoadWith( loadOptions ).FirstOrDefault();
    }

所以它成为一个很好的扩展方法。

我的问题是,我怎样才能使用相同的接口/模式等,同时实现 IQueryable 以正确支持 LinqToSQL 并获得一些严重的性能改进?

IRepository 的当前实现/接口如下

public interface IRepository<T> : IEnumerable<T> where T : class
{

    void Add(T entity);

    void AddMany(IEnumerable<T> entities);

    void Delete(T entity);


    void DeleteMany(IEnumerable<T> entities);

    IEnumerable<T> All();

    IEnumerable<T> Find(Func<T, bool> predicate);

    T FindFirst(Func<T, bool> predicate);
}

然后这是由像这样的 SqlClientRepository 实现的

public sealed class SqlClientRepository<T> : IRepository<T> where T : class
{
    private readonly Table<T> _source;

    internal SqlClientRepository(Table<T> source)
    {
        if( source == null ) throw new ArgumentNullException( "source", Gratte.Aurora.SHlib.labelText("All_TableIsNull",1) );
        _source = source;
    }

    //removed add delete etc 

    public IEnumerable<T> All()
    {
        return _source;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _source.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

目前的问题是,在上面的示例中,.Where 正在调用“GetEnumerator”,然后将所有行加载到内存中,然后查找我们需要的行。

如果我更改 IRepository 以实现 IQueryable,我将无法实现所需的三个方法,因为它们在 Table 类中不是公共的。

我想我应该将 SQLClientRepository 更改为这样定义

public sealed class SqlClientRepository<T> : IQueryable<T>, IRepository<T> where T : class

然后实现必要的方法,但我不知道如何传递表达式等,因为它们是 Table 类的私有成员,就像这样

public override Type ElementType
    {
        get { return _source.ElementType; } //Won't work as ElementType is private
    }

    public override Expression Expression
    {
        get { return _source.Expression; } //Won't work as Expression is private
    }

    public override IQueryProvider Provider
    {
        get { return _source.Provider; } //Won't work as Provider is private
    }

任何帮助都非常感谢将其从“加载后遍历数据库中的每一行”移动到“选择 x 其中 id=1”!

4

2 回答 2

0

如果您想公开 linq,您可以停止使用存储库模式并直接使用 Linq2Sql。原因是每个 Linq To Sql 提供程序都有自己的自定义解决方案。因此,如果您公开 LINQ,您会得到一个泄漏的抽象。那么使用抽象层就没有意义了。

您有两个选择,而不是公开 LINQ:

  1. 实现规范模式
  2. 使用我在这里描述的存储库模式:http: //blog.gauffin.org/2013/01/repository-pattern-done-right/
于 2013-02-06T11:02:54.590 回答
0

因此,虽然它可能不再是一个真正的抽象,但重点是在不更新所有已编写的查询的情况下获得 linq to sql 的好处。

所以,我让 IRepository 实现了 IQueryable 而不是 IEnumerable。

然后在SqlClientRepository实现中,我可以调用AsQueryable()将Table强制转换为IQueryable,然后一切就好了,像这样。

现在到处有人写了 IRepository().Where(qi => qi.id = id) 或类似的东西,它实际上将 ID 传递给 sql server 并且只拉回一条记录,而不是全部,然后循环查找正确的一个。

/// <summary>Provides the ability to query and access entities within a SQL Server data store.</summary>
/// <typeparam name="T">The type of entity in the repository.</typeparam>
public sealed class SqlClientRepository<T> : IRepository<T> where T : class
{
    private readonly Table<T> _source;
    private readonly IQueryable<T> _sourceQuery;

    IQueryable<T> Query()
    {
        return (IQueryable<T>)_source;
    }

    public Type ElementType
    {
        get { return _sourceQuery.GetType(); }
    }

    public Expression Expression
    {
        get { return _sourceQuery.Expression; }
    }

    public IQueryProvider Provider
    {
        get { return _sourceQuery.Provider; }
    }



    /// <summary>Initializes a new instance of the <see cref="SqlClientRepository{T}"/> class.</summary>
    /// <param name="source">A <see cref="Table{T}"/> to a collection representing the entities from a SQL Server data store.</param>
    /// <exception cref="ArgumentNullException"><paramref name="source"/> is a <c>null</c> reference (<c>Nothing</c> in Visual Basic).</exception>
    internal SqlClientRepository(Table<T> source)
    {
        if( source == null ) throw new ArgumentNullException( "source", "All_TableIsNull" ) );
        _source = source;
        _sourceQuery = _source.AsQueryable();
    }
于 2013-02-07T09:23:19.213 回答