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