1

我已经继承了一个系统,它使用Castle Windsor IRepository模式从LinqToSQL的DAL中抽象出来。更改IRepository以支持IQueryable(LINQtoSQL查询)

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

当前用法如下

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

这采用拉姆达过滤,像这样

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

目前的问题是执行,在我们的例子上面,调用'GetEnumerator'时,它会将所有行加载到内存中,然后查找我们需要的行。

如果我更改IRepository来实现IQueryable,我无法实现所需的三个方法,因为这些方法在Table类中不是公有的。

我想我应该改变SQLClientRepository像这样

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

被定义,然后实施必要的方法,但我不能想出如何绕过等表述,因为它们是私有成员表类,像这样

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”!

回答

0

,虽然它可能不是一个真正的抽象,主要的一点是要获得linq to sql的好处,而不需要更新所有已经写好的查询。

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

然后在SqlClientRepository实现中,我可以调用AsQueryable()将Table强制转换为IQueryable,然后一切都很好,就像这样。现在无处不在有人写IRepository()。其中​​(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(); 
    } 
0

如果您想公开linq,您可以停止使用存储库模式并直接使用Linq2Sql。这是因为每个Linq To Sql提供者都有自己的定制解决方案。所以如果你暴露LINQ你会得到一个漏洞抽象。然后使用抽象层没有意义。

反而暴露LINQ,你有两个选择:

  1. 因为我在这里描述实现该规范模式
  2. 使用存储库模式:所以http://blog.gauffin.org/2013/01/repository-pattern-done-right/
+0

谢谢你。实际上,这正是我在某些地方所做的,但在这方面,我希望获得好处,而不必重写我们已经完成的所有事情。我会看看你的文章。谢谢。 – adudley

+0

还有其他问题吗? – jgauffin