开发者

LINQ Query to find all tags?

I have an application that manages documents called Notes. Like a blog, Notes can be searched for matches against one or more Tags, which are contained in a Note.开发者_Go百科Tags collection property. A Tag has Name and ID properties, and matches are made against the ID. A user can specify multiple tags to match against, in which case a Note must contain all Tags specified to match.

I have a very complex LINQ query to perform a Note search, with extension methods and looping. Quite frankly, it has a real code smell to it. I want to rewrite the query with something much simpler. I know that if I made the Tag a simple string, I could use something like this:

var matchingNotes = from n in myNotes
                    where n.Tags.All(tag => searchTags.Contains(tag))

Can I do something that simple if my model uses a Tag object with an ID? What would the query look like. Could it be written in fluent syntax? what would that look like?


I believe you can find notes that have the relevant tags in a single LINQ expression:

IQueryable<Note> query = ... // top part of query

query = query.Where(note => searchTags.All(st =>
    note.Tags.Any(notetag => notetag.Id == st.Id)));

Unfortunately there is no “fluent syntax” equivalent for All and Any, so the best you can do there is

query = from note in query
        where searchTags.All(st =>
            note.Tags.Any(notetag => notetag.Id == st.Id))
        select note;

which is not that much better either.


For starters see my comment; I suspect the query is wrong anyway! I would simplifiy it, by simply enforcing separately that each tag exists:

IQueryable<Note> query = ... // top part of query
foreach(var tagId in searchTagIds) {
    var tmpId = tagId; // modified closures...
    query = query.Where(note => note.Tags.Any(t => t.Id == tmpId));
}

This should have the net effect of enforcing all the tags specified are present and accounted for.


Timwi's solution works in most dialects of LINQ, but not in Linq to Entities. I did find a single-statement LINQ query that works, courtesy of ReSharper. Basically, I wrote a foreach block to do the search, and ReSharper offered to convert the block to a LINQ statement--I had no idea it could do this.

I let ReSharper perform the conversion, and here is what it gave me:

return searchTags.Aggregate<Tag, IQueryable<Note>>(DataStore.ObjectContext.Notes, (current, tag) => current.Where(n => n.Tags.Any(t => t.Id == tag.Id)).OrderBy(n => n.Title));

I read my Notes collection from a database, using Entity Framework 4. DataStore is the custom class I use to manage my EF4 connection; it holds the EF4 ObjectContext as a property.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜