编写一个扩展方法来帮助查询多对多关系
本文关键字:帮助 查询 方法 关系 扩展 一个 | 更新日期: 2023-09-27 17:59:07
我正在尝试编写一个extension method
,以便重构我正在编写的linq多对多查询。我正在尝试检索一个Post(s)
的集合,该集合已被作为参数传递给我的方法的集合中的任何Tag(s)
标记。
以下是相关实体及其一些属性:
张贴
标量属性:
PostID
、PostDate
导航属性:
PostTags
PostTag
标量属性:
PostTagID
、PostID
、TagID
导航属性:
Post
、Tag
标签
标量属性:
TagID
导航属性:
PostTags
这是我目前使用的查询,运行良好:
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;
}
这是我正在努力创建的extension method
的开始(可能不正确):
public static IEnumerable<TResult> SelectRange<TSource, TResult>(
this IEnumerable<TSource> collection,
Func<IEnumerable<TSource>, IEnumerable<TResult>> selector)
{
return selector(collection);
}
和原始查询的理想简化:
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;
}
在编写此extension method
时提供的任何帮助,或执行此查询的任何其他更有效的方法,都将不胜感激。
我认为您的原始查询很好,您只需要处理重复的帖子。在末尾添加一个不同的。或者你可以使用这样的任意方法。
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;
}
编辑-添加了另一个Any语句
实现目标的最有效方法可能是使用内置的join
子句,该子句专用于这样的多对多关系:
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;
这并不比之前的选项"更正确":当你发布问题时,你已经有了一些工作。但就语法和性能而言,这无疑是一种更整洁的方式。
实际上,我不得不想出自己的解决方案来解决这个问题,因为我处理的数据源与实体框架(odbc连接上的DBase4)不兼容,该框架具有相当多的多对多表关系,并且我不必写出linq的长时间连接和组块,而是创建了两个扩展方法
现在请注意,我只处理一个方向数据源本质上是该程序仅用于的数据集历史数据,不接受新的输入或更改,所以我不确定这些扩展方法与任何实际的数据更新插入或删除等
(为了可读性,我把每个参数都包装在自己的行上)
public static IEnumerable<TResult> ManyToManyGroupBy
<TLeft, TRight, TMiddle, TLeftKey, TRightKey, TGroupKey, TResult>
(
this IEnumerable<TLeft> Left,
IEnumerable<TRight> Right,
IEnumerable<TMiddle> Middle,
Func<TLeft, TLeftKey> LeftKeySelector,
Func<TMiddle, TLeftKey> MiddleLeftKeySelector,
Func<TRight, TRightKey> RightKeySelector,
Func<TMiddle, TRightKey> MiddleRightKeySelector,
Func<TLeft, TGroupKey> GroupingSelector,
Func<TGroupKey, IEnumerable<TRight>, TResult> Selector
)
{
return Left
.Join(Middle, LeftKeySelector, MiddleLeftKeySelector, (L, M) => new { L, M })
.Join(Right, LM => MiddleRightKeySelector(LM.M), RightKeySelector, (LM, R) => new { R, LM.L })
.GroupBy(LR => GroupingSelector(LR.L))
.Select(G => Selector(G.Key, G.Select(g => g.R)));
}
public static IEnumerable<TResult> ManyToManySelect
<TLeft, TRight, TMiddle, TLeftKey, TRightKey, TResult>
(
this IEnumerable<TLeft> Left,
IEnumerable<TRight> Right,
IEnumerable<TMiddle> Middle,
Func<TLeft, TLeftKey> LeftKeySelector,
Func<TMiddle, TLeftKey> MiddleLeftKeySelector,
Func<TRight, TRightKey> RightKeySelector,
Func<TMiddle, TRightKey> MiddleRightKeySelector,
Func<TLeft, TRight, TResult> Selector
)
{
return Left
.Join(Middle, LeftKeySelector, MiddleLeftKeySelector, (L, M) => new { L, M })
.Join(Right, LM => MiddleRightKeySelector(LM.M), RightKeySelector, (LM, R) => new { R, LM.L })
.Select(LR => Selector(LR.L, LR.R));
}
它们是相当长的方法,但它们涵盖了通过中间表将两个表连接在一起,以及通过任何特定的Left项或Left项的属性进行分组
使用它们看起来像这样(就像上面一样,为了可读性,我把每个参数都包装在它自己的行上)
var EmployeesCoursesTest = Employees.ManyToManySelect
(
Courses,
CourseParticipations,
E => E.SS,
CP => CP.SS,
C => C.EVENTNO,
CP => CP.EVENTNO,
(E, C) => new
{
C.EVENTNO,
C.START_DATE,
C.END_DATE,
C.HOURS,
C.SESSIONS,
Trainings.First(T => T.TRGID == C.TRGID).TRAINING,
Instructors.First(I => I.INSTRUCTID == C.INSTRUCTID).INSTRUCTOR,
Locations.First(L => L.LOCATIONID == C.LOCATIONID).LOCATION,
Employee = E
}
);
var EmployeesCoursesGroupByTest = Employees.ManyToManyGroupBy
(
Courses,
CourseParticipations,
E => E.SS,
CP => CP.SS,
C => C.EVENTNO,
CP => CP.EVENTNO,
E => E,
(E, Cs) => new
{
Employee = E,
Courses = Cs
}
);
也许这会对你有所帮助——我自己倾向于远离查询语法,所以我几乎只使用Linq的方法语法,所以编写这些类型的扩展方法只是我现在认为的一种方式。