C# UI层是否应该能够将lambda表达式传递到服务层,而不是调用特定的方法?

C# UI层是否应该能够将lambda表达式传递到服务层,而不是调用特定的方法?,c#,asp.net,.net,linq,architecture,C#,Asp.net,.net,Linq,Architecture,我正在处理的ASP.NET项目有3层;UI、BLL和DAL。我想知道UI是否可以将lambda表达式传递给BLL,或者UI是否应该传递参数,服务方法是否应该使用这些参数来构造lambda表达式?下面是一个示例类,显示了这两个senarios public class JobService { IRepository<Job> _repository; public JobService(IRepository<Job> repository)

我正在处理的ASP.NET项目有3层;UI、BLL和DAL。我想知道UI是否可以将lambda表达式传递给BLL,或者UI是否应该传递参数,服务方法是否应该使用这些参数来构造lambda表达式?下面是一个示例类,显示了这两个senarios

public class JobService 
{
    IRepository<Job> _repository;

    public JobService(IRepository<Job> repository) 
    {
        _repository = repository;
    }

    public Job GetJob(int jobID)
    {
        return _repository.Get(x => x.JobID == jobID).FirstOrDefault();
    }

    public IEnumerable<Job> Get(Expression<Func<Job, bool>> predicate)
    {
        return _repository.Get(predicate);
    }
}
公共类就业服务
{
i存储库;
公共就业服务(IRepository repository)
{
_存储库=存储库;
}
公共作业GetJob(int jobID)
{
return _repository.Get(x=>x.JobID==JobID).FirstOrDefault();
}
公共IEnumerable Get(表达式谓词)
{
return _repository.Get(谓词);
}
}
对于上述类,UI是否可以调用以下内容:

JobService jobService = new JobService(new Repository<Job>());
Job job = jobService.Get(x => x.JobID == 1).FirstOrDefault();
JobService JobService=newjobservice(newrepository());
Job Job=jobService.Get(x=>x.JobID==1.FirstOrDefault();
还是只允许它调用GetJob(intjobid)


这是一个简单的示例,我的问题是,UI层是否应该能够将lambda表达式传递到服务层,而不是调用特定的方法?

这是一个基于情况的判断调用。像这样传入谓词并不一定是错误的。我认为这应该被认为是一种轻微的异味

如果lambda表达式的传入允许您将6个方法减少到1个,那么这可能是一个好的举措。另一方面,如果您可以同样轻松地传入一个简单类型,那么lambda语法是一个不必要的复杂问题

在上面的例子中,由于不知道上下文,我倾向于使用一个简单的整数参数。通常应该有一个基本的方法,只通过记录的ID来获取记录。也许还有一个或两个其他的方法可以通过应用程序重复使用。然后可能是一个通用的方法,需要一个λ

你也应该考虑一些建议应该是一条规则:你在你的UI和你的业务层之间没有任何带有lambda指定谓词的方法。(有些人有理由相信,你的存储库甚至不应该有这样的方法!)我不认为这应该是一条铁一般的规则,但有充分的理由这样做。在它们之间的业务层和数据层应该可以防止发生危险的查询。如果您允许传入lambdas,那么对于UI层中的初级开发人员来说,很容易指定能够真正访问数据库的查询。(例如,他们将对非索引字段执行大量查询,和/或使用LINQ to对象对结果集进行筛选,但他们没有意识到这是多么低效。)

与许多其他良好实践一样,这在某种程度上取决于范围。在我最近的大型应用程序中,我没有将lambda语法从UI层传递到业务层。我的计划是在业务层进行大量投资,使其变得非常智能。它具有所有需要的简单类型的方法。事实上,它通常通过简单的域对象属性提供所需的内容,而不需要任何参数。我的接口确保UI只能导致高效查询的发生,在UI层中可能只有较小的LINQ to Objects谓词。(这甚至适用于“搜索”页面。我的业务层接受可能性有限的criteria对象,并确保高效查询。)


现在,你说的是“层”,而不是“层”。那么这些都在同一个组件中?lambda的另一个缺点是它们(目前)很难序列化。所以如果你不得不分开你的层次,你会后悔的。

我不会那样做。这是个坏习惯。这是合法的,但不好


您正在将DAL逻辑添加到将DAL作为包装器呈现的UI中。您的UI应该只将参数传递给BLL或DAL,而不执行获取数据的逻辑。

这只是我的观点,但您尝试编写的内容与LINQ表达式的编写方式类似,因此,如果它对LINQ足够好,为什么不呢


如果你认为这会让你的工作更轻松,那么我不明白你为什么不应该这么做。我唯一要小心的是确保它易于维护,即能够跟踪您正在传递的所有lambda。

假设您认为它可能不合适的原因是它可能会违反BLL的封装,我将使用它取决于

和是基本的语言结构,因此使用它们的简单行为本身不会破坏任何封装

当然,例外情况是,作为参数传递给方法的委托的签名公开了一个不应直接从调用方作用域中访问的类型。让我举个例子:

// Bad
public IEnumerable<Job> Get(Action<SqlCommand> queryCommand);

// Good
public IEnumerable<Job> Get(Func<Job, bool> predicate);
//坏的
公共IEnumerable Get(操作查询命令);
//好
公共IEnumerable Get(Func谓词);
在第一个示例中,方法签名通过以
SqlCommand
类型的形式向调用方公开实现细节来打破封装。它还允许调用方将任何查询指定为参数,从而赋予调用方过多的自由,这显然是危险的


另一方面,在第二个示例中,方法签名simple公开了一个模型类,它已经是层的公共接口的一部分。它不会增加API的表面积,也不会公开有关如何实现操作的任何细节。

对于公开表达式树(和
IQueryable
s)应限于哪一层,这始终是一个没有意义的问题

因此,首先,允许
IQueryable
lambda进入一个层的主要好处是明显地允许查询具有极大的灵活性,而无需编写大量的
GetXXXByY