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.
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.
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.
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.
There are 3 different types of relations in the database: one-to-one. one-to-many, and. many-to-many.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With