2012-09-04 20 views
6

目前我正在建立一個使用sqlite的Windows應用程序。在數據庫中有一個表格,表示User,在我的代碼中有Repository<User>UserManager。我認爲這是一個非常普通的設計。在倉庫有一個List方法:返回可查詢<T>或列表<T>存儲庫中<T>

//Repository<User> class 
public List<User> List(where, orderby, topN parameters and etc) 
{ 
    //query and return 
} 

這帶來一個問題,如果我想要做一些複雜的UserManager.cs

//UserManager.cs 
public List<User> ListUsersWithBankAccounts() 
{ 
    var userRep = new UserRepository(); 
    var bankRep = new BankAccountRepository(); 
    var result = //do something complex, say "I want the users live in NY 
       //and have at least two bank accounts in the system 
} 

可以看到,返回List<User>帶來的性能問題,監守的查詢早於預期執行。現在我需要改變它的東西就像一個IQueryable<T>

//Repository<User> class 
public TableQuery<User> List(where, orderby, topN parameters and etc) 
{ 
    //query and return 
} 

TableQuery<T>是sqlite的驅動程序,這幾乎是EF等於IQueryable<T>,它提供了一個查詢並不會立即執行的一部分。但現在的問題是:在UserManager.cs中,它不知道什麼是TableQuery<T>,我需要在業務層項目中添加新的引用和導入命名空間,如using SQLite.Query。它確實帶來糟糕的代碼感覺。爲什麼我的業務層應該知道數據庫的細節?爲什麼業務層應該知道什麼是SQLite?什麼是正確的設計呢?

+0

你能不能「僅僅」在業務層和數據訪問層之間添加抽象的一個新的圖層(接口)?是的,這需要一組代表業務層中相同數據的對象,並在DA層和DA層之間進行映射 - 從某種意義上說,代碼重複 - 但它應該讓您在各層之間有更清晰的切換,如果我正確理解你的情況。 – Kjartan

+1

我同意@Kjartan,你真正想要的是IQueryable ,可惜SQLite沒有一個,值得創建另一個層來轉移到正確的類型。 – ivenxu

+0

@Kjartan @ivenxu:我沒有說'TableQuery '不正確,也沒有'IQueryable '是正確的。即使我添加了一個新的傳輸接口,如'IQuery ',它仍然* *基於*'TableQuery ',並且當我在業務層中使用'IQuery '時,所有內容都是相同的。它只是*看起來*與SQLite無關,但事實上,它戴着面具。 –

回答

2

我建議你使用IEnumerable<T>而非IQueryable<T>,它允許延遲加載了。IEnumerable不,但是,意味着你可以查詢任何方式的數據。您DB LINQ Provider可能會減少功能集。

0

延遲加載可以是一個PITA,我寧願嘗試將加載策略注入到存儲庫中,而不是試圖在應用程序或業務層中擺弄查詢對象。特別是當查詢對象迫使我緊緊地連接圖層時。

+0

「加載策略」是什麼意思? – ken2k

2

通常,在乾淨的架構中,數據查詢邏輯被封裝在存儲庫中。使用管道和過濾器可以幫助重用查詢邏輯。用數據層/存儲庫中的方法來包裝這些數據將更具可讀性和可維護性,再加上可重用。

例如,管道和過濾器來查詢用戶:

/// Pipes/Filters for user queries. 
public static class UserExtensions 
{ 
    public static IQueryable<User> Active(this IQueryable<User> query) 
    { 
     return query.Where(user => user.Active == true); 
    } 
} 

public class UserRepository : IRepository<User>, IUserRepository 
{ 
    /// Retrieve all users 
    public List<User> List() 
    { 
     // Logic to query all users in the database. 
    } 
    public List<User> ListActive() 
    { 
     // Logic to query all active users in the database. 
     return context.Users.Active().ToList(); 
    } 
} 

複雜的查詢需要理解它的目的和責任抽象查詢邏輯,它的存儲庫。舉例來說,「讓所有帳戶屬於該用戶」可在AccountRepository類寫成List<Account> ListForUser(int userId) { }

編輯:基礎上的評論,這裏是編寫檢索用戶生活在洛杉磯的搜索查詢的情況下誰至少有2個賬戶。

public class UserRepository : IRepository<User>, IUserRepository 
{ 
    // other queries. 

    public List<User> List(ISearchQuery query) 
    { 
     // Logic to query all active users in the database. 
     return context.Users.Active().LivesIn(query.Country).WithAccounts(query.AccountsAtLeast).ToList(); 
    } 
} 

public static class UserExtensions 
{ 
    // other pipes and filters. 

    public static IQueryable<User> LivesIn(this IQueryable<User> query, string country) 
    { 
     return query.Where(user => user.Country.Name == country); 
    } 
    public static IQueryable<User> WithAccounts(this IQueryable<User> query, int count) 
    { 
     return query.Where(user => user.Accounts.Count() >= count); 
    } 
} 
+0

你會在哪裏提出要求的邏輯:「詢問住在紐約的用戶並在系統中至少有兩個銀行賬戶」。 –

+0

這些查詢是用Repository編寫的,ofcoz可以相應地重構。是的,也有一個限制,因爲不是所有的查詢場景都可以被EF中的單個SQL查詢解析。在這種情況下,我們將不得不提出其他方法。 –

1

由於TableQuery<T>實現IEnumerable<T>,而不是IQueryable<T>,最好的解決辦法是簡單地改變你的資料庫界面返回IEnumerable<T>而不是TableQuery<T>。這不僅會打破SqlLite庫對客戶端的依賴關係,而且在您的界面中使用抽象IEnumerable<T>)而不是(TableQuery<T>)也是更好的設計。

你的實例方法應該是這樣的:

//Repository<User> class 
public IEnumerable<User> List(where, orderby, topN parameters and etc) 
{ 
    //query and return 
} 
相關問題