मैं इकाई ढांचे में शून्य मानों के लिए क्वेरी कैसे कर सकता हूं?


109

मैं इस तरह से एक क्वेरी निष्पादित करना चाहता हूं

   var result = from entry in table
                     where entry.something == null
                     select entry;

और एक IS NULLउत्पन्न हो।

संपादित: पहले दो उत्तरों के बाद मुझे स्पष्ट करने की आवश्यकता है कि मैं एंटिटी फ्रेमवर्क का उपयोग कर रहा हूं और एसक्यूएल के लिए लाइनक का नहीं। ऑब्जेक्ट। ईक्ल्स () विधि EF में काम नहीं करती है।

नंबर 2 संपादित करें: उपरोक्त क्वेरी उद्देश्य के अनुसार काम करती है। यह सही ढंग से उत्पन्न करता है IS NULL। हालाँकि मेरा प्रोडक्शन कोड था

value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;

और उत्पन्न SQL था something = @p; @p = NULL। ऐसा लगता है कि ईएफ निरंतर अभिव्यक्ति का सही अनुवाद करता है लेकिन अगर एक चर शामिल है तो यह सामान्य तुलना की तरह व्यवहार करता है। वास्तव में समझ में आता है। मैं इस प्रश्न को बंद कर दूंगा


17
मुझे लगता है कि यह वास्तव में समझ में नहीं आता है ... कनेक्टर थोड़ा स्मार्ट होना चाहिए और हमें अपना काम करने के लिए नहीं कहना चाहिए: सही सी # क्वेरी के एसक्यूएल में एक सही अनुवाद करें। यह एक अप्रत्याशित व्यवहार उत्पन्न करता है।
जुलिएन एन

6
मैं जुलिएन के साथ हूं, यह EF की ओर से एक विफलता है
श्री बेल

1
यह मानकों की विफलता है, और यह केवल अब खराब हो रही है कि अशक्त की तुलना में स्थायी रूप से अपरिष्कृत के रूप में SQL सर्वर 2016 के रूप में अपरिभाषित है जिसके परिणामस्वरूप ANSI नल स्थायी रूप से सेट है। अशक्त एक अज्ञात मूल्य का प्रतिनिधित्व कर सकता है , लेकिन "अशक्त" स्वयं एक अज्ञात मूल्य नहीं है। एक शून्य मान के साथ एक शून्य मान की तुलना पूरी तरह से सही होनी चाहिए, लेकिन दुर्भाग्य से मानक सामान्य ज्ञान के साथ-साथ बूलियन तर्क भी छोड़ देता है।
त्रिवेंको

जवाबों:


126

Linq-to-SQL के लिए समाधान:

var result = from entry in table
             where entry.something.Equals(value)
             select entry;

Linq-to-Entities (ouch!) के लिए समाधान:

var result = from entry in table
             where (value == null ? entry.something == null : entry.something == value)
             select entry;

यह एक बुरा बग है जो मुझे कई बार काट चुका है। यदि इस बग ने आपको प्रभावित किया है, तो कृपया UserVoice पर बग रिपोर्ट देखें और Microsoft को बताएं कि इस बग ने आपको भी प्रभावित किया है।


संपादित करें: यह बग EF 4.5 में तय किया जा रहा है ! इस बग को उभारने के लिए सभी का धन्यवाद!

पश्चगामी संगतता के लिए, यह ऑप्ट-इन होगा - आपको मैन्युअल रूप से entry == valueकार्य करने के लिए सेटिंग सक्षम करने की आवश्यकता है । यह सेटिंग क्या है, इस पर अभी तक कोई शब्द नहीं है। बने रहें!


एडिट 2: EF टीम द्वारा इस पोस्ट के अनुसार , इस मुद्दे को EF6 में तय किया गया है! वू हू!

हमने तीन-मूल्यवान तर्क की भरपाई के लिए EF6 के डिफ़ॉल्ट व्यवहार को बदल दिया।

इसका मतलब यह है कि मौजूदा कोड जो पुराने व्यवहार पर निर्भर करता है ( null != nullलेकिन केवल जब एक चर की तुलना में) या तो उस व्यवहार पर भरोसा नहीं करने के लिए बदलना होगा, या UseCSharpNullComparisonBehaviorपुराने टूटे हुए व्यवहार का उपयोग करने के लिए झूठे पर सेट करना होगा।


6
मैंने बग रिपोर्ट को वोट दिया है। उम्मीद है कि वे इसे ठीक कर लें। मैं यह नहीं कह सकता कि मैं वास्तव में इस बग को vs2010 बीटा में मौजूद होने के बारे में याद करता हूं ...
noobish

2
ओह माइक्रोसॉफ्ट पर आओ ... सच में?!?!? संस्करण 4.1 में?!?! +1
डेविड

1
कि Linq-To-SQL वर्कअराउंड काम करने के लिए नहीं लगता है (एक गाइड के साथ कोशिश कर रहा है?)। एंटिटीज-वर्कअराउंड L2S में काम करता है, लेकिन भयावह SQL उत्पन्न करता है। मैं कोड में कोई अगर-कथन करना ही था(var result = from ...; if(value.HasValue) result = result.Where(e => e.something == value) else result = result.Where(e => e.something == null);
माइकल Stum

5
Object.Equals वास्तव में काम करता है(where Object.Equals(entry.something,value))
माइकल Stum

5
@ leen3o (या कोई और) - क्या किसी को अभी तक यह पता चला है कि यह कथित फिक्स ईएफ 4.5 / 5.0 में कहां है? मैं 5.0 का उपयोग कर रहा हूं और यह अभी भी दुर्व्यवहार कर रहा है।
शुल बेहार

17

एंटिटी फ्रेमवर्क 5.0 के बाद से आप अपनी समस्या को हल करने के लिए निम्नलिखित कोड का उपयोग कर सकते हैं:

public abstract class YourContext : DbContext
{
  public YourContext()
  {
    (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true;
  }
}

यह आपकी समस्याओं को हल करना चाहिए क्योंकि Entity Framerwork शून्य तुलना की तरह 'C # का उपयोग करेगी।


16

LINQ से एंटिटीज़ के साथ काम करने वाला थोड़ा सरल तरीका है:

var result = from entry in table
         where entry.something == value || (value == null && entry.something == null)
         select entry;

यह कार्य पुन: उपयोग करता है, जैसा कि AZ ने देखा, LINQ से एंटिटीज विशेष मामलों के लिए x == null (यानी null कॉन्स्टेंट के खिलाफ एक समानता तुलना) और इसे x IS NULL में अनुवाद करता है।

हम वर्तमान में इस व्यवहार को बदलने पर विचार कर रहे हैं ताकि यदि समानता के दोनों पक्ष अशक्त हों, तो प्रतिपूरक तुलना की शुरुआत करने के लिए। हालांकि कुछ चुनौतियाँ हैं:

  1. यह संभावित रूप से कोड को तोड़ सकता है जो पहले से ही मौजूदा व्यवहार पर निर्भर करता है।
  2. नया अनुवाद मौजूदा प्रश्नों के प्रदर्शन को तब भी प्रभावित कर सकता है जब कोई अशक्त पैरामीटर शायद ही कभी उपयोग किया जाता है।

किसी भी मामले में, चाहे हमें इस पर काम करना हो, हमारे ग्राहक इसे प्रदान करने वाली सापेक्ष प्राथमिकता पर बहुत अधिक निर्भर होने वाले हैं। यदि आप इस मुद्दे की परवाह करते हैं, तो मैं आपको हमारी नई फ़ीचर सुझाव साइट में इसके लिए वोट करने के लिए प्रोत्साहित करता हूं: https://data.uservoice.com


9

यदि यह एक अशक्त प्रकार है, तो होशवेल संपत्ति का उपयोग करने का प्रयास करें।

var result = from entry in table
                 where !entry.something.HasValue
                 select entry;

हालांकि यहाँ पर परीक्षण करने के लिए कोई EF नहीं है ... बस एक सुझाव =)


1
ठीक है ... यह केवल तभी काम करता है जब आप सिर्फ नल की तलाश में हैं, लेकिन फिर == nullवैसे भी बग की चपेट में नहीं आता है। बिंदु को एक चर के मूल्य से फ़िल्टर किया जाना है, जिसका मूल्य शून्य हो सकता है, और शून्य मान को शून्य रिकॉर्ड मिल सकता है।
डेव कज़िनो

1
आपके उत्तर ने मुझे बचा लिया। मैं अपनी इकाई मॉडल वर्ग पर एक अशक्त प्रकार का उपयोग करना भूल गया और (x => x.Column == null)काम नहीं कर सका । :)
रिबेल रिबेरो

यह देता है System.NullReferenceException , चूंकि ऑब्जेक्ट पहले से ही अशक्त है!
तियाबाम


5

के Object.Equals()बजाय अशक्त तुलना का उपयोग करने से निपटने के लिए==

इस संदर्भ की जाँच करें


यह Linq-To-Sql में पूरी तरह से काम करता है और उचित SQL (कुछ अन्य उत्तर यहां भयावह SQL या गलत परिणाम उत्पन्न करता है) भी उत्पन्न करता है।
माइकल स्टम

मान लीजिए मैं साथ compaire करना चाहते हैं null, Object.Equals(null)क्या करता है, तो Objectअपने आप में शून्य है?
तियाबाम

4

यह इंगित करते हुए कि सभी एंटिटी फ्रेमवर्क <6.0 सुझाव कुछ अजीब एसक्यूएल उत्पन्न करते हैं। "क्लीन" फिक्स के लिए दूसरा उदाहरण देखें।

हास्यास्पद काम करने का तरीका

// comparing against this...
Foo item = ...

return DataModel.Foos.FirstOrDefault(o =>
    o.ProductID == item.ProductID
    // ridiculous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948
    && item.ProductStyleID.HasValue ? o.ProductStyleID == item.ProductStyleID : o.ProductStyleID == null
    && item.MountingID.HasValue ? o.MountingID == item.MountingID : o.MountingID == null
    && item.FrameID.HasValue ? o.FrameID == item.FrameID : o.FrameID == null
    && o.Width == w
    && o.Height == h
    );

SQL में परिणाम जैसे:

SELECT TOP (1) [Extent1].[ID]                 AS [ID],
       [Extent1].[Name]               AS [Name],
       [Extent1].[DisplayName]        AS [DisplayName],
       [Extent1].[ProductID]          AS [ProductID],
       [Extent1].[ProductStyleID]     AS [ProductStyleID],
       [Extent1].[MountingID]         AS [MountingID],
       [Extent1].[Width]              AS [Width],
       [Extent1].[Height]             AS [Height],
       [Extent1].[FrameID]            AS [FrameID],
FROM   [dbo].[Foos] AS [Extent1]
WHERE  (CASE
  WHEN (([Extent1].[ProductID] = 1 /* @p__linq__0 */)
        AND (NULL /* @p__linq__1 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[ProductStyleID] = NULL /* @p__linq__2 */) THEN cast(1 as bit)
      WHEN ([Extent1].[ProductStyleID] <> NULL /* @p__linq__2 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[ProductStyleID] IS NULL)
        AND (2 /* @p__linq__3 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[MountingID] = 2 /* @p__linq__4 */) THEN cast(1 as bit)
      WHEN ([Extent1].[MountingID] <> 2 /* @p__linq__4 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[MountingID] IS NULL)
        AND (NULL /* @p__linq__5 */ IS NOT NULL)) THEN
    CASE
      WHEN ([Extent1].[FrameID] = NULL /* @p__linq__6 */) THEN cast(1 as bit)
      WHEN ([Extent1].[FrameID] <> NULL /* @p__linq__6 */) THEN cast(0 as bit)
    END
  WHEN (([Extent1].[FrameID] IS NULL)
        AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
        AND ([Extent1].[Height] = 16 /* @p__linq__8 */)) THEN cast(1 as bit)
  WHEN (NOT (([Extent1].[FrameID] IS NULL)
             AND ([Extent1].[Width] = 20 /* @p__linq__7 */)
             AND ([Extent1].[Height] = 16 /* @p__linq__8 */))) THEN cast(0 as bit)
END) = 1

अपमानजनक वर्कअराउंड

यदि आप क्लीनर SQL उत्पन्न करना चाहते हैं, तो कुछ इस तरह है:

// outrageous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948
Expression<Func<Foo, bool>> filterProductStyle, filterMounting, filterFrame;
if(item.ProductStyleID.HasValue) filterProductStyle = o => o.ProductStyleID == item.ProductStyleID;
else filterProductStyle = o => o.ProductStyleID == null;

if (item.MountingID.HasValue) filterMounting = o => o.MountingID == item.MountingID;
else filterMounting = o => o.MountingID == null;

if (item.FrameID.HasValue) filterFrame = o => o.FrameID == item.FrameID;
else filterFrame = o => o.FrameID == null;

return DataModel.Foos.Where(o =>
    o.ProductID == item.ProductID
    && o.Width == w
    && o.Height == h
    )
    // continue the outrageous workaround for proper sql
    .Where(filterProductStyle)
    .Where(filterMounting)
    .Where(filterFrame)
    .FirstOrDefault()
    ;

पहली जगह में आप क्या चाहते थे:

SELECT TOP (1) [Extent1].[ID]                 AS [ID],
           [Extent1].[Name]               AS [Name],
           [Extent1].[DisplayName]        AS [DisplayName],
           [Extent1].[ProductID]          AS [ProductID],
           [Extent1].[ProductStyleID]     AS [ProductStyleID],
           [Extent1].[MountingID]         AS [MountingID],
           [Extent1].[Width]              AS [Width],
           [Extent1].[Height]             AS [Height],
           [Extent1].[FrameID]            AS [FrameID],
FROM   [dbo].[Foos] AS [Extent1]
WHERE  ([Extent1].[ProductID] = 1 /* @p__linq__0 */)
   AND ([Extent1].[Width] = 16 /* @p__linq__1 */)
   AND ([Extent1].[Height] = 20 /* @p__linq__2 */)
   AND ([Extent1].[ProductStyleID] IS NULL)
   AND ([Extent1].[MountingID] = 2 /* @p__linq__3 */)
   AND ([Extent1].[FrameID] IS NULL)

SQL पर चलने वाला कोड क्लीनर और तेज़ होगा लेकिन EF, sql सर्वर पर भेजने से पहले हर संयोजन के लिए नई क्वेरी योजना बनाएगा, जो इसे अन्य वर्कअराउंड्स की तुलना में धीमा बनाता है।
बुरक तमतर्क

2
var result = from entry in table
                     where entry.something == null
                     select entry;

उपरोक्त क्वेरी इरादा के अनुसार काम करती है। यह IS NULL को सही ढंग से जनरेट करता है। हालाँकि मेरा प्रोडक्शन कोड था

var value = null;
var result = from entry in table
                         where entry.something == value
                         select entry;

और उत्पन्न SQL कुछ = @ पी था; @ पी = पूरा। ऐसा लगता है कि ईएफ निरंतर अभिव्यक्ति का सही अनुवाद करता है लेकिन अगर एक चर शामिल है तो यह सामान्य तुलना की तरह व्यवहार करता है। वास्तव में समझ में आता है।


1

ऐसा प्रतीत होता है कि Linq2Sql में यह "समस्या" भी है। ऐसा प्रतीत होता है कि ANSI NULLs ON या OFF होने के कारण इस व्यवहार का एक वैध कारण है, लेकिन यह दिमाग को चकरा देता है कि एक सीधा "== अशक्त" काम क्यों करेगा जैसा कि आप उम्मीद करेंगे।


1

व्यक्तिगत रूप से, मैं पसंद करता हूं:

var result = from entry in table    
             where (entry.something??0)==(value??0)                    
              select entry;

ऊपर

var result = from entry in table
             where (value == null ? entry.something == null : entry.something == value)
             select entry;

क्योंकि यह दोहराव को रोकता है - हालांकि यह गणितीय रूप से सटीक नहीं है, लेकिन यह ज्यादातर मामलों में अच्छी तरह से फिट बैठता है।


0

मैं डिवेगा की पोस्ट पर टिप्पणी नहीं कर पा रहा हूं, लेकिन यहां प्रस्तुत विभिन्न समाधानों के बीच, डिवेगा का समाधान सर्वश्रेष्ठ एसक्यूएल का उत्पादन करता है। दोनों प्रदर्शन बुद्धिमान और लंबाई बुद्धिमान। मैंने सिर्फ SQL सर्वर प्रोफाइलर के साथ और निष्पादन योजना ("सेट सांख्यिकी प्रोफाइल पर") को देखकर जाँच की।


0

दुर्भाग्य से एंटिटी फ्रेमवर्क 5 में DbContext मुद्दा अभी भी तय नहीं है।

मैंने इस वर्कअराउंड का उपयोग किया (MSSQL 2012 के साथ काम करता है, लेकिन ANSI NULLS सेटिंग को भविष्य के MSSQL संस्करण में चित्रित किया जा सकता है)।

public class Context : DbContext
{

    public Context()
        : base("name=Context")
    {
        this.Database.Connection.StateChange += Connection_StateChange;
    }

    void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e)
    {
        // Set ANSI_NULLS OFF when any connection is opened. This is needed because of a bug in Entity Framework
        // that is not fixed in EF 5 when using DbContext.
        if (e.CurrentState == System.Data.ConnectionState.Open)
        {
            var connection = (System.Data.Common.DbConnection)sender;
            using (var cmd = connection.CreateCommand())
            {
                cmd.CommandText = "SET ANSI_NULLS OFF";
                cmd.ExecuteNonQuery();
            }
        }
    }
}

यह ध्यान दिया जाना चाहिए कि यह एक गंदा काम है, लेकिन यह एक है जिसे बहुत तेज़ी से लागू किया जा सकता है और सभी प्रश्नों के लिए काम करता है।


एक बार ANSI NULLS स्थायी रूप से SQL सर्वर के भविष्य के संस्करण में चालू होने पर, चेतावनी के स्पष्ट होने पर यह कार्य करना बंद कर देगा।
ट्रायन्को

0

यदि आप विधि (लैम्ब्डा) सिंटैक्स का उपयोग करना पसंद करते हैं जैसा कि मैं करता हूं, तो आप इस तरह से काम कर सकते हैं:

var result = new TableName();

using(var db = new EFObjectContext)
{
    var query = db.TableName;

    query = value1 == null 
        ? query.Where(tbl => tbl.entry1 == null) 
        : query.Where(tbl => tbl.entry1 == value1);

    query = value2 == null 
        ? query.Where(tbl => tbl.entry2 == null) 
        : query.Where(tbl => tbl.entry2 == value2);

    result = query
        .Select(tbl => tbl)
        .FirstOrDefault();

   // Inspect the value of the trace variable below to see the sql generated by EF
   var trace = ((ObjectQuery<REF_EQUIPMENT>) query).ToTraceString();

}

return result;

-1
var result = from entry in table    
             where entry.something == value||entry.something == null                   
              select entry;

उसका उपयोग करें


5
यह बहुत गलत है क्योंकि यह उन सभी प्रविष्टियों का चयन करेगा जहाँ मूल्य मेल खाता है और सभी प्रविष्टियाँ जहाँ कुछ शून्य है, भले ही आप मूल्य पूछें।
माइकल स्टम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.