HowTo – Guida ASP.NET (Parte 8)


PARTE 3 – CAPITOLO 8 – LINQ

LINQ (acronimo di Language INtegrated Query) è un set di estensioni del linguaggio che permettono di effettuare query su diversi tipi di entità senza abbandonare il linguaggio C# ed i suoi costrutti.
Esistono diverse estensioni per gestire differenti componenti: LINQ to Object (query su collection di oggetti in memoria come, ad esempio, le liste), LINQ to DataSet (query su collection di DataSet in memoria), LINQ to SQL (interroga un SQL Server senza scrivere Data Access Code), LINQ to XML (manipola XML senza usare le classi ed i metodi dedicati).
LINQ-To-Object (forse il tipo più usato insieme a LINQ-To-SQL) è utile per sostituire i cicli (tipicamente i foreach) sugli oggetti in memoria (ad esempio su una lista). Come viene indicato subito sotto è necessario memorizzare i risultati di una query LINQ in un oggetto di tipo IEnumerable.
Di norma si crea una variabile di tipo IEnumerable (in generale IEnumerable) e si lancia la query nella forma var = from CAMPO in TABELLA where CAMPO.METODO.SOTTOMETODO() select CAMPO (più eventuali proiezioni).
Nella SELECT è possibile rinominare i campi con SELECT NEW {NEWNAME = CAMPO (più eventuali sottocampi)}.
È possibile ordinare i valori ottenuti aggiungendo la direttiva orderby dopo la FROM e prima della SELECT. È oltretutto possibile, nel caso si utilizzi una entità come sorgente, selezionare solamente alcune proprietà con Entità.Proprietà (ad esempio employee.FirstName + employee.LastName). Riepilogando è possibile procedere con una Anonymous Class o con una Entità definita: con var (non posso definire un IEnumerable in questo caso) matches = from employee in employees select new {First = employee.FirstName, Last = employee.LastName} creo una nuova anonymous class che contiene I parametric First e Last e che può essere usata localmente e/o per un binding (foreach employee {…First, Last…}). Se definissi una classe con First e Last potrei usare IEnumerable matches = from employee in employees select new EmployeeName {FirstName = employee.FirstName, LastName = employee.LastName}.
Le clausole where possono essere dichiarate inline, prima della select, oppure tramite un metodo (ad esempio private bool TestEmployee(EmployeeDetails employee) { return employee.LastName.StartsWith(“D”) }) da inserire poi in matches = from employee in employees where TestEmployee(employee) select employee;
È possibile raggruppare i valori ottenuti aggiungendo la direttiva group CAMPO by CAMPO.PROPRIETA dopo la FROM e prima della SELECT (di solito i gruppi così creati hanno alias “g”).
L’ordinamento si effettua con una o più “orderby” separate da virgola prima della “select” (se l’oggetto implementa IComparable).

LINQ mette in atto la Deferred Execution: gli operatori vengono eseguiti non durante la loro costruzione ma nel momento in cui vengono enumerati (tipicamente con il costrutto ToList()).
Gli Extension Methods vengono utilizzati quando si richiamano proprietà direttamente sugli oggetti LINQ invece che sugli oggetti C# (ad esempio il .Count()).
Un Extension Method deve essere statico e come primo parametro deve accettare una referenza all’oggetto su cui viene chiamato (preceduto da this).
Le nuove versioni di Microsoft .NET Framework permettono di eseguire anche “Lambda Expressions”, ovvero query LINQ nella forma matches = employees.Select(employee => employee).
In generale le Lambda Expressions permettono di aggiungere parametri con (p => p.UnitPrice). employee => employee (il primo è la referenza all’oggetto, il secondo è ciò che viene fatto su ogni elemento o valore di ritorno).
Quando uso LINQ-To-DataSet devo usare l’Extension Method dataRow.Field(“FirstName”); per ovviare al problema dei dati non tipizzati del dataset. Inoltre devo implementare l’Extension Method “AsEnumerable” sugli oggetti datatable per renderli IENumerable-compatibili.
Per rendere possibile il Data Bind occorre dichiarare var matches = from employee in s.Tables[“Employees”].AsEnumerable() where employee.Field(“LastName”).StartsWith(“D”) select employee; gridEmployees.DataSource = matches.AsDataView(); gridEmployees.DataBind().
LINQ-To-(Sql)Server: traduce istruzioni LINQ in istruzioni SQL Server. In generale non offre alcuna feature che non sia disponibile anche con ADO.NET, ma si scrive meno codice, si usa la stessa sintassi su più entità e si possono batchare le istruzioni.
È necessario creare gli attributi tables e columns nella classe in cui si vogliono mappare gli elementi. [Table(Name=”Employees”)] public class EmployeeDetails { [Column(IsPrimaryKey=true)] public int EmployeeID { get; set; } public EmployeeDetails(int employeeID, string firstName, string lastName, string titleOfCourtesy) { EmployeeID = employeeID; FirstName = firstName; LastName = lastName; TitleOfCourtesy = titleOfCourtesy; } public EmployeeDetails(){} }
DataContext è la classe che si preoccupa di effettuare il fetch dei dati dal DB all’oggetto al momento dell’enumerazione.
Non tutta la sintassi di LINQ ha corrispondenze con SQL. In questo caso si ottengono i dati con LINQ To Sql e poi si effettuano le query con LINQ To Object (occorre dichiarare un cast per le “table” da IQueriable a IEnumerable con ASEnumerable()).
Con .Single() o SingleOrDefault() è possibile ricercare una particolare tupla (o ricevere un valore nullo).
EntitySet permette di definire le associazioni [Association(Storage=”orders”, OtherKey=”CustomerID”, ThisKey=”CustomerID”)] che vanno dichiarate prima del costruttore.
Per forzare il load immediato dei dati bisogna settare l’opzione LoadOptions del DataContext.
Con AssociateWith è possibile associare una entità ad un’altra, di solito per filtrare una query (esempio: clienti con ordini superiori a…).
Di default quando viene chiamato un “submitchanges” su un oggetto dopo averlo modificato in-memory, questa operazione viene eseguita in transazione, annullando tutto se si verifica anche un solo errore. Inserimento e aggiornamento si effettuano con “insertonupdate” e “deleteonupdate”. Il cambiamento di eventuali relazioni viene fatto in automatico.
Per gestire la concorrenza esistono 4 modi e sono gli stessi già visti in altre occasioni: match-all-value (attributo di colonna >> UpdtateCheck = always), match-timestamp (proprietà timestamp), last-in-wins (UpdateCheck = never) e merge (updatecheck = WhenChanged).
Usando LinqDataSource posso automatizzare la maggior parte dei processi legati all’allineamento con il DB.
Posso poi mostrare i dati in un “<asp:TemplateField HeaderText=”# Linked Territories”>  <ItemTemplate> <%# Eval(“EmployeeTerritories.Count”) %> </ItemTemplate></asp:TemplateField>”.
Il modello DBML offre tre possibilità per effettuare la validazione: quando si setta una proprietà (ONXXXChanging), quando si inviano i dati (OnValidate) o in risposta a specifiche operazioni di aggiornamento.

[Omnia / Luca Zaccaro]


Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *