मेरे डेटाबेस में मेरे पास 1:N संबंध के साथ दो टेबल Organizations और OrganizationMembers हैं।

मैं एक प्रश्न व्यक्त करना चाहता हूं जो प्रत्येक संगठन को पहले संगठन के स्वामी के पहले और अंतिम नाम के साथ लौटाता है।

मेरी वर्तमान चयन अभिव्यक्ति काम करती है, लेकिन यह न तो कुशल है और न ही यह मुझे सही लगती है, क्योंकि प्रत्येक सबक्वायरी कई बार परिभाषित हो जाती है।

await dbContext.Organizations
    .AsNoTracking()
    .Select(x =>
    {
        return new OrganizationListItem
        {
            Id = x.Id,
            Name = x.Name,
            OwnerFirstName = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).FirstName,
            OwnerLastName = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).LastName,
            OwnerEmailAddress = (x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner)).EmailAddress
        };
    })
    .ToArrayAsync();

क्या उपश्रेणियों को संक्षेप में प्रस्तुत करना या उनका पुन: उपयोग करना संभव है, इसलिए मुझे उन्हें कई बार परिभाषित करने की आवश्यकता नहीं है?

ध्यान दें कि मैंने पहले ही सबक्वायरी परिणाम को एक चर में संग्रहीत करने का प्रयास किया है। यह काम नहीं करता है, क्योंकि इसके लिए अभिव्यक्ति को एक स्टेटमेंट बॉडी में बदलने की आवश्यकता होती है, जिसके परिणामस्वरूप एक कंपाइलर त्रुटि होती है।

4
Redstone 19 सितंबर 2019, 16:17

3 जवाब

सबसे बढ़िया उत्तर

इंटरमीडिएट प्रोजेक्शन (Select) को शुरू करके सबक्वेरी का पुन: उपयोग किया जा सकता है, जो कि क्वेरी सिंटैक्स में let ऑपरेटर के बराबर है।

उदाहरण के लिए:

dbContext.Organizations.AsNoTracking()
    // intermediate projection
    .Select(x => new
    {
        Organization = x,
        Owner = x.Members
            .Where(member => member.Role == RoleType.Owner)
            .OrderBy(member => member.CreatedAt)
            .FirstOrDefault()
    })
    // final projection
    .Select(x => new OrganizationListItem
    {
        Id = x.Organization.Id,
        Name = x.Organization.Name,
        OwnerFirstName = Owner.FirstName,
        OwnerLastName = Owner.LastName,
        OwnerEmailAddress = Owner.EmailAddress
    })

ध्यान दें कि यदि आप क्लाइंट मूल्यांकन से बचना चाहते हैं तो आपको EF Core 3.0 से पहले First के बजाय FirstOrDefault का उपयोग करना होगा।

साथ ही यह जेनरेट की गई SQL क्वेरी को बेहतर/तेज़ नहीं बनाता है - इसमें अभी भी अंतिम चयन में शामिल प्रत्येक संपत्ति के लिए अलग इनलाइन सबक्वायरी शामिल है। इसलिए पठनीयता में सुधार होगा, लेकिन दक्षता में नहीं।

इसलिए आमतौर पर नेस्टेड ऑब्जेक्ट को बिना फ़्लैट किए गए डीटीओ प्रॉपर्टी में प्रोजेक्ट करना बेहतर होता है, यानी OwnerFirstName, OwnerLastName, OwnerEmailAddress के बजाय गुणों वाला एक वर्ग होता है FirstName, LastName , EmailAddress और प्रॉपर्टी मान लें Owner उस प्रकार का OrganizationListItem (संदर्भ नेविगेशन प्रॉपर्टी वाली इकाई के समान)। इस तरह आप कुछ इस तरह इस्तेमाल कर पाएंगे

dbContext.Organizations.AsNoTracking()
    .Select(x => new
    {
        Id = x.Organization.Id,
        Name = x.Organization.Name,
        Owner = x.Members
            .Where(member => member.Role == RoleType.Owner)
            .OrderBy(member => member.CreatedAt)
            .Select(member => new OwnerInfo // the new class
             {
                 FirstName = member.FirstName,
                 LastName = member.LastName,
                 EmailAddress = member.EmailAddress
             })
            .FirstOrDefault()
    })

दुर्भाग्य से पूर्व 3.0 संस्करणों में EF Core इस LINQ क्वेरी के लिए N + 1 SQL क्वेरी उत्पन्न करेगा, लेकिन 3.0+ में यह एक एकल और काफी कुशल SQL क्वेरी उत्पन्न करेगा।

7
Ivan Stoev 19 सितंबर 2019, 17:36

इस बारे में कैसा है:

await dbContext.Organizations
    .AsNoTracking()
    .Select(x =>
    {
        var firstMember = x.Members.OrderBy(member => member.CreatedAt).First(member => member.Role == RoleType.Owner);
        return new OrganizationListItem
        {
            Id = x.Id,
            Name = x.Name,
            OwnerFirstName = firstMember.FirstName,
            OwnerLastName = firstMember.LastName,
            OwnerEmailAddress = firstMember.EmailAddress
        };
    })
    .ToArrayAsync();
0
akg179 19 सितंबर 2019, 16:26
धन्यवाद, लेकिन मैंने पहले ही कोशिश कर ली है। यह संभव नहीं है और इसके परिणामस्वरूप एक त्रुटि होगी: A lambda expression with a statement body cannot be converted to an expression tree.
 – 
Redstone
19 सितंबर 2019, 16:29
क्या आपने इस linq में 'AsEnumerable ()' का उपयोग करने का प्रयास किया है? यह सुझाव नहीं दे रहा है कि यह सीधे स्मृति में क्वेरी परिणाम लाएगा और इसका शेष कोड पर अवांछित प्रभाव हो सकता है।
 – 
akg179
19 सितंबर 2019, 16:36

ऐसा करने के बारे में कैसे

await dbContext.Organizations
    .AsNoTracking()
    .Select(x => new OrganizationListItem
        {
            Id = x.Id,
            Name = x.Name,
            OwnerFirstName = x.Members.FirstOrDefault(member => member.Role == RoleType.Owner).FirstName,
            OwnerLastName = x.Members.FirstOrDefault(member => member.Role == RoleType.Owner)).LastName,
            OwnerEmailAddress = x.Members.FirstOrDefault(member => member.Role == RoleType.Owner)).EmailAddress
        })
    .ToArrayAsync();
1
Hamza Khanzada 19 सितंबर 2019, 16:27
वह हिस्सा जिसे ओपी दोहराना नहीं चाहता था x.Members.FirstOrDefault(member => member.Role == RoleType.Owner) अभी भी आपके कोड में दोहराया गया है, तो आपका कोड किस समस्या का समाधान करता है?
 – 
Arad
9 अक्टूबर 2020, 12:52