2011-04-04 17 views
54

मैं इस क्वेरी को कैसे बदलूं ताकि यह सभी u.usergroups को वापस कर सके?इकाई ढांचे में शामिल

from u in usergroups 
from p in u.UsergroupPrices 
select new UsergroupPricesList 
{ 
UsergroupID = u.UsergroupID, 
UsergroupName = u.UsergroupName, 
Price = p.Price 
}; 
+1

शायद [इस] (http://geekswithblogs.net/SudheersBlog/archive/2009/06/11/132758.aspx) मदद कर सकते हैं। यह [SO] पर एक और सवाल पर था (http://stackoverflow.com/questions/2376701/linq-to-entities-how-to-define-left- left-join-for-grouping) – Menahem

उत्तर

94

MSDN से अनुकूलित, how to left join using 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) 
      }; 
+1

मुझे से बेहतर यह पसंद है जहां gj.DefaultIfEmpty() अंत में क्योंकि मैं कहां या चयन में एक्स का उपयोग कर सकता हूं! – Gary

+0

क्या आप 'x से gj.DefaultIfEmpty()' लाइन में बता सकते हैं? –

+0

@AlexDresko यह हिस्सा शामिल होने से सभी परिणाम लेता है, और जिनके पास कोई सही हाथ मूल्य नहीं है, आपको शून्य (ऑब्जेक्ट का डिफ़ॉल्ट नहीं है) देता है। एचटी – Menahem

14

यह एक overkill का एक सा हो सकता है, लेकिन मैं एक विस्तार विधि ने लिखा है, ताकि आप Join वाक्य रचना (का उपयोग करते हुए कम से कम विधि में एक LeftJoin कर सकते हैं कॉल अंकन):

persons.LeftJoin(
    phoneNumbers, 
    person => person.Id, 
    phoneNumber => phoneNumber.PersonId, 
    (person, phoneNumber) => new 
     { 
      Person = person, 
      PhoneNumber = (phoneNumber != null) ? phoneNumber.Number : null 
     } 
); 

मेरे कोड एक GroupJoin और एक SelectMany कॉल जोड़ने से ज्यादा कुछ नहीं करता है वर्तमान अभिव्यक्ति पेड़ के लिए। फिर भी, यह बहुत जटिल लग रहा है क्योंकि मुझे अभिव्यक्तियां बनाना है और उपयोगकर्ता द्वारा निर्दिष्ट resultSelector पैरामीटर में अभिव्यक्ति वृक्ष को संशोधित करना है ताकि पूरे पेड़ को 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); 
     } 
    } 
} 
+0

इस एक्सटेंशन के लिए धन्यवाद। – Fergers

0

मैं मुख्य मॉडल पर 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 
      }); 
+1

यहां, आपको 'DefaultIfEmpty()' की आवश्यकता नहीं है: यह केवल तभी प्रभावित होता है जब 'db.Complains' खाली होता है। 'db.Complains.Where (...) ऑर्डरबी (...)। चुनें (x => नया {..., सदस्य नाम = x.Member! = शून्य? x.Member.Name: string.Empty,। ..}) ', बिना किसी' डीफॉल्ट IfEmpty() ', पहले से ही बाएं शामिल होगा (मान लीजिए कि 'सदस्य' संपत्ति को वैकल्पिक के रूप में चिह्नित किया गया है)। – hvd

2

कृपया सुनिश्चित आपके जीवन को आसान (समूह में शामिल होने के उपयोग न करें):

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 
      }; 
+1

समूह में शामिल होने से बचें राय का विषय है, लेकिन यह निश्चित रूप से एक वैध राय है। 'मूल्य = ugp.Price' विफल हो सकता है यदि' मूल्य' एक गैर-शून्य संपत्ति है और बाएं शामिल होने से कोई परिणाम नहीं मिलता है। – hvd

+0

उपरोक्त के साथ सहमत हैं, लेकिन दो से अधिक तालिकाओं के साथ यह दृष्टिकोण पढ़ने और बनाए रखने के लिए इतना आसान है। –

संबंधित मुद्दे