通用Linq具有特定的where / order by子句

时间:2013-11-20 09:56:06

标签: linq generics sitecore sitecore7

我想创建一个通用方法,允许我使用Linq到Sitecore(.Net 4.5)搜索Sitecore 7索引。

这将用于各种搜索,例如:

  • 获取前5个最新新闻页
  • 获取最近20个活动页面
  • 通用网站搜索
  • Etc等等

我可以创建一个非常通用的搜索方法,适用于所有类型的页面。它为Where子句提供了适用于所有类型搜索的通用模板谓词。

但是,对于上面的搜索,我还需要为Where子句添加特定谓词,并为Order By等添加特定表达式。目的是为每种类型的搜索创建子类,这将实现这些细节。

我已经压缩了一些代码,如下所示。在此我尝试为新页面搜索添加特定功能。

所有页面类都来自“Base”。

public virtual ReadOnlyCollection<T> Search<T>() where T : Base, new()
    {
        List<T> results = new List<T>();

        using (IProviderSearchContext context = ContentSearchManager.GetIndex("sitecore_web_index").CreateSearchContext())
        {
            Expression<Func<T, bool>> outerPredicate = PredicateBuilder.True<T>();

            // Create a predicate for the template id.
            Expression<Func<T, bool>> templatePredicate = PredicateBuilder.False<T>();
            templatePredicate = templatePredicate.Or(baseItem => (baseItem.TemplateIdFromIndex.Equals("8b1fc00c76314d32b8e1bce93dd41ccd")));

            // Create a predicate for a news page search.
            Expression<Func<NewsPageBase, bool>> datePredicate = PredicateBuilder.False<NewsPageBase>();
            datePredicate = datePredicate.And(newsPage => newsPage.ArticleDate < DateTime.Now);

            // 1. outerPredicate = outerPredicate.And(datePredicate);

            // 2. IQueryable<T> searchQuery = context.GetQueryable<T>().Where(outerPredicate).OrderByDescending(newsPage => newsPage.ArticleDate).Take(5);
            IQueryable<T> searchQuery = context.GetQueryable<T>().Where(outerPredicate);

            results = searchQuery.ToList();
        }

        return new ReadOnlyCollection<T>(results);
    }

此代码符合并运行。

但是,如果我取消注释标记为[1]的行,编译器会错误地使用特定的新闻页谓词来定义通用模板谓词。

错误是“方法的类型参数'Sitecore.ContentSearch.Linq.Utilities.PredicateBuilder.And(System.Linq.Expressions.Expression&gt;,System.Linq.Expressions.Expression&gt;)'无法从使用中推断出来”

如果我取消注释标记为[2]的行,则会出现类似的错误。

如何创建具有每种搜索类型特定功能的通用方法?

2 个答案:

答案 0 :(得分:2)

按如下方式修改代码可以实现您的需求..

public virtual ReadOnlyCollection<T> Search<T>() where T : Base, new()
    {
        List<T> results = new List<T>();

        using (IProviderSearchContext context = ContentSearchManager.GetIndex("sitecore_web_index").CreateSearchContext())
        {
            Expression<Func<T, bool>> outerPredicate = PredicateBuilder.True<T>();

            // Create a predicate for a news page search.
            Expression<Func<NewsPageBase, bool>> datePredicate = PredicateBuilder.True<NewsPageBase>();
            datePredicate = datePredicate.And(newsPage => newsPage.ArticleDate < DateTime.Now);

            //outerPredicate = outerPredicate.And((Expression<Func<T,bool>>)(object)datePredicate);
            outerPredicate = outerPredicate.And((Expression<Func<T, bool>>)(object)datePredicate);

            // 2. IQueryable<T> searchQuery = context.GetQueryable<T>().Where(outerPredicate).OrderByDescending(newsPage => newsPage.ArticleDate).Take(5);
            IQueryable<T> searchQuery = context.GetQueryable<T>().Where(outerPredicate);

            results = searchQuery.ToList();
        }

        return new ReadOnlyCollection<T>(results);
    }

我所做的改变是:

  1. 删除了未使用过的templatePredicate - 我不确定您是否打算使用此功能,或者在缩小原始代码时忘记将其删除。
  2. 使用PredicateBuilder.True而不是PredicateBuilder.False创建datePredicate - 这需要在.And()中使用,否则不会返回任何结果。
  3. 我(un)box datePredicate在outerPredicate.And()中使用。如果您尝试将datePredicate强制转换为Expression<Func<T, bool>>,但将其强制转换为object首先解决此问题,编译器会抱怨。

答案 1 :(得分:1)

我找到了解决方法。

而不是:

Expression<Func<NewsPageBase, bool>> datePredicate = PredicateBuilder.False<NewsPageBase>();
datePredicate = datePredicate.And(newsPage => newsPage.ArticleDate < DateTime.Now);

您可以使用:

Expression<Func<T, bool>> innerPredicate = PredicateBuilder.False<T>();
innerPredicate = innerPredicate.Or(item => ((DateTime)item[(ObjectIndexerKey)"article_date"] < DateTime.Now));

对于OrderBy子句,您可以使用:

searchQuery = searchQuery.OrderByDescending(item => item[(ObjectIndexerKey)"article_date"]);

如果您的基类是Sitecore类“SearchResultItem”的子类,那么这将有效。