सामग्री लोड किए बिना EntityFramework के भीतर पंक्तियों को कैसे विभाजित करें?


109

मैं कैसे करने के लिए निर्धारित करने के लिए कोशिश कर रहा हूँ गिनती EntityFramework का उपयोग कर एक मेज पर मिलान पंक्तियाँ।

समस्या यह है कि प्रत्येक पंक्ति में कई मेगाबाइट डेटा (बाइनरी फ़ील्ड में) हो सकता है। बेशक SQL कुछ इस तरह होगा:

SELECT COUNT(*) FROM [MyTable] WHERE [fkID] = '1';

मैं सभी पंक्तियों को लोड कर सकता हूं और फिर गणना को निम्न के साथ ढूंढ सकता हूं :

var owner = context.MyContainer.Where(t => t.ID == '1');
owner.MyTable.Load();
var count = owner.MyTable.Count();

लेकिन वह स्थूल रूप से अक्षम है। क्या कोई सरल तरीका है?


संपादित करें: धन्यवाद, सभी। मैंने डीबी को एक निजी संलग्न से स्थानांतरित किया है ताकि मैं प्रोफाइलिंग चला सकूं; यह मदद करता है लेकिन भ्रम की स्थिति पैदा करता है जिसकी मुझे उम्मीद नहीं थी।

और मेरी वास्तविक डेटा थोड़ा गहरा है, मैं इस्तेमाल करेंगे ट्रक ले जाने Pallets के मामले की आइटम - और मैं नहीं करना चाहते ट्रक जब तक कि वहाँ है कम से कम एक छोड़ने के लिए आइटम उस में।

मेरे प्रयासों को नीचे दिखाया गया है। मुझे जो हिस्सा नहीं मिलता है वह यह है कि CASE_2 कभी भी DB सर्वर (MSSQL) का उपयोग नहीं करता है।

var truck = context.Truck.FirstOrDefault(t => (t.ID == truckID));
if (truck == null)
    return "Invalid Truck ID: " + truckID;
var dlist = from t in ve.Truck
    where t.ID == truckID
    select t.Driver;
if (dlist.Count() == 0)
    return "No Driver for this Truck";

var plist = from t in ve.Truck where t.ID == truckID
    from r in t.Pallet select r;
if (plist.Count() == 0)
    return "No Pallets are in this Truck";
#if CASE_1
/// This works fine (using 'plist'):
var list1 = from r in plist
    from c in r.Case
    from i in c.Item
    select i;
if (list1.Count() == 0)
    return "No Items are in the Truck";
#endif

#if CASE_2
/// This never executes any SQL on the server.
var list2 = from r in truck.Pallet
        from c in r.Case
        from i in c.Item
        select i;
bool ok = (list.Count() > 0);
if (!ok)
    return "No Items are in the Truck";
#endif

#if CASE_3
/// Forced loading also works, as stated in the OP...
bool ok = false;
foreach (var pallet in truck.Pallet) {
    pallet.Case.Load();
    foreach (var kase in pallet.Case) {
        kase.Item.Load();
        var item = kase.Item.FirstOrDefault();
        if (item != null) {
            ok = true;
            break;
        }
    }
    if (ok) break;
}
if (!ok)
    return "No Items are in the Truck";
#endif

और CASE_1 से उत्पन्न SQL को sp_executesql के माध्यम से पाइप किया जाता है , लेकिन:

SELECT [Project1].[C1] AS [C1]
FROM   ( SELECT cast(1 as bit) AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN  (SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(cast(1 as bit)) AS [A1]
        FROM   [dbo].[PalletTruckMap] AS [Extent1]
        INNER JOIN [dbo].[PalletCaseMap] AS [Extent2] ON [Extent1].[PalletID] = [Extent2].[PalletID]
        INNER JOIN [dbo].[Item] AS [Extent3] ON [Extent2].[CaseID] = [Extent3].[CaseID]
        WHERE [Extent1].[TruckID] = '....'
    )  AS [GroupBy1] ) AS [Project1] ON 1 = 1

[ मेरे पास वास्तव में ट्रक, ड्राइवर, पैलेट, मामले या आइटम नहीं हैं; जैसा कि आप एसक्यूएल से देख सकते हैं ट्रक-पैलेट और पैलेट-केस रिश्ते कई-से-कई हैं - हालांकि मुझे नहीं लगता कि यह मायने रखता है। मेरी वास्तविक वस्तुएं इंटिजीबल्स हैं और वर्णन करने में कठिन हैं, इसलिए मैंने नाम बदल दिए। ]


1
आपने फूस की लोडिंग समस्या को कैसे हल किया?
शर्लक

जवाबों:


123

क्वेरी सिंटैक्स:

var count = (from o in context.MyContainer
             where o.ID == '1'
             from t in o.MyTable
             select t).Count();

विधि सिंटैक्स:

var count = context.MyContainer
            .Where(o => o.ID == '1')
            .SelectMany(o => o.MyTable)
            .Count()

दोनों एक ही SQL क्वेरी उत्पन्न करते हैं।


क्यों SelectMany()? क्या इसकी जरूरत है? क्या यह इसके बिना उचित काम नहीं करेगा?
जो स्मू

@JoSmo, नहीं, यह बिल्कुल अलग क्वेरी है।
क्रेग स्टंट ज़ूल

मेरे लिए इसे साफ़ करने के लिए धन्यवाद। बस यकीन होना चाहता था। :)
जो स्मू जूल

1
क्या आप मुझे बता सकते हैं कि यह SelectMany के साथ अलग क्यों है? मुझे समझ नहीं आ रहा है। मैं इसे SelectMany के बिना करता हूं लेकिन यह वास्तव में धीमा हो जाता है क्योंकि मेरे पास 20 मिलियन से अधिक रिकॉर्ड हैं। मैंने यांग झांग से जवाब की कोशिश की और महान काम करता है, बस जानना चाहता था कि SelectMany क्या करता है।
mikesoft

1
@AustinFelipe SelectMany के लिए कॉल के बिना, क्वेरी MyContainer में पंक्तियों की संख्या को '1' के बराबर आईडी के साथ वापस कर देगी। SelectMany कॉल MyTable में उन सभी पंक्तियों को लौटाता है जो क्वेरी के पिछले परिणाम से संबंधित हैं (जिसका अर्थ है परिणाम MyContainer.Where(o => o.ID == '1'))
sbecker

48

मुझे लगता है कि आप कुछ चाहते हैं

var count = context.MyTable.Count(t => t.MyContainer.ID == '1');

(टिप्पणियों को प्रतिबिंबित करने के लिए संपादित)


1
नहीं, उसे MyTable में संस्थाओं की गिनती की जरूरत है जो MyContainer में ID = 1 के साथ एक इकाई द्वारा संदर्भित है
Craig Stuntz

3
संयोग से, यदि t.ID एक PK है, तो ऊपर दिए गए कोड में गिनती हमेशा 1 होगी। :)
क्रेग स्टंट्ज़

2
@ क्रेग, आप सही कह रहे हैं, मुझे t.ForeignTable.ID का उपयोग करना चाहिए था। अपडेट किया गया।
केविन

1
वैसे यह छोटा और सरल है। मेरी पसंद है: var count = context.MyTable.Count(t => t.MyContainer.ID == '1'); लंबे और बदसूरत नहीं: var count = (from o in context.MyContainer where o.ID == '1' from t in o.MyTable select t).Count(); लेकिन यह कोडिंग शैली पर निर्भर करता है ...
CL

सुनिश्चित करें कि आप "System.Linq का उपयोग कर", या इस अभ्यस्त काम को शामिल करते हैं
काउंटमर्फी

16

जैसा कि मैंने इसे समझा, चयनित उत्तर अभी भी संबंधित परीक्षणों के सभी लोड करता है। इस msdn ब्लॉग के अनुसार, एक बेहतर तरीका है।

http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx

विशेष रूप से

using (var context = new UnicornsContext())

    var princess = context.Princesses.Find(1);

    // Count how many unicorns the princess owns 
    var unicornHaul = context.Entry(princess)
                      .Collection(p => p.Unicorns)
                      .Query()
                      .Count();
}

3
अतिरिक्त Find(1)अनुरोध करने की आवश्यकता नहीं है । बस इकाई बना सकते हैं और संदर्भ के लिए देते हैं:var princess = new PrincessEntity{ Id = 1 }; context.Princesses.Attach(princess);
tenbits

13

यह मेरा कोड है:

IQueryable<AuctionRecord> records = db.AuctionRecord;
var count = records.Count();

सुनिश्चित करें कि चर IQueryable के रूप में परिभाषित किया गया है जब आप गणना () विधि का उपयोग करते हैं, तो ईएफ कुछ ऐसा निष्पादित करेगा

select count(*) from ...

अन्यथा, यदि रिकॉर्ड IEnumerable के रूप में परिभाषित किया गया है, तो उत्पन्न SQL पूरी तालिका को क्वेरी करेगा और पंक्तियों को वापस लौटाएगा।


10

खैर, यहां तक ​​कि SELECT COUNT(*) FROM Tableकाफी अयोग्य होगा, विशेष रूप से बड़ी तालिकाओं पर, क्योंकि SQL सर्वर वास्तव में कुछ भी नहीं कर सकता है, लेकिन एक पूर्ण तालिका स्कैन (क्लस्टर इंडेक्स स्कैन) करता है।

कभी-कभी, डेटाबेस से पंक्तियों की अनुमानित संख्या को जानना काफी अच्छा होता है, और ऐसे में, इस तरह के एक बयान को भुगतना पड़ सकता है:

SELECT 
    SUM(used_page_count) * 8 AS SizeKB,
    SUM(row_count) AS [RowCount], 
    OBJECT_NAME(OBJECT_ID) AS TableName
FROM 
    sys.dm_db_partition_stats
WHERE 
    OBJECT_ID = OBJECT_ID('YourTableNameHere')
    AND (index_id = 0 OR index_id = 1)
GROUP BY 
    OBJECT_ID

यह डायनेमिक मैनेजमेंट व्यू का निरीक्षण करेगा और एक विशिष्ट टेबल को देखते हुए पंक्तियों की संख्या और उसमें से टेबल का आकार निकालेगा। यह ढेर (index_id = 0) या क्लस्टर इंडेक्स (index_id = 1) के लिए प्रविष्टियों को संक्षेप में प्रस्तुत करता है।

यह त्वरित है, इसका उपयोग करना आसान है, लेकिन यह 100% सटीक या अद्यतित होने की गारंटी नहीं है। लेकिन कई मामलों में, यह "काफी अच्छा" है (और सर्वर पर बहुत कम बोझ डाल दिया है)।

शायद यह आपके लिए भी काम करेगा? बेशक, इसे ईएफ में उपयोग करने के लिए, आपको इसे एक संग्रहित खरीद में लपेटना होगा या सीधे "एक्सेक्यूट SQL क्वेरी" कॉल का उपयोग करना होगा।

न घुलनेवाली तलछट


1
यह WHK में FK संदर्भ के कारण एक पूर्ण तालिका स्कैन नहीं होगा। केवल मास्टर के विवरण को स्कैन किया जाएगा। प्रदर्शन की समस्या वह बूँद डेटा लोड करने से थी, न कि रिकॉर्ड गणना से। यह मानते हुए कि आमतौर पर मास्टर रिकॉर्ड के अनुसार विवरण रिकॉर्ड + के दसियों नहीं हैं, मैं वास्तव में धीमा नहीं है, जो कुछ "अनुकूलन" नहीं करेगा।
क्रेग स्टंट्ज़

ठीक है, हाँ, उस स्थिति में, आप केवल एक सबसेट का चयन करेंगे - जो ठीक होना चाहिए। बूँद डेटा के लिए - मैं इस धारणा के तहत था कि आप इसे लोड करने से बचने के लिए अपने ईएफ तालिकाओं में से किसी में किसी भी स्तंभ पर "स्थगित लोडिंग" सेट कर सकते हैं, ताकि मदद मिल सके।
marc_s

क्या EntityFramework के साथ इस SQL ​​का उपयोग करने का कोई तरीका है? वैसे भी, इस मामले में मुझे केवल यह जानना चाहिए कि मिलान पंक्तियाँ थीं, लेकिन मैंने जानबूझकर प्रश्न को अधिक सामान्यतः पूछा।
एनवीआरएएम

4

इकाई संदर्भ के ExecuteStoreQuery विधि का उपयोग करें । यह पूरे परिणाम सेट को डाउनलोड करने और एक सरल पंक्ति गणना करने के लिए ऑब्जेक्ट में डिसेररलाइज़ करने से बचा जाता है।

   int count;

    using (var db = new MyDatabase()){
      string sql = "SELECT COUNT(*) FROM MyTable where FkId = {0}";

      object[] myParams = {1};
      var cntQuery = db.ExecuteStoreQuery<int>(sql, myParams);

      count = cntQuery.First<int>();
    }

6
यदि आप लिखते हैं int count = context.MyTable.Count(m => m.MyContainerID == '1')तो उत्पन्न एसक्यूएल ठीक उसी तरह से समान होगा जैसा आप कर रहे हैं, लेकिन कोड बहुत अच्छा है। किसी भी संस्था को मेमोरी में लोड नहीं किया जाता है। यदि आपको पसंद है तो इसे LINQPad में आज़माएं - यह आपको कवर के तहत प्रयुक्त SQL दिखाएगा।
आकर्षित नोक

इन-लाइन एसक्यूएल। । मेरी पसंदीदा चीज नहीं।
डुआनने

3

मुझे लगता है कि यह काम करना चाहिए ...

var query = from m in context.MyTable
            where m.MyContainerId == '1' // or what ever the foreign key name is...
            select m;

var count = query.Count();

यह वह दिशा है जो मैं पहले भी गया था, लेकिन यह मेरी समझ है कि जब तक आपने इसे मैन्युअल रूप से नहीं जोड़ा है, तब तक मेरे पास MyContainer संपत्ति होगी, लेकिन MyContainerId नहीं। इसलिए, आप जो जांच करना चाहते हैं वह m.MyContainer.ID है।
केविन

यदि MyContainer माता-पिता हैं और MyTable बच्चे हैं, तो आपको कुछ विदेशी कुंजी के साथ उस संबंध को स्थापित करना था, मुझे यकीन नहीं है कि आप और कैसे जान पाएंगे कि कौन से MyTable संस्थाएं जहां MyContainer इकाई से जुड़ी हैं - लेकिन शायद मैं संरचना के बारे में एक धारणा बनाई गई ...
बाइटबेंडर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.