2009-07-16 10 views
1

zu optimieren Ich freue mich auf meine LINQ-Abfrage zu optimieren, denn obwohl es richtig funktioniert, erzeugt das SQL es verworren und ineffizient ist ...Hilfe erforderlich ist, um LINQ-Abfrage

Grundsätzlich suche ich nach Kunden wählen (als CustomerDisplay Objekte), die das gewünschte Produkt (reqdProdId) bestellt und sind mit einer Kreditkartennummer (gespeichert als eine Zeile in RegisteredCustomer Tabelle mit einem Fremdschlüssel CustId)

var q = from cust in db.Customers 
     join regCust in db.RegisteredCustomers on cust.ID equals regCust.CustId 
     where cust.CustomerProducts.Any(co => co.ProductID == reqdProdId) 
     where regCust.CreditCardNumber != null && regCust.Authorized == true 
     select new CustomerDisplay 
      { 
       Id = cust.Id, 
       Name = cust.Person.DisplayName, 
       RegNumber = cust.RegNumber 
      }; 

als Überblick registriert, hat ein Kunde eine entsprechende Person der hat den Namen; PersonID ist ein Fremdschlüssel in Customer-Tabelle. Wenn ich das generierte SQL ansehe, sehe ich, dass alle Spalten aus der Tabelle Person ausgewählt werden. Fyi, DisplayName ist eine Erweiterungsmethode, die Customer.FirstName und LastName verwendet. Irgendwelche Ideen, wie ich die Spalten von Person begrenzen kann?

Zweitens möchte ich die Any-Klausel loswerden (und eine Unterabfrage verwenden), um alle anderen CustomerIds auszuwählen, die die erforderliche ProductID haben, weil sie (verständlicherweise) eine Exists-Klausel generiert. Wie Sie vielleicht wissen, hat LINQ ein bekanntes Problem mit Junction-Tabellen, so dass ich nicht einfach ein cust.CustomerProducts.Products ausführen kann. Wie kann ich alle Kunden in der Junction-Tabelle mit der erforderlichen ProductID auswählen?

Jede Hilfe/Beratung wird geschätzt.

Antwort

1

Der erste Schritt ist, Ihre Abfrage von CustomerProducts zu starten (als Alex Said):

IQueryable<CustomerDisplay> myCustDisplay = 
    from custProd in db.CustomerProducts 
    join regCust in db.RegisteredCustomers 
     on custProd.Customer.ID equals regCust.CustId 
    where 
     custProd.ProductID == reqProdId 
     && regCust.CreditCardNumber != null 
     && regCust.Authorized == true 
    select new CustomerDisplay 
    { 
     Id = cust.Id, 
     Name = cust.Person.Name, 
     RegNumber = cust.RegNumber 
    }; 

Dies wird Ihre Syntax vereinfachen und hoffentlich zu einem besseren Ausführungsplan führen.

Als Nächstes sollten Sie in Erwägung ziehen, eine Fremdschlüsselbeziehung zwischen Kunden und registrierten Kunden zu erstellen. Dies würde zu einer Abfrage führen, die so aussah:

IQueryable<CustomerDisplay> myCustDisplay = 
    from custProd in db.CustomerProducts 
    where 
     custProd.ProductID == reqProdId 
     && custProd.Customer.RegisteredCustomer.CreditCardNumber != null 
     && custProd.Customer.RegisteredCustomer.Authorized == true 
    select new CustomerDisplay 
    { 
     Id = cust.Id, 
     Name = cust.Person.Name, 
     RegNumber = cust.RegNumber 
    }; 

schließlich für eine optimale Geschwindigkeit, haben LINQ Abfrage bei der Kompilierung kompiliert, anstatt Zeit zu laufen durch eine kompilierte Abfrage:

Func<MyDataContext, SearchParameters, IQueryable<CustomerDisplay>> 
    GetCustWithProd = 
    System.Data.Linq.CompiledQuery.Compile(
     (MyDataContext db, SearchParameters myParams) => 
     from custProd in db.CustomerProducts 
     where 
      custProd.ProductID == myParams.reqProdId 
      && custProd.Customer.RegisteredCustomer.CreditCardNumber != null 
      && custProd.Customer.RegisteredCustomer.Authorized == true 
     select new CustomerDisplay 
     { 
      Id = cust.Id, 
      Name = cust.Person.Name, 
      RegNumber = cust.RegNumber 
     }; 
    ); 

Sie können die kompilierte Abfrage wie folgt aufrufen:

IQueryable<CustomerDisplay> myCustDisplay = GetCustWithProd(db, myParams); 
+0

Thansk Lame Duck, dachte nie an kompilierte LINQ ... wird hineinschauen. Das ist ein paar nette funktionale Programmier-Sachen. Auch ich einen Fremdschlüssel zwischen dem Kunden haben und RegisteredCustomer, und ich bin nicht sicher, ob es einen Unterschied zwischen ist custProd.Customer.RegisteredCustomer.CreditCardNumber! = Null (wie Sie vorschlagen) und custProd.CreditCardNumber! = null (wie ich hatte) LINQ die custProd findet heraus ist die Zeile in der Verknüpfung und der SQL erzeugt war INNER JOIN RegisteredCustomer als t2 auf t1.Id = t2.CustomerID ... ... t2 .CreditCardNumber ist NICHT NULL –

+0

Erneutes Kompilieren der Abfrage: Die Abfrage wird zur Laufzeit noch kompiliert, nicht zur Kompilierzeit. Dies hilft nur, wenn Sie dieselbe Abfrage mehrmals verwenden, da Sie die kompilierte Abfrage zwischenspeichern und wiederverwenden können. – Lucas

0

Ich würde vorschlagen, Ihre Anfrage von dem fraglichen Produkt, z. so etwas wie:

from cp in db.CustomerProducts 
join ..... 
where cp.ProductID == reqdProdID 
+0

vielen Dank Alex. Ich drehte den LINQ herum und es kümmerte sich um den Junction-Table-Kram. Jeder hat irgendwelche Einblicke in die Frage der Erweiterungsmethode? –

+0

Ich hätte angenommen, dass, wenn Ihr Code nur auf Person.FirstName und Person.LastName zugreift, das alles ausgewählt wäre. Long shot: hast du es ohne die extension-methode versucht? z.B. Name = cust.Person.FirstName + "" + cust.Person.LastName –

+0

Ich stimme zu, ich habe es in LINQPad getestet und festgestellt, dass, wenn Sie die beiden Felder direkt angeben, DisplayName = cust.FirstName + "" + cust.LastName, die SQL, die erzeugt wird, hat nur diese beiden Felder –

0

wie Sie gefunden haben, definiert eine Eigenschaft mit als Erweiterungsfunktion oder in einer Teilklasse erfordert, dass das gesamte Objekt zunächst mit Feuchtigkeit versorgt wird und dann wird die Auswahlprojektion auf der Client-Seite ausgeführt, da der Server diese zusätzlichen Eigenschaften nicht kennt. Sei froh, dass dein Code überhaupt gelaufen ist. Wenn Sie den nicht zugeordneten Wert an einer anderen Stelle in Ihrer Abfrage verwenden würden (anders als in der Projektion), würden Sie wahrscheinlich eine Laufzeitausnahme sehen. Sie können dies sehen, wenn Sie versuchen, die Customer.Person.DisplayName-Eigenschaft in einer Where-Klausel zu verwenden. Wie Sie herausgefunden haben, besteht die Lösung darin, die String-Verkettung in der Projektionsklausel direkt auszuführen.

Lame Duck, ich glaube, es gibt einen Fehler in Ihrem Code, da die cust-Variable, die in Ihrer SELECT-Klausel verwendet wird, nicht als lokale Quellenvariable (in den from-Klauseln) deklariert wird.

Verwandte Themen