我正在尝试编写一个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
或其他任何更有效的方式来执行此查询时,我们将非常感谢您提供帮助。
答案 0 :(得分:2)
我认为您的原始查询很好,您只需要处理重复的帖子。添加一个截然不同的结尾。或者您可以使用Any方法。
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语句
答案 1 :(得分:1)
实现目标的最有效方法可能是使用专用于这种多对多关系的内置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;
它不比以前的选项“更正确”:当你发布问题时,你已经有了一些工作。但它肯定是使其在语法和性能方面发挥作用的更简洁的方式。
答案 2 :(得分:1)
我实际上必须提出我自己的解决方案来解决这个问题,因为我正在处理一个与Entity Framework(odbc连接上的DBase 4)不兼容的数据源,它有很多表关系,而不是必须写出一个长期绘制的连接和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));
}
它们是相当长的方法,但它们涵盖了通过中间表将两个表连接在一起,以及按任何特定的左项或左项的属性进行分组
使用它们看起来像这样(就像上面我为了便于阅读而将每个参数包装在它自己的行上)
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的方法语法,所以编写这些扩展方法只是我现在想的一种方式。