2013-04-09 8 views
6

Ho un determinato campo calcolato che desidero regolarmente restituire nei campi "select" di una query Linq, ad es. L'ordine del cliente è totale quest'anno, insieme ad altre informazioni demografiche del cliente.Come astrarre un campo nell'elenco Seleziona di una query Linq?

public class Customer { 
    public decimal TotalPurchasesThisYear(MyDataContext db) { 
    return db.Orders.Where(o => o.CustomerID == ID) 
        .Sum(o => o.OrderTotalAmt); 
    } 
} 

public class SomeReport { 
    public void GetCustomerInfoBySalesperson(long salespersonID) { 
    using (var db = new MyDataContext()) { 
     var q = db.Customers.Where(c => c.SalespersonID == salespersonID) 
          .Select(c => new { c.Name, c.Address, ThisYearPurchases = c.TotalPurchasesThisYear(db) }) 
          .ToList(); 
     // etc.. 
    } 
    } 
} 

Ovviamente, questo non funziona, perché non ha TotalPurchasesThisYear traduzione SQL. Ma tutto all'interno di esso ha una traduzione SQL. Non voglio includere quel codice direttamente nella query, perché sto facendo lo stesso calcolo in molti posti. Il mio istinto mi dice che questo dovrebbe essere fatto con un Expression ma ho giocato intorno e non riesco a capire la sintassi giusta.

Aiuto, qualcuno? Grazie!

+0

Quello che ho appena commentare qui è che si sta violando Persistenza Ignoranza: http://msdn.microsoft.com/en-us/ rivista/dd882510.aspx. Il modello dipende dal livello persistente, ad esempio è necessario considerare l'utilizzo di Reposistory. –

+1

@CuongLe - Non sono così preoccupato per quanto riguarda strettamente il Cliente e l'Ordine; questo è solo un semplice esempio di ciò che sto cercando di fare, comunque. Sto seguendo il * principio * di come astrarre un campo nella clausola Select. –

+0

L'accoppiamento stretto è un problema che non puoi ignorare. I metodi che mostri sono repository o metodi di servizio tipici. Se non è l'ignoranza della persistenza, allora è una singola responsabilità di cui dovresti preoccuparti. Il cliente non sembra nemmeno avere una raccolta ordini, quindi non ha nulla a che fare con gli ordini. Quando un approccio causa problemi, affrontalo alla radice. Questo è solo qualcosa che non dovresti fare. Richiede una soluzione complessa che è destinata a introdurre nuovi problemi. Solo un consiglio ツ. –

risposta

0

OK, ho trovato un modo per fare questo, anche se non sono sicuro che sia il modo migliore:

public class CustomerOrderInfo { 
    public long CustomerID; 
    public decimal TotalPurchases; 
} 

public class Customer { 
    public static Expression<Func<Customer, CustomerOrderInfo>> GetCustomerOrderInfo() { 
    return c => new CustomerOrderInfo { 
        CustomerID = c.ID, 
        TotalPurchases = c.Orders.Where(o => o.OrderDate.Year == DateTime.Now.Year) 
            .Sum(o => o.OrderTotalAmt) 
       }; 
    } 
} 

public class SomeReport { 
    public void GetCustomerInfoBySalesperson(long salespersonID) { 
    using (var db = new MyDataContext()) { 
     var q = db.Customers 
       .Join(db.Customers.Select(Customer.GetCustomerOrderInfo()), 
         c => c.ID, i => i.CustomerID, 
         (c, i) => new { c.Name, c.Address, i.TotalPurchases }) 
       .ToList(); 
    // etc.. 
    } 
    } 
} 

funziona ... ma la SQL sottostante finisce con un join su un query nidificata, che non è il mio ideale. Vorrei vedere lo SQL sottostante essere qualcosa di semplice, come:

select c.Name, c.Address, sum(o.OrderTotalAmt) TotalPurchases 
from Customer c 
left join [Order] o on o.CustomerID = c.ID 
where Year(o.OrderDate) = @year 
group by c.Name, c.Address 
0
List<Parent> myParents = new List<Parent> 
             { 
              new Parent() 
              { 
               Prop1 = "1", 
               Prop2 = "2", 
               Prop3 = "3", 
               Children = new List<Child>() 
                 { 
                  new Child(){ Prop1 = 1, Prop2 = 2, Prop3 = 3 }, 
                  new Child(){ Prop1 = 21, Prop2 = 22, Prop3 = 23 }, 
                  new Child(){ Prop1 = 31, Prop2 = 32, Prop3 = 33 } 
                 } 
              }, 
             }; 

      Expression<Func<Parent, int>> GetChildSum = 
       p => p.Children.Where(c => c.Prop1 > 0).Sum(o => o.Prop2); 

      var v = myParents.Where(w => w.Prop1 == "1").Select(p => GetChildSum.Compile().Invoke(p)).ToList(); 

      Console.WriteLine(v.First()); 

      //output is 56 
+0

Ya, beh, questa è esattamente la mia situazione: io * do * ho tonnellate di righe e ho bisogno che il lavoro sia fatto sul lato SQL ... –

+0

Ok, cosa succede se restituisci il Sum dalla tua espressione invece di CustomerOrderInfo. E quindi interrogare l'espressione nell'istruzione select come hai fatto nel Question-post? –

+0

Non seguo il tuo suggerimento ... puoi modificare la tua risposta per mostrare come sarebbe? –