2011-02-05 47 views
10

我需要能夠通過多個搜索字段搜索客戶帳戶。現在,我的存儲庫中有我的搜索邏輯。搜索邏輯包括一些過濾,感覺更像是它屬於域層,但這意味着使用IQueryable之類的東西,我也不確定我喜歡那種。哪個更好?在存儲庫或域級服務中有複雜的搜索邏輯(通過IQueryable或其他)?

例如,現在我有了所有字段搜索類利用它用戶可以搜索:

public class AccountSearch 
{ 
    public decimal Amount { get; set; } 
    public string CustomerId { get; set; } 
    public string Address { get; set; } 
    public string CustomerName { get; set; } 
    public string City { get; set; } 
    public string PostalCode { get; set; } 
    public string Email { get; set; } 
    public string PhoneNumber { get; set; } 
    public string State { get; set; } 
} 

然後,我有一個簡單地將搜索類關閉的一個域級別的服務庫。我不喜歡它:

public class AccountsService : IAccountsService 
{ 
    private readonly IAccountRepository _accountRepository; 

    public AccountsService(IAccountRepository accountRepository) 
    { 
     _accountRepository = accountRepository;    
    } 

    public IEnumerable<Account> Search(AccountSearch accountSearch) 
    { 
     return _accountRepository.Search(accountSearch); 
    } 
} 

然後,我把所有的篩選邏輯在我的倉庫實現:

public class AccountRepository : IAccountRepository 
{ 
    private AccountDataContext _dataContext; 

    public AccountRepository(AccountDataContext entityFrameworkDataContext) 
    { 
     _dataContext = entityFrameworkDataContext; 
    } 

    public IEnumerable<Account> Search(AccountSearch accountSearch) 
    { 
     // My datacontext contains database entities, not domain entities. 
     // This method must query the data context, then map the database 
     // entities to domain entities. 

     return _dataContext.Accounts 
      .Where(TheyMeetSearchCriteria) 
      .Select(MappedAccounts); 
    } 

    // implement expressions here: 
    // 1. TheyMeetSearchCriteria filters the accounts by the given criteria 
    // 2. MappedAccounts maps from database to domain entities 
} 

不知道我是否應該感到好這個,或者我應該另找這樣實現搜索的方式。在這個情況下,你會怎麼做?

+0

請問有什麼AccountsService`這裏的`點全部似乎要做的是包裝`IAccountRepository`看似毫無用處 – 2011-02-05 15:35:26

+0

正確與往常一樣,爲了解決問題,我已經刪除了其他方法,而且你的問題和我的一樣,除了我寧願將搜索邏輯放在域名服務,我只是不知道如何最好地實現它。 – 2011-02-05 15:37:28

回答

3

爲什麼不從存儲庫本身公開IQueryable?這將允許任何LINQ查詢從請求代碼運行。

public class AccountRepository : IAccountRepository 
{ 
    AccountContext context = new AccountContext(); 

    public IQueryable<Account> GetItems() 
    { 
     return context.Accounts; 
    } 
} 

您可以AccountSearch負責根據其自身的邏輯建立查詢:

public class AccountSearch 
{ 
    public decimal Amount { get; set; } 
    public string CustomerId { get; set; } 
    public string Address { get; set; } 
    public string CustomerName { get; set; } 
    public string City { get; set; } 
    public string PostalCode { get; set; } 
    public string Email { get; set; } 
    public string PhoneNumber { get; set; } 
    public string State { get; set; } 

    public IQueryable<Account> BuildQuery (IQueryable<Account> source) 
    { 
     var query = source.Where (a => 
      a.Amount == Amount); 

     // you can use more twisted logic here, like applying where clauses conditionally 
     if (!string.IsNullOrEmpty (Address)) 
      query = query.Where (a => 
       a.Address == Address); 

     // ... 

     return query;  
    } 
} 

然後從客戶端代碼中使用它:

var filter = GetSearchFields(); // e.g. read from UI 
var allItems = repository.GetItems(); 

var results = filter.BuildQuery (allItems).ToList(); 

這僅僅是可能的一個但我喜歡它,因爲它允許在搜索過濾器類中使用複雜的邏輯。例如,您可能會在UI中使用不同的搜索類型,然後按不同的字段進行搜索。當使用這種模式時,這在AccountSearch中都是可以表達的。您可以使一些搜索字段成爲可選字段,因爲在本示例中我已使用Address。畢竟,您需要負責將客戶代碼中的查詢實際構建到AccountSearch,因爲它最瞭解搜索條件及其含義。

+0

Yessir,我很喜歡這個,我會試試看,我會讓你知道它是怎麼回事 – 2011-02-05 16:34:45

6

有很多技巧可以使用,其中最好的將取決於您的特定場景。

與其僅僅根據位置(例如在服務或域中)討論搜索邏輯,在規範位置和執行位置之間劃分區別可能更有幫助。根據規範的位置,我的意思是在哪些圖層中指定要搜索哪些字段。通過執行位置,我的意思是立即執行或延遲執行。

如果您有幾種互斥類型的搜索(例如,在場景A中,您希望通過CustomerId進行搜索,而在場景B中您想通過CustomerName進行搜索),則可以通過創建一個專用的存儲庫每種搜索類型的方法,或者在.Net中,你都可以使用LINQ表達式。例如:

特定領域的搜索方法,包括:在存儲庫

_customers.WithName("Willie Nelson") 

LINQ查詢執行的IQueryable:

_customers.Where(c => c.Name.Equals("Willie Nelson") 

前者允許更表現域而後者提供了更大的靈活性使用時間稍微縮短(可能會犧牲可讀性)。

對於更復雜的搜索條件需求,您可以使用您所描述的傳遞搜索條件集合(強類型或其他)的技術,也可以使用Specification Pattern。規範模式的優勢在於它提供了一種更富有表現力的,豐富的,豐富的查詢語言。一個示例性的使用可能是:

_customers.MeetingCriteria(
     Criteria.LivingOutsideUnitedStates.And(Criteria.OlderThan(55))) 

通過規範模式所提供的組合物可以通過NET的LINQ API被提供爲好,雖然有超過指定意向揭示代碼較少的控制。

就執行時間而言,可以通過編寫庫來提供延遲執行,方法是返回IQueryable,或者允許傳入LINQ表達式以通過存儲庫方法進行評估。例如:

遞延查詢:

var customer = (from c in _customers.Query() 
        where c.Name == "Willie Nelson" 
        select c).FirstOrDefault(); 

由執行查詢()方法:

var customer = 
    _customers.Query(q => from c in q 
          where c.Name == "Willie Nelson" 
          select c).FirstOrDefault(); 

它返回一個IQueryable前者查詢()方法具有稍微更容易測試的優點因爲Query()可以很容易地通過調用代碼來提供操作的集合,而後者具有更具確定性的優點。

=====編輯====

通過gaearon的做法的啓發,我決定修改我的回答有一個類似的技術。他的方法有些反向的規範模式,規範執行實際的查詢。這實際上使得它在自己的權利查詢,所以我們只把它叫做:

public class SomeClass 
{ 
    // Get the ICustomerQuery through DI 
    public SomeClass(ICustomerQuery customerQuery) 
    { 
     _customerQuery = customerQuery; 
    } 

    public void SomeServiceMethod() 
    { 
     _customerQuery() 
      .WhereLivingOutSideUnitedStates() 
      .WhereAgeGreaterThan(55) 
      .Select(); 
    } 
} 

所以,哪來的庫中,您可能會問?這裏我們不需要一個。我們ICustomerQuery可以一下就注射了一個IQueryable不過可實現你喜歡的(也許是國際奧委會的註冊,只是返回NHibernate的以下內容:?

_container.Resolve<ISession>().Linq<Customer>() 
相關問題