इकाई ढांचा जुड़ गया


84

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

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

1
शायद यह मदद कर सकता है। यह
मेनेहेम

जवाबों:


135

MSDN से अनुकूलित, EF 4 का उपयोग करके बाएं से कैसे जुड़ें

var query = from u in usergroups
            join p in UsergroupPrices on u.UsergroupID 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) 
            };

2
मुझे यह बेहतर लगता है कि आखिर कहाँ से gj.DefaultIfEmpty () अंत में क्योंकि मैं x का उपयोग कहाँ या चयन कर सकता हूँ!
गैरी

1
क्या आप 'x से gj.DefaultIfEmpty ()' लाइन को समझा सकते हैं?
एलेक्स ड्रेस्को

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

2
क्या होगा अगर दो से अधिक टेबल हैं?
मोहम्मदहोसिन R

1
यह efcore के साथ थोड़ा बदल गया; from x in gj.DefaultIfEmpty()बन जाता है from p in gj.DefaultIfEmpty()docs.microsoft.com/en-us/ef/core/querying/…
carlin.scott

30

यह थोड़ा अधिक हो सकता है, लेकिन मैंने एक विस्तार विधि लिखी है, इसलिए आप सिंटैक्स LeftJoinका उपयोग कर सकते हैं Join(कम से कम विधि कॉल संकेतन में):

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

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

2
इस विस्तार के लिए धन्यवाद फेरो।
फर्गर्स

यह अभी भी बहुत अच्छा है। धन्यवाद!
द गीक यू नेड

1
.NET फ्रेमवर्क 4.6.2 के भीतर इसका परीक्षण किया गया है और यह उम्मीद के मुताबिक काम करता है (यानी एक LEFT OUTER JOIN)। मैं सोच रहा था कि यह .NET कोर पर काम करता है, हालांकि। धन्यवाद।
अलेक्सई

23

कृपया अपने जीवन को आसान बनाएं (समूह में शामिल न हों):

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 != null ? ugp.Price : 0 //this is to handle nulls as even when Price is non-nullable prop it may come as null from SQL (result of Left Outer Join)
            };

2
समूह में शामिल होने से बचना एक विचार का विषय है, लेकिन यह निश्चित रूप से एक मान्य राय है। Price = ugp.Priceयदि Priceकोई अशक्त संपत्ति है और बायाँ जोड़ किसी भी परिणाम नहीं देता है, तो विफल हो सकता है ।

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

1
हम जाँच सकते हैं कि क्या ugp == NULLऔर के लिए एक डिफ़ॉल्ट मान सेट करें Price
Hp93

एकदम सही :)
मोहम्मदहोसिन आर

1
बहुत बढ़िया! मैं पठनीयता के लिए इस समाधान को पसंद करता हूं। इसके अलावा, यह अधिक जुड़ता है (यानी 3 या अधिक तालिका से) बहुत आसान है! मैंने इसे 2 बाएं जोड़ों (यानी 3 टेबल) के लिए सफलतापूर्वक उपयोग किया।
जेरेमी मॉरेन

4

यदि आप मेथड कॉल नोटेशन पसंद करते हैं, तो आप SelectManyसंयुक्त के साथ लेफ्ट जॉइन को बाध्य कर सकते हैं DefaultIfEmpty। कम से कम इकाई फ्रेमवर्क पर 6 मार SQL सर्वर। उदाहरण के लिए:

using(var ctx = new MyDatabaseContext())
{
    var data = ctx
    .MyTable1
    .SelectMany(a => ctx.MyTable2
      .Where(b => b.Id2 == a.Id1)
      .DefaultIfEmpty()
      .Select(b => new
      {
        a.Id1,
        a.Col1,
        Col2 = b == null ? (int?) null : b.Col2,
      }));
}

(ध्यान दें कि MyTable2.Col2एक प्रकार का कॉलम है int)। उत्पन्न SQL इस तरह दिखेगा:

SELECT 
    [Extent1].[Id1] AS [Id1], 
    [Extent1].[Col1] AS [Col1], 
    CASE WHEN ([Extent2].[Col2] IS NULL) THEN CAST(NULL AS int) ELSE  CAST( [Extent2].[Col2] AS int) END AS [Col2]
    FROM  [dbo].[MyTable1] AS [Extent1]
    LEFT OUTER JOIN [dbo].[MyTable2] AS [Extent2] ON [Extent2].[Id2] = [Extent1].[Id1]

मेरे लिए इसमें "CROSS APPLY" के साथ कुछ बेहद धीमी क्वेरी उत्पन्न हो रही है।
मियोकी

2

2 और अधिक बाएं जोड़ों के लिए (बाएं शामिल होने वाले निर्माता और आरंभकर्ता)

IQueryable<CreateRequestModel> queryResult = from r in authContext.Requests
                                             join candidateUser in authContext.AuthUsers
                                             on r.CandidateId equals candidateUser.Id
                                             join creatorUser in authContext.AuthUsers
                                             on r.CreatorId equals creatorUser.Id into gj
                                             from x in gj.DefaultIfEmpty()
                                             join initiatorUser in authContext.AuthUsers
                                             on r.InitiatorId equals initiatorUser.Id into init
                                             from x1 in init.DefaultIfEmpty()

                                             where candidateUser.UserName.Equals(candidateUsername)
                                             select new CreateRequestModel
                                             {
                                                 UserName = candidateUser.UserName,
                                                 CreatorId = (x == null ? String.Empty : x.UserName),
                                                 InitiatorId = (x1 == null ? String.Empty : x1.UserName),
                                                 CandidateId = candidateUser.UserName
                                             };

1

मैं मुख्य मॉडल पर 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(...).OrderBy(...).Select(x => new { ..., MemberName = x.Member != null ? x.Member.Name : string.Empty, ... }), बिना किसी के .DefaultIfEmpty(), पहले से ही लेफ्ट जॉइन करेगा (यह मानते हुए कि Memberसंपत्ति वैकल्पिक है)।

1

अगर UserGroups का UserGroupPrices तालिका के साथ कई संबंध है, तो EF में, एक बार संबंध को कोड में परिभाषित किया गया है:

//In UserGroups Model
public List<UserGroupPrices> UserGrpPriceList {get;set;}

//In UserGroupPrices model
public UserGroups UserGrps {get;set;}

आप बस इसके द्वारा सेट किए गए बाएं सम्मिलित परिणाम को खींच सकते हैं:

var list = db.UserGroupDbSet.ToList();

बाईं तालिका के लिए अपने DbSet को संभालने के लिए UserGroupDbSet है, जिसमें UserGrpPriceList शामिल होगा, जो दाईं तालिका से सभी संबद्ध रिकॉर्ड की एक सूची है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.