LINQ EF Core 左连接 [重复]
Posted
技术标签:
【中文标题】LINQ EF Core 左连接 [重复]【英文标题】:LINQ EF Core left join [duplicate] 【发布时间】:2021-05-06 00:26:37 【问题描述】:这里有一个问题:LEFT OUTER JOIN in LINQ,但这是专门针对 Linq-to-Objects 提出的。这个问题是关于 Linq-to-Entities。
我有一个内部联接,我想将它变成外部联接:
alumni = alumni.Join(_context.AlumniSurvey, a => a.Uid, s => s.Uid, (a, s) => new a, s )
.Where(x => x.s.UniversityNumber != x.s.Uid)
.Select(x => x.a);
并不是说我只返回校友;我包括 AlumniSurvey 因为我将根据用户在索引视图中选择的内容执行各种 where 子句
【问题讨论】:
你试过用谷歌吗?这是 .NET 世界中最常见的问题。 ***.com/questions/3404975/left-outer-join-in-linq 我不确定。我宁愿不必引用每个属性。如该示例所示 我很高兴这个人回答了我的问题。我被提及的另一个问题几乎没有这个答案有用。我很快就发现了我的错误。 相关:***.com/questions/19356439/… 【参考方案1】:这应该转换为 SQL 中的 LEFT JOIN
- 请注意,如果有多个匹配的 AlumniSurvey
行,这将导致重复的 alumni
行。
alumni = alumni.GroupJoin(_context.AlumniSurvey, a => a.Uid, s => s.Uid, (a, sj) => new a, sj )
.SelectMany(asj => asj.sj.DefaultIfEmpty(), (asj, s) => new asj.a, s )
.Where(x => x.s.UniversityNumber != x.s.Uid)
.Select(x => x.a);
你可以创建一个扩展方法来简化这个:
private static Expression<Func<TOuter, TInner, TResult>> CastSMLambda<TOuter, TInner, TResult>(LambdaExpression ex, TOuter _1, TInner _2, TResult _3) => (Expression<Func<TOuter, TInner, TResult>>)ex;
public static IQueryable<TResult> LeftOuterJoin<TOuter, TInner, TKey, TResult>(
this IQueryable<TOuter> outer,
IQueryable<TInner> inner,
Expression<Func<TOuter, TKey>> outerKeyExpr,
Expression<Func<TInner, TKey>> innerKeyExpr,
Expression<Func<TOuter, TInner, TResult>> resExpr)
var gjResTemplate = new outer = default(TOuter), innerj = default(IEnumerable<TInner>) ;
// typeof(new outer, innerj ) oij
var oijParm = Expression.Parameter(gjResTemplate.GetType(), "oij");
// TInner inner
var iParm = Expression.Parameter(typeof(TInner), "inner");
// oij.outer
var oijOuter = Expression.PropertyOrField(oijParm, "outer");
// (oij,inner) => resExpr(oij.outer, inner)
var selectResExpr = CastSMLambda(Expression.Lambda(resExpr.Apply(oijOuter, iParm), oijParm, iParm), gjResTemplate, default(TInner), default(TResult));
return outer.GroupJoin(inner, outerKeyExpr, innerKeyExpr, (outer, innerj) => new outer, innerj )
.SelectMany(r => r.innerj.DefaultIfEmpty(), selectResExpr);
// Apply: (x => f).Apply(args)
/// <summary>
/// Substitutes an array of Expression args for the parameters of a lambda, returning a new Expression
/// </summary>
/// <param name="e">The original LambdaExpression to "call".</param>
/// <param name="args">The Expression[] of values to substitute for the parameters of e.</param>
/// <returns>Expression representing e.Body with args substituted in</returns>
public static Expression Apply(this LambdaExpression e, params Expression[] args)
var b = e.Body;
foreach (var pa in e.Parameters.Zip(args, (p, a) => (p, a)))
b = b.Replace(pa.p, pa.a);
return b.PropagateNull();
/// <summary>
/// Replaces an Expression (reference Equals) with another Expression
/// </summary>
/// <param name="orig">The original Expression.</param>
/// <param name="from">The from Expression.</param>
/// <param name="to">The to Expression.</param>
/// <returns>Expression with all occurrences of from replaced with to</returns>
public static T Replace<T>(this T orig, Expression from, Expression to) where T : Expression => (T)new ReplaceVisitor(from, to).Visit(orig);
/// <summary>
/// ExpressionVisitor to replace an Expression (that is Equals) with another Expression.
/// </summary>
public class ReplaceVisitor : ExpressionVisitor
readonly Expression from;
readonly Expression to;
public ReplaceVisitor(Expression from, Expression to)
this.from = from;
this.to = to;
public override Expression Visit(Expression node) => node == from ? to : base.Visit(node);
public static T PropagateNull<T>(this T orig) where T : Expression => (T)new NullVisitor().Visit(orig);
/// <summary>
/// ExpressionVisitor to replace a null.member Expression with a null
/// </summary>
public class NullVisitor : System.Linq.Expressions.ExpressionVisitor
public override Expression Visit(Expression node)
if (node is MemberExpression nme && nme.Expression is ConstantExpression nce && nce.Value == null)
return Expression.Constant(null, nce.Type.GetMember(nme.Member.Name).Single().GetMemberType());
else
return base.Visit(node);
【讨论】:
谢谢!我在 .Select (...) 之后添加了 .Distinct() 以获得不同的记录。 @RonIsaac 请注意这一点 - 至少在 EF 5.x 中,如果任何列属于TEXT
类型,则可能会出现 SQL 异常,因为您无法执行 DISTINCT
在TEXT
列上...
是的 - 谢谢 - 我确实明白了,但是对于网格,我不需要文本类型。我很想只使用 ADO.NET 或 EF 以外的其他东西。或者也许 EF 有意见。你对这个问题有什么想法吗?
@RonIsaac 我非常喜欢 LINQ 而不是 ADO.Net。我只会在需要 EF 无法处理的查询(例如全文搜索)时使用视图。我认为 EF Core 5.x 已经接近可用,所以我会尝试这样做。
谢谢你-我会继续前进。但是因为我现有的数据库有些古怪,我不能使用外键,所以我不能在 EF 中使用导航。因此,我不得不揭开面纱,多学一点。以上是关于LINQ EF Core 左连接 [重复]的主要内容,如果未能解决你的问题,请参考以下文章
EF Core Navigation Property Include 使用左连接而不是内连接
如何在 EF / EF Core 中的第二个表上实现具有某些条件的左连接?