Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing an extension method to help with querying many-to-many relationships

I am trying to write an extension method in order to refactor a linq many-to-many query I'm writing. I am trying to retrieve a collection of Post(s) which have been tagged with any of the Tag(s) in a collection passed as a parameter to my method.

Here are the relevant entities along with some of their properties:

Post

Scalar Properties: PostID, PostDate

Navigation Property: PostTags

PostTag

Scalar Properties: PostTagID, PostID, TagID

Navigation Properties: Post, Tag

Tag

Scalar Properties: TagID

Navigation Property: PostTags

This is the query I'm currently using which works well:

public IEnumerable<Post> GetPostsByTags(IEnumerable<Tag> tags)
{
    return from pt in context.PostTags
           from t in tags
           where pt.TagID == t.TagID &&
                 pt.Post.PostDate != null
           orderby pt.Post.PostDate descending
           select pt.Post;               
}   

This is the (probably incorrect) start of the extension method I'm struggling to create:

public static IEnumerable<TResult> SelectRange<TSource, TResult>(
    this IEnumerable<TSource> collection,
    Func<IEnumerable<TSource>, IEnumerable<TResult>> selector)
{
    return selector(collection);
}

And the ideal simplification of the original query:

public IEnumerable<Post> GetPostsByTags(IEnumerable<Tag> tags)
{
    return from p in context.Posts
           where p.PostTags.SelectRange(x => ?) &&
                 p.PostDate != null                    
           orderby p.PostDate descending
           select p;
}

Any help in writing this extension method, or any other more efficient way to perform this query, will be greatly appreciated.

like image 955
aligray Avatar asked Jun 15 '11 09:06

aligray


People also ask

How do you write a SQL query for many-to-many relationships?

When you need to establish a many-to-many relationship between two or more tables, the simplest way is to use a Junction Table. A Junction table in a database, also referred to as a Bridge table or Associative Table, bridges the tables together by referencing the primary keys of each data table.

How do you deal with many-to-many relationships?

Many-to-many (m:n) relationships add complexity and confusion to your model and to the application development process. The key to resolve m:n relationships is to separate the two entities and create two one-to-many (1:n) relationships between them with a third intersect entity.

How do you create a many-to-many relationship in Sqlalchemy?

Many to Many relationship between two tables is achieved by adding an association table such that it has two foreign keys - one from each table's primary key.

What are the four 4 types of relationships in a database?

There are 3 different types of relations in the database: one-to-one. one-to-many, and. many-to-many.


2 Answers

I think your original query is fine, you just need to handle duplicate posts. Add a distinct to the end. Or you can use the Any method like so.

public IEnumerable<Post> GetPostsByTags(IEnumerable<Tag> tags)
{
    return from p in context.Posts
           where p.PostTags.Any(pt => tags.Any(t => t.TagID == pt.TagID)) &&
                 p.PostDate != null                    
           orderby p.PostDate descending
           select p;
}

Edit - Added another Any statement

like image 81
Aducci Avatar answered Nov 05 '22 03:11

Aducci


The most efficient way to achieve your goal is probably to use the built-in join clause which is dedicated to such many-to-many relationships :

from pt in PostTags
where pt.Post.PostDate != null
join t in tags on pt.TagID equals t.TagID
orderby pt.Post.PostDate descending
select pt.Post;

It is not "more correct" than the previous options : you already had something working when you posted your question. But it is surely the neater way to have it work in terms of syntax and performance.

like image 44
Ssithra Avatar answered Nov 05 '22 04:11

Ssithra