2016-08-23 2 views
0

Hintergrund: Ich arbeite an einem System, um eine interne Kundenliste zu bereinigen und herauszufinden, E-Mail-Adressen von Kontakten, wo wir die E-Mail-Adresse von jemand anderem bereits bei dieser Firma haben. Um dies zu tun, ich habe (vereinfacht) 3 Tabellen:Warum enthält Linq das Generieren dieses SQL?

Kontakte:

ID 
CompanyId 
Email 
Domain 

EmailDomains:

ID 
Domain 
EmailFormat 
EmailFormatConfirmed 

Ich habe eine manuelle Routine, die sagt, angesichts der Unternehmen, finden Sie den nächsten Kontakt, wo wir ihren Domain-Namen haben, aber nicht ihre E-Mail-Adresse:

int companyId = 53; 

var emails = Contacts.Where(p => p.companyId == companyId 
              && p.Email == null 
              && !string.IsNullOrEmpty(p.Domain)).Select(p => p.Domain); 
var domain = 
    EmailDomains.FirstOrDefault(
     d => !d.EmailFormatConfirmed 
      && !string.IsNullOrEmpty(d.Domain) 
      && emails.Contains(d.Domain)); 

Diese Abfrage läuft sehr langsam und auf dem SQL-Check-out generiert:

-- Region Parameters 
DECLARE @p__linq__0 Int = 53 
-- EndRegion 
SELECT TOP (1) 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Domain] AS [Domain], 
    [Extent1].[EmailFormat] AS [EmailFormat], 
    [Extent1].[EmailFormatConfirmed] AS [EmailFormatConfirmed], 
    FROM [dbo].[EmailDomain] AS [Extent1] 
    WHERE ([Extent1].[EmailFormatConfirmed] <> 1) 
     AND (NOT (([Extent1].[Domain] IS NULL) OR ((LEN([Extent1].[Domain])) = 0))) 
     AND (EXISTS (
      SELECT 1 AS [C1] 
      FROM [dbo].[Contacts] AS [Extent2] 
      WHERE ([Extent2].[CompanyId] = @p__linq__0) 
       AND ([Extent2].[Email] IS NULL) 
        AND (NOT (([Extent2].[Domain] IS NULL) OR ((LEN([Extent2].[Domain])) = 0))) 
        AND (([Extent2].[Domain] = [Extent1].[Domain]) OR (([Extent2].[Domain] IS NULL) AND ([Extent1].[Domain] IS NULL))) 
    )) 

ich, dass der säumige Teil OR (([Extent2].[Domain] IS NULL) AND ([Extent1].[Domain] IS NULL)) Klausel am Ende der existiert, ist zu sehen. Warum sollte das überhaupt dort sein? Ich kann nicht verstehen, wie es gültig ist, und wenn ich den Sql von Hand kodiere (was derzeit der Fall ist, auf den ich zurückgreifen muss), wäre es nicht so. Fehle ich etwas Offensichtliches? Das Entfernen dieser die Abfrage sehr schnell laufen lässt (wie zu erwarten wäre - es gibt eine Menge von null Domänen sind, die effektiv hier Kreuz verbunden werden)

+0

Es ist notwendig, 'NULL' Fälle zu behandeln, da in SQL' NULL! = NULL' ist. Dies führt zu 'value1 = value2 OR (Wert1 IST NULL UND Wert2 IST NULL)'. – Maarten

+0

Welche Version von EF verwenden Sie? Vielleicht können Sie [this] (https://msdn.microsoft.com/en-us/library/system.data.objects.objectcontextoptions.usecsharpnullcomparisonbehavior.aspx) verwenden, siehe auch [this] (http://stackoverflow.com)/a/2541042/261050) – Maarten

+0

@Maarten - mit EF6.Ich bin jedoch nicht sicher, ob Sie das Problem bekommen - ich könnte dies in Sql als CTE oder Sub Query schreiben, die die Kontakt-Domains für das angegebene Unternehmen ohne E-Mails abrufen und dann innerlich mit der EmailDomains-Tabelle verbinden würde. Ich verstehe null! = Null, aber ich möchte einfach keine Nullen berücksichtigen – Macros

Antwort

0

In EF6 dieses Verhalten durch die DbContextConfigurationUseDatabaseNullSemantics Eigenschaft gesteuert wird (standardmäßig falsch):

Ruft einen Wert ab, der angibt, ob Null-Semantik der Datenbank angezeigt wird, wenn zwei Operanden verglichen werden, die potenziell Nullwerte enthalten können, oder legt diesen fest. Der Standardwert ist false. Zum Beispiel (operand1 == operand2) wird wie folgt übersetzt: (operand1 = operand2) wenn UseDatabaseNullSemantics wahr ist (((operand1 = operand2) AND (NOT (operand1 IST NULL ODER operand2 IST NULL))) ODER ((operand1 IS NULL) UND (operand2 ist NULL))) wenn UseDatabaseNullSemantics false ist.

Also drehen Sie es einfach auf true und das Problem wird gelöst.

Das können Sie in Ihrem DbContext Konstruktor:

this.Configuration.UseDatabaseNullSemantics = true; 

oder nur für einen bestimmten Kontext Instanzen vor Ausführen von Befehlen:

var db = new YourDbContext(); 
db.Configuration.UseDatabaseNullSemantics = true; 
var emails = db.Contacts.Where(...) 
... 

Aber bitte beachten Sie, dass die Option drehen könnte einige unerwartete verursachen Ergebnisse, wenn Sie Felder mit NULL-Wert in den Abfragen vergleichen und keine expliziten null -Überprüfungen enthalten, verwenden Sie sie daher mit Vorsicht.

Verwandte Themen