entity framework左连接
如何更改此查询,以便返回所有u.usergroups?
from u in usergroups from p in u.UsergroupPrices select new UsergroupPricesList { UsergroupID = u.UsergroupID, UsergroupName = u.UsergroupName, Price = p.Price };
从MSDN改编, 如何用EF 4离开连接
var query = from u in usergroups join p in UsergroupPrices on u equals p.UsergroupID into gj from x in gj.DefaultIfEmpty() select new { UsergroupID = u.UsergroupID, UsergroupName = u.UsergroupName, Price = (x == null ? String.Empty : x.Price) };
这可能有点矫枉过正,但是我写了一个扩展方法,所以你可以使用Join
语法来做一个LeftJoin
(至less在方法调用符号中):
persons.LeftJoin( phoneNumbers, person => person.Id, phoneNumber => phoneNumber.PersonId, (person, phoneNumber) => new { Person = person, PhoneNumber = (phoneNumber != null) ? phoneNumber.Number : null } );
我的代码只不过是将一个GroupJoin
和一个SelectMany
调用添加到当前的expression式树中。 不过,它看起来相当复杂,因为我必须自己构buildexpression式,并在resultSelector
参数中修改用户指定的expression式树,以保持整个树可由LINQ-to-Entities转换。
public static class LeftJoinExtension { public static IQueryable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>( this IQueryable<TOuter> outer, IQueryable<TInner> inner, Expression<Func<TOuter, TKey>> outerKeySelector, Expression<Func<TInner, TKey>> innerKeySelector, Expression<Func<TOuter, TInner, TResult>> resultSelector) { MethodInfo groupJoin = typeof (Queryable).GetMethods() .Single(m => m.ToString() == "System.Linq.IQueryable`1[TResult] GroupJoin[TOuter,TInner,TKey,TResult](System.Linq.IQueryable`1[TOuter], System.Collections.Generic.IEnumerable`1[TInner], System.Linq.Expressions.Expression`1[System.Func`2[TOuter,TKey]], System.Linq.Expressions.Expression`1[System.Func`2[TInner,TKey]], System.Linq.Expressions.Expression`1[System.Func`3[TOuter,System.Collections.Generic.IEnumerable`1[TInner],TResult]])") .MakeGenericMethod(typeof (TOuter), typeof (TInner), typeof (TKey), typeof (LeftJoinIntermediate<TOuter, TInner>)); MethodInfo selectMany = typeof (Queryable).GetMethods() .Single(m => m.ToString() == "System.Linq.IQueryable`1[TResult] SelectMany[TSource,TCollection,TResult](System.Linq.IQueryable`1[TSource], System.Linq.Expressions.Expression`1[System.Func`2[TSource,System.Collections.Generic.IEnumerable`1[TCollection]]], System.Linq.Expressions.Expression`1[System.Func`3[TSource,TCollection,TResult]])") .MakeGenericMethod(typeof (LeftJoinIntermediate<TOuter, TInner>), typeof (TInner), typeof (TResult)); var groupJoinResultSelector = (Expression<Func<TOuter, IEnumerable<TInner>, LeftJoinIntermediate<TOuter, TInner>>>) ((oneOuter, manyInners) => new LeftJoinIntermediate<TOuter, TInner> {OneOuter = oneOuter, ManyInners = manyInners}); MethodCallExpression exprGroupJoin = Expression.Call(groupJoin, outer.Expression, inner.Expression, outerKeySelector, innerKeySelector, groupJoinResultSelector); var selectManyCollectionSelector = (Expression<Func<LeftJoinIntermediate<TOuter, TInner>, IEnumerable<TInner>>>) (t => t.ManyInners.DefaultIfEmpty()); ParameterExpression paramUser = resultSelector.Parameters.First(); ParameterExpression paramNew = Expression.Parameter(typeof (LeftJoinIntermediate<TOuter, TInner>), "t"); MemberExpression propExpr = Expression.Property(paramNew, "OneOuter"); LambdaExpression selectManyResultSelector = Expression.Lambda(new Replacer(paramUser, propExpr).Visit(resultSelector.Body), paramNew, resultSelector.Parameters.Skip(1).First()); MethodCallExpression exprSelectMany = Expression.Call(selectMany, exprGroupJoin, selectManyCollectionSelector, selectManyResultSelector); return outer.Provider.CreateQuery<TResult>(exprSelectMany); } private class LeftJoinIntermediate<TOuter, TInner> { public TOuter OneOuter { get; set; } public IEnumerable<TInner> ManyInners { get; set; } } private class Replacer : ExpressionVisitor { private readonly ParameterExpression _oldParam; private readonly Expression _replacement; public Replacer(ParameterExpression oldParam, Expression replacement) { _oldParam = oldParam; _replacement = replacement; } public override Expression Visit(Expression exp) { if (exp == _oldParam) { return _replacement; } return base.Visit(exp); } } }
我能够通过调用主模型上的DefaultIfEmpty()来完成此操作。 这让我离开加载懒惰的实体,似乎更可读:
var complaints = db.Complaints.DefaultIfEmpty() .Where(x => x.DateStage1Complete == null || x.DateStage2Complete == null) .OrderBy(x => x.DateEntered) .Select(x => new { ComplaintID = x.ComplaintID, CustomerName = x.Customer.Name, CustomerAddress = x.Customer.Address, MemberName = x.Member != null ? x.Member.Name: string.Empty, AllocationName = x.Allocation != null ? x.Allocation.Name: string.Empty, CategoryName = x.Category != null ? x.Category.Ssl_Name : string.Empty, Stage1Start = x.Stage1StartDate, Stage1Expiry = x.Stage1_ExpiryDate, Stage2Start = x.Stage2StartDate, Stage2Expiry = x.Stage2_ExpiryDate });
请让你的生活更轻松(不要使用join组):
var query = from ug in UserGroups from ugp in UserGroupPrices.Where(x => x.UserGroupId == ug.Id).DefaultIfEmpty() select new { UserGroupID = ug.UserGroupID, UserGroupName = ug.UserGroupName, Price = ugp.Price };