问题
Goal:
I have a widget that attempts to return blog posts that have matching tags to the current item.
Problem:
So I have acquired the current ContentItem in my widget and I have returned the TagsPart
of that ContentItem:
var itemTagsPart = _contentManager.Get<TagsPart>(currentContentItem.Id);
And I am now trying to create a query to return blog posts that have a tag record with a matching TagName.
var blogs = _contentManager.Query(VersionOptions.Published, "BlogPost")
.Join<TagsPartRecord>().Where(tpr => tpr.Tags.Any(tag => itemTagsPart.CurrentTags.Any(t => t.TagName == tag.TagRecord.TagName)))
.Slice(part.MaxPosts);
Unfortunately, on the predicate for filtering the returned TagsPartRecord records, I get a null reference exceptions. I haven't been able to reduce exactly which field would cause this, but I have added null checks in my predicate (above code has them all removed to keep it clean for here). Example
var blogs = _contentManager.Query(VersionOptions.Published, "BlogPost")
.Join<TagsPartRecord>().Where(tpr => tpr.Tags != null && tpr.Tags.Any(tag => tag != null && itemTagsPart.CurrentTags.Any(t => t.TagName != null && t.TagName == tag.TagRecord.TagName)))
.Slice(part.MaxPosts);
I have even tried exacting out the itemTagsPart.CurrentTags.ToList()
.
Below is the error I am getting. However, I read on this posted Question that you can't do this type of filter, at least not the way I am going about it. How can I filter the returned blog posts based on matching tagsParts?
Version: 1.7
The error:
Orchard.ContentManagement.Drivers.Coordinators.ContentPartDriverCoordinator - NullReferenceException thrown from IContentPartDriver by TrueFit.RelatedBlogPosts.Drivers.RelatedBlogPostsWidgetDriver
System.NullReferenceException: Object reference not set to an instance of an object.
at NHibernate.Linq.Visitors.WhereArgumentsVisitor.GetExistsCriteria(MethodCallExpression expr)
at NHibernate.Linq.Visitors.WhereArgumentsVisitor.VisitMethodCall(MethodCallExpression expr)
at NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
at NHibernate.Linq.Visitors.WhereArgumentsVisitor.GetExistsCriteria(MethodCallExpression expr)
at NHibernate.Linq.Visitors.WhereArgumentsVisitor.VisitMethodCall(MethodCallExpression expr)
at NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
at NHibernate.Linq.Visitors.ExpressionVisitor.VisitLambda(LambdaExpression lambda)
at NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
at NHibernate.Linq.Visitors.WhereArgumentsVisitor.VisitUnary(UnaryExpression expr)
at NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
at NHibernate.Linq.Visitors.RootVisitor.HandleWhereCall(MethodCallExpression call)
at NHibernate.Linq.Visitors.RootVisitor.VisitMethodCall(MethodCallExpression expr)
at NHibernate.Linq.Visitors.ExpressionVisitor.Visit(Expression exp)
at NHibernate.Linq.Visitors.NHibernateQueryTranslator.Translate(Expression expression, QueryOptions queryOptions)
at Orchard.ContentManagement.DefaultContentQuery.Where[TRecord](Expression`1 predicate) in c:\inetpub\Orchard\src\Orchard\ContentManagement\DefaultContentQuery.cs:line 89
at Orchard.ContentManagement.DefaultContentQuery.ContentQuery`2.Orchard.ContentManagement.IContentQuery<T,TR>.Where(Expression`1 predicate) in c:\inetpub\Orchard\src\Orchard\ContentManagement\DefaultContentQuery.cs:line 237
at TrueFit.RelatedBlogPosts.Drivers.RelatedBlogPostsWidgetDriver.Display(RelatedBlogPostsWidgetPart part, String displayType, Object shapeHelper)
at System.Dynamic.UpdateDelegates.UpdateAndExecute4[T0,T1,T2,T3,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3)
at Orchard.ContentManagement.Drivers.ContentPartDriver`1.Orchard.ContentManagement.Drivers.IContentPartDriver.BuildDisplay(BuildDisplayContext context) in c:\inetpub\Orchard\src\Orchard\ContentManagement\Drivers\ContentPartDriver.cs:line 27
at Orchard.ContentManagement.Drivers.Coordinators.ContentPartDriverCoordinator.<>c__DisplayClassa.<BuildDisplay>b__9(IContentPartDriver driver) in c:\inetpub\Orchard\src\Orchard\ContentManagement\Drivers\Coordinators\ContentPartDriverCoordinator.cs:line 47
at Orchard.InvokeExtensions.Invoke[TEvents](IEnumerable`1 events, Action`1 dispatch, ILogger logger) in c:\inetpub\Orchard\src\Orchard\InvokeExtensions.cs:line 17
回答1:
I'm working on a site where we filter blog posts based on a single tag name like this:
// Get the blog that contains the posts you want to filter
BlogPart blog = _contentManager.Query<BlogPart, BlogPartRecord>(VersionOptions.Published)
.Join<TitlePartRecord>()
.Where(t => t.Title == "your-blog-name")
.Slice(0, 1).FirstOrDefault();
// Query for blog posts that contain a tag called "my-tag"
IEnumerable<BlogPostPart> posts = _contentManager.Query(VersionOptions.Published, "BlogPost")
.Join<CommonPartRecord>()
.Where(cr => cr.Container == blog.Record.ContentItemRecord)
.Join<TagsPartRecord>()
.Where(tpr => tpr.Tags.Any(t => t.TagRecord.TagName == "my-tag"))
.WithQueryHints(new QueryHints().ExpandRecords<TagsPartRecord>().ExpandParts<TagsPart>())
.Slice(maxPosts)
.Select(ci => ci.As<BlogPostPart>());
Based on this, I would guess (though I haven't tested) that with a string collection of tag names, you can change this line:
.Where(tpr => tpr.Tags.Any(t => t.TagRecord.TagName == "my-tag"))
to this line (where your tag name collection is myTags):
.Where(tpr => tpr.Tags.Any(t => myTags.Contains(t.TagRecord.TagName)))
If that doesn't work, you can create a list of blog post parts, loop through your collection of tag names, and use the original query to get the blog posts for each tag name:
// Get the blog that contains the posts you want to filter
BlogPart blog = _contentManager.Query<BlogPart, BlogPartRecord>(VersionOptions.Published)
.Join<TitlePartRecord>()
.Where(t => t.Title == "your-blog-name")
.Slice(0, 1).FirstOrDefault();
List<BlogPostPart> blogPosts = new List<BlogPostPart>();
foreach (string tag in myTags){
// Query for blog posts that contain a tag called "my-tag"
IEnumerable<BlogPostPart> posts = _contentManager.Query(VersionOptions.Published, "BlogPost")
.Join<CommonPartRecord>()
.Where(cr => cr.Container == blog.Record.ContentItemRecord)
.Join<TagsPartRecord>()
.Where(tpr => tpr.Tags.Any(t => t.TagRecord.TagName == tag))
.WithQueryHints(new QueryHints().ExpandRecords<TagsPartRecord>().ExpandParts<TagsPart>())
.Slice(maxPosts)
.Select(ci => ci.As<BlogPostPart>());
blogPosts.AddRange(posts);
}
来源:https://stackoverflow.com/questions/17559104/how-to-filter-related-content-parts-within-a-orchard-cms-query