将一个完整的lambda表达式作为参数发送,包括:where、orderby、take、skip。

25 浏览
0 Comments

将一个完整的lambda表达式作为参数发送,包括:where、orderby、take、skip。

我正在编写一个分层的ASP.Net应用程序,其中包括业务层、存储库层、服务层...。在存储库层中,我使用EntityFramework作为ORM。在服务层中,我想以Lambda形式传递一个查询(包括OrderBy或OrderByDescending,take,skip等),并在DbSet上运行查询并返回结果实体。

简单地说:(我如何在ASP.Net C#中编写以下模拟代码)

public class Repository
{
  public List findby(var query)
  {
    var dbcontext = DataContextFactory.GetDataContext();
    //以下代码应该执行这样的操作:dbcontext.Books.Where(B=>B.New==true && B.Id>99).OrderBy(B=>B.Date).ThenBy(B=>B.Id).Skip(2).Take(10);
    List matchedBooks = RunQueryOnBooks(dbcontext.Books,query); 
    return matchedBooks;
  }
}
public class Service
{
  public List getTopNewBooks(Repository _repository)
  {
    var query = Where(B=>B.New==true && B.Id>99).OrderBy(B=>B.Date).ThenBy(B=>B.Id).Skip(2).Take(10);
    List matchedBooks = _repository.findby(query);
    return matchedBooks;
  }
}

那么问题是:

- 我应该使用什么类型来代替var来声明查询(如果有的话)?

- 如何在dbcontext.Books上执行查询?

请给出一个像我这样的好的简单示例和更多的参考资料。提前谢谢。

0
0 Comments

如果你仔细思考一下,你说的是你想要将一些书籍的集合作为输入,并返回一些经过筛选/排序的书籍集合。因此,查询的适当类型应该是Func<IEnumerable<Books>, IEnumerable<Books>

基于这个想法,我可能会将查询作为一个函数(getTopNewBooks)而不是一个lambda表达式,但两者都可以。无论哪种方式,你都可以将委托方法传递给findby函数,例如:

public class Application
{
  public static void Main()
  {
    var repo = new Repository();
    foreach (var topBook in repo.findby(Service.getTopNewBooks))
    {
      // This is a matching top book
    }
  }
}
public class Repository
{
  public List<Book> findby(Func<IEnumerable<Book>,IEnumerable<Book>> query)
  {
    var dbcontext = DataContextFactory.GetDataContext();
    List<Book> matchedBooks = query(dbcontext.Books).ToList(); 
    return matchedBooks;
  }
}
public class Service
{
  public static IEnumerable<Book> getTopNewBooks(IEnumerable<Book> input)
  {
    return input.Where(B=>B.New==true && B.Id>99).OrderBy(B=>B.Date).ThenBy(B=>B.Id).Skip(2).Take(10);
  }
}

在这个例子中,我们定义了一个Application类,其中包含一个Main方法作为程序的入口点。我们还定义了一个Repository类,其中包含了一个findby方法,该方法接受一个Func<IEnumerable<Book>,IEnumerable<Book>>类型的查询作为参数,并返回匹配的书籍集合。最后,我们还定义了一个Service类,其中包含了一个名为getTopNewBooks的静态方法,该方法接受一个IEnumerable<Book>类型的输入,并返回经过筛选/排序的书籍集合。

通过将Service.getTopNewBooks作为参数传递给repo.findby方法,我们可以使用getTopNewBooks方法中定义的lambda表达式来筛选和排序书籍集合。在这个例子中,lambda表达式使用Where方法筛选出满足条件B.New==true && B.Id>99的书籍,然后使用OrderByThenBy方法按日期和Id进行排序,最后使用SkipTake方法跳过2个元素并取出10个元素。

通过这种方式,我们可以将lambda表达式作为整体传递给findby方法,并在Repository类中使用它来过滤和排序书籍集合。这种方法的好处是可以将查询逻辑封装在一个可复用的方法中,使代码更加清晰和可维护。

0
0 Comments

问题的原因是LINQ使用Lambda表达式作为参数,但在某些情况下需要将整个Lambda表达式作为参数传递给另一个函数。在给定的示例中,需要将包含where、orderby、take和skip操作的完整Lambda表达式作为参数传递给Func。

要解决这个问题,可以创建一个Func,该Func返回适当的类型,并且接受适当的参数类型。然后,可以将包含所需操作的完整Lambda表达式作为参数传递给该Func,并调用该Func来执行操作。

在给定的示例中,创建了一个Func,该Func接受一个IEnumerable类型的参数,并返回一个IEnumerable类型的结果。Lambda表达式中的where操作被替换为一个始终返回true的条件。然后,可以将这个Func应用到一个空的IEnumerable对象上,以获取结果。

整个Lambda表达式作为参数传递给Func的代码如下所示:

var myFunc = new Func<IEnumerable<Book>, IEnumerable<Book>>(book => book.Where(i => true /* rest of your code here */));
var test = Enumerable.Empty<Book>();
var result = myFunc(test);

0
0 Comments

问题的出现原因:

该问题是由于需要在服务层(service layer)向数据访问层(repository layer)发送查询(query)时遇到的困扰。服务层需要将包含where、orderby、take和skip的完整lambda表达式作为参数传递给数据访问层的方法。然而,最初的解决方法中,函数接受的参数类型为Func<IEnumerable<Book>, IEnumerable<Book>>,而不是能够将lambda表达式转换为SQL并且只返回10条记录的IQueryable<Book>类型。

解决方法:

为了解决这个问题,首先将函数接受的参数类型改为Func<IQueryable<Book>, IQueryable<Book>>,并将query(set)改为query(set.AsQueryable())。这样做的好处是使用IQueryable而不是IEnumerable,可以将lambda表达式转换为SQL并且只返回10条记录,从而提高性能。

关于这种方法的适用性:

对于将查询从服务层传递到数据访问层的设计模式,这种方法是可行的。它允许在领域服务(domain service)中发送查询(Sql/Linq)到数据上下文/表,然后将查询转换为数据库语言并将结果返回到领域服务。然而,这种方法可能会破坏数据访问层的封装性,因为服务层可以运行任何Sql/Linq查询。另一方面,这种数据访问层对于其使用者来说是灵活的。

最后,虽然这个解决方案被接受,但在编辑答案时应根据评论修改代码。将Func<IEnumerable<Book>, IEnumerable<Book>>改为Func<IQueryable<Book>, IQueryable<Book>>,并将query(set)改为query(set.AsQueryable())

0