c# – 哪个更好?在存储库或域级服务(通过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
}

不知道我是否应该觉得这样,或者我应该找到另一种方法来实现这样的搜索.在这个情况下,你会怎么做?

解决方法

您可以使用许多技术,其中最好的将取决于您的具体情况.

在位置方面(例如在服务或域中)仅仅讨论搜索逻辑,而不是在指定位置和执行位置之间区分起来更有帮助.按照规格位置,我的意思是你指定要搜索哪些字段的层次.通过执行位置,我的意思是立即执行或延迟执行.

如果您有几种相互排斥的搜索类型(例如,您想通过CustomerId搜索的方案A中,以及您想要通过CustomerName进行搜索的方案B),则可以通过为每个搜索创建专门的方法来实现特定于域的存储库键入,或在.Net中,您可以使用LINQ表达式.例如:

域专用搜索方式:

_customers.WithName("Willie Nelson")

对实现IQueryable的存储库进行LINQ查询:

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

由Query()方法执行:

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

返回IQueryable的前一个Query()方法具有稍微容易测试的优点,因为可以容易地将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,但是您可以实现(也许是一个IoC注册,只返回NHibernate的以下内容:

_container.Resolve<ISession>().Linq<Customer>()

相关文章

项目中经常遇到CSV文件的读写需求,其中的难点主要是CSV文件...
简介 本文的初衷是希望帮助那些有其它平台视觉算法开发经验的...
这篇文章主要简单记录一下C#项目的dll文件管理方法,以便后期...
在C#中的使用JSON序列化及反序列化时,推荐使用Json.NET——...
事件总线是对发布-订阅模式的一种实现,是一种集中式事件处理...
通用翻译API的HTTPS 地址为https://fanyi-api.baidu.com/api...