Difference between revisions of "CSharp:Esempi LinQ Teorici"
From Aino Wiki
(→Calcoli) |
(No difference)
|
Latest revision as of 14:14, 14 April 2018
Contents
Relazioni
INNER Join
Es. 1:
List<Article> articles = (List<Article>) (from u in blogDB.Users join b in blogDB.Articles on u.Id equals b.UserId where u.Id == 10 select b).ToList();
Es. 2, Selezione di un nuovo oggetto, uso dell'operatore var
che creerà una variabile di tipo dinamico:
var userArticles = from u in blogDB.Users join b in blogDB.Articles on u.Id equals b.UserId where u.Id == 10 select new { Name=u.name, Email=u.Email, Title=b.Title, Description=b.Description }; foreach (var userArticle in userArticles) { Console.WriteLine("{0} - {1}",userArticle.Name, userArticle.Title); }
Selezione di entità ma cambiando solo qualche campo
NON FUNZIONA!
Da una query LinQ supponendo di modificare solo qualche campo dei record dell'entità selezionata ecco come evitare di usare la new {}
var query = someList.Select(x => { x.SomeProp = "foo"; return x; })
LEFT Join
Es. 1, notare userArticles.DefaultIfEmpty() nella join e l'indicazione in caso di righe non corrispondenti tra le tabelle:var userArticles = from u in blogDB.Users join b in blogDB.Articles on u.Id equals b.UserId into userArticles where u.Id == 10 from ua in userArticles.DefaultIfEmpty() select new{Name=u.Name,Title = (ua==null)?"":ua.Title};
Esempio realistico 1
Si hanno due liste se ne vuole una nuova che sia fusione delle due anche se del tipo di una delle due. Si applica una left join mediante "from la in lpa.DefaultIfEmpty()":
List<ArticoloPrelievoDraft> listaPrelievo = new List<ArticoloPrelievoDraft>(); List<itChefArticoliOrdinabili> lstAO = new List<itChefArticoliOrdinabili>(); // Riempio: listaPrelievo e lstAO List<ArticoloPrelievoDraft> mergedList = (from lp in listaPrelievo join la in lstAO on lp.codice_articolo equals la.codice_articolo into lpa from la in lpa.DefaultIfEmpty() // <-- Left Join select new ArticoloPrelievoDraft { id_articolo = lp.id_articolo, codice_articolo = lp.codice_articolo, descrizione_articolo = lp.descrizione_articolo, unita_misura = lp.unita_misura, quantita_fabbisogno = lp.quantita_fabbisogno, prezzo = (la == null ? -1 : la.costo_articolo), // <------------- costo_totale_prezzo_salvato = lp.costo_totale_prezzo_salvato, costo_totale_prezzo_standard = lp.costo_totale_prezzo_standard, }).ToList();
Dove i due tipi di oggetti hanno definizione:
public class ArticoloPrelievoDraft { [DataMember] public Int32 id_articolo; [DataMember] public string codice_articolo; [DataMember] public string descrizione_articolo; [DataMember] public string unita_misura; [DataMember] public double quantita_fabbisogno; /// <summary> /// Questo è il VALORE CHE VERRA' CONTROLLATO TRA PrezzoSalvato E PrezzoStandard /// </summary> [DataMember] public double prezzo; //* o PrezzoSalvato o PrezzoStandard, il primo non zero/null [DataMember] public double costo_totale_prezzo_salvato; //* Se lista Chiusa = LC [DataMember] public double costo_totale_prezzo_standard; //* Altrimenti di LC } // e public class itChefArticoliOrdinabili { public int id_articolo; public string codice_articolo; public string descrizione_articolo; public string UM; public double costo_articolo; public double quantita; public string codice_fornitore; public string conservazione; public string descrizione_fornitore; public string famiglia; }
Esempio realistico 2
Join su più tabelle e più campi chiave su ogni relazione, sotto c'è anche una LEFT JOIN
IQueryable<PD_S_PRODUCT_PRICELIST> outPD_S_PRODUCT_PRICELISTs = (from pl in request.Input.ProductList join ppl in PD_S_PRODUCT_PRICELISTs on new { pl.COMPANY_CODE, pl.DIVISION_CODE, pl.PRODUCT_CODE, } equals new { ppl.COMPANY_CODE, ppl.DIVISION_CODE, ppl.PRODUCT_CODE } //into x1 // LEFT JOIN join pc in request.Input.ProductPricelistList.DefaultIfEmpty() on new { pl.COMPANY_CODE, pl.DIVISION_CODE, pl.PRODUCT_CODE } equals new { pc.COMPANY_CODE, pc.DIVISION_CODE, pc.PRODUCT_CODE } select ppl);
OUTER Join
Es. 1, notare userArticles.DefaultIfEmpty() nella join e le 2 indicazioni in caso di righe non corrispondenti tra le tabelle:var userArticles = from u in blogDB.Users join b in blogDB.Articles on u.Id equals b.UserId into userArticles where u.Id == 10 from ua in userArticles.DefaultIfEmpty() select new{Name=(ua==null)?"":ua.Name, Title=(ua==null)?"":ua.Title};
List<User>users = blogDB.Users.ToList(); List<TVRole>articles= blogDB.Articles.ToList(); var userBlogs = users .Join(articles, user => user.Id, article=> article.UserId, (user, article) => new { Name = user.Name, Description = article.Description}); foreach (var userBlog in userBlogs) { Console.WriteLine("{0} - {1}",userBlog.Name, userBlog.Description); }
GROUP Join
Esempi
using System; using System.Collections.Generic; using System.Linq; namespace ConsoleApplication1 { // An example of SQL's LEFT OUTER JOIN equivalent using Entity Framework : // Inputs : A dataset of Employees and a dataset of Offices. Each Employee record is holding an optional reference to its assigned Office. // Goal : List the employees along with their (optional) assigned Office // // Ex : // Id FirstName Name Office // --------------------------------- // 1 John Smith New York // 2 Jean Durand Paris // 3 Oleg Kouletchov // 4 Bobby Lost // // // Item classes : Office, Employee and ResultClass public class Office { public int Id { get; set; } public string Name { get; set; } } public class Employee { public int Id { get; set; } public int? IdOffice { get; set; } // <- an office can be null public string FirstName { get; set; } public string LastName { get; set; } } public class ResultClass // result records as we want them { public int IdEmployee { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string WorkPlace { get; set; } } class Program { static void Main(string[] args) { // populate datasets var listQueryableOffice = new List<Office>() { new Office() {Id = 1, Name = "New York"}, new Office() {Id = 2, Name = "Paris"} }.AsQueryable(); var listQueryableEmployees = new List<Employee>() { new Employee() { Id = 1, FirstName = "John", LastName = "Smith", IdOffice = 1 }, new Employee() { Id = 2, FirstName = "Jean", LastName = "Durand", IdOffice = 2 }, new Employee() // This guy has no workplace assigned yet { Id = 3, FirstName = "Oleg", LastName = "Kouletchov" }, new Employee() // This guy has a reference to an Office that does not exist { Id = 4, FirstName = "Bobby", LastName = "Lost", IdOffice = 7 }, }.AsQueryable(); // Perform a GroupJoin followed by a SelectMany to obtain the following SQL Query equivalent with Entity Framework : // SELECT E.Id, E.FirstName, E.LASTNAME, O.NAME as WORKPLACE // FROM EMPLOYEES E // LEFT OUTER JOIN OFFICE O IEnumerable<ResultClass> joinedResults = listQueryableEmployees.GroupJoin(listQueryableOffice, employee => employee.IdOffice, // perform the join on IdOffice office => office.Id, (employee, offices) => new // Intermediate anonymous type { IdEmployee = employee.Id, FirstName = employee.FirstName, LastName = employee.LastName, WorkPlaces = offices.DefaultIfEmpty() // IEnumerable<Office> , can be null }) .SelectMany( x => x.WorkPlaces.Select( // each (possibly empty) WorkPlace record will yield a result record place => new ResultClass() // the result type we want { IdEmployee = x.IdEmployee, FirstName = x.FirstName, LastName = x.LastName, WorkPlace = place == null ? "" : place.Name // workplace may be null (LEFT JOIN here!) but we still return a record })); // Display the results foreach (var joinedResult in joinedResults) { Console.WriteLine(joinedResult.IdEmployee + " " + joinedResult.FirstName + " " + joinedResult.LastName + " " + joinedResult.WorkPlace); } Console.ReadLine(); } } }
JOIN usando Lambda expression
Esempi
JOIN con chiavi multiple (vedi legami)
Uno
static List<string> GetMedicAreaList(data_context.AnaMedEntities dboAnaMed, string areaCode) { return dboAnaMed.AM_B_MEDICAL .Join(dboAnaMed.AM_S_MEDICAL_AREA, m => m.MEDICAL_CODE, a => a.AM_B_MEDICAL_CODE, (a, m) => new { Med = a, Area = m }) .Join(dboAnaMed.AM_S_MEDICAL_DETAILS, m => m.Med.MEDICAL_CODE, d => d.AM_B_MEDICAL_CODE, (a, m) => new { Med = a, Det = m }) .Where(x => (x.Med.Area.SY_AREA_CODE == areaCode) && (x.Med.Med.IS_ACTIVE == true) && (x.Det.SY_JOB_CODE == "001")) .Select(x => x.Med.Med.MEDICAL_CODE).ToList(); }
Due
static List<string> GetMedicShopList(data_context.AnaMedEntities dboAnaMed, string shop) { return dboAnaMed.AM_B_MEDICAL .Join(dboAnaMed.AM_S_MEDICAL_AREA, m => m.MEDICAL_CODE, a => a.AM_B_MEDICAL_CODE, (a, m) => new { Med = a, Area = m }) .Join(dboAnaMed.AM_S_MEDICAL_DETAILS, m => m.Med.MEDICAL_CODE, d => d.AM_B_MEDICAL_CODE, (a, m) => new { Med = a, Det = m }) .Where(x => (x.Med.Area.SY_SHOP_CODE == shop) && (x.Med.Med.IS_ACTIVE == true) && (x.Det.SY_JOB_CODE == "001")) .Select(x => x.Med.Med.MEDICAL_CODE).ToList(); }
Ordinamento
ORDER BY
ATTENZIONE notare l'assegnazione della lista ordinata in una nuova variabile, la nuova variabile avrà gli elementi ordinata ma se ad esempio l'ordinamento lo si facesse passando la lista ad un metodo l'ordinamento non avrebbe effetto nel metodo!!! Ciò accade perché essendo un oggetto non semplice, quello passato al metodo, esso è passato PER REFERENZA e pertanto ciò inficia l'ordinamento!var itemList = (from t in ctn.Items where !t.Items && t.DeliverySelection select t).OrderByDescending(x => x.Delivery.SubmissionDate);
Ordinamento su campo data stringa convertito in DateTime, assumendo che il formato della data sia Italiano 01/08/2017 = Primo Agosto 2017.
IFormatProvider culture = new System.Globalization.CultureInfo("it-IT", true); foreach (OrdineFornitore of in aof.ordini.OrderBy(x => DateTime.Parse(x.data_consegna, culture, System.Globalization.DateTimeStyles.AssumeLocal))) { }
Altro esempio con conversione in lista di oggetti:
public class RouteInfo { public string name; /// <summary> /// Conterrà la Storia di tutte le posizioni registrate per la nave. /// </summary> public List<WP> _lstWp = new List<WP>(); /// <summary> /// Nome della sorgente da cui il singolo dato è stato preso. /// Necessario in quanto per stessa nave i dati posson provenire da fonti\protocolli\dispositivi diversi. /// (Necessario da quando s'è aggiunto OPC UA) /// </summary> public string SourceName; public DateTime timeStampUTC; } public class WP { public double latitude; public double longitude; public string name; public DateTime lastUpdateUTC; public bool isEqual(WP wp) { if (wp.longitude == longitude && wp.latitude == latitude && wp.name == name) { return true; } return false; } } //---------------- Ordinamento usando LinQ ----------------------- RouteInfo ri = new RouteInfo(); List<WP> lstWp = (List<WP>)((ObjectValueInfo)(OPCUAItemToModel.ValueObject)).ValueObject; ri._lstWp = lstWp.OrderBy(w => w.name).ToList();
Ordinamento multiplo
Da stackoverflow, si può usare dopo l'Order by anche il ThenBy e/o la sua estensione ThenByDescending:foobarList.OrderBy(x => x.Foo).ThenBy( x => x.Bar) // Esempio pratico su una lista: List<UA_EventItem> lstUAEvents = new List<UA_EventItem>(); // Riempimento della lista ..... e poi: ProcessingAndRoutingEvents(lstUAEvents.OrderBy(x => x.EventTime).ThenBy(x => x.EventCode).ToList());
Sort
L'esempio che segue è un pò complesso in quanto usa le funzioni anonime mediante il delegate:
private class DettaglioFornitore { public string Descrizione_fornitore { get; set; } } private class DettaglioArticolo { public string Descrizione { get; set; } public List<DettaglioFornitore> FornitoriArticolo { get; set; } } //--------- etc in un metodo: List<DettaglioArticolo> dettaglioArticoli = new List<DettaglioArticolo>(); // Ordina gli articoli per Denominazione Fornitore, SUPPONENDO esista almeno un Articolo pertanto esiste l'elemento in posizione [0] dettaglioArticoli.Sort(delegate (DettaglioArticolo x, DettaglioArticolo y) { return String.Compare(x.FornitoriArticolo[0].Descrizione_fornitore, y.FornitoriArticolo[0].Descrizione_fornitore, true); });
Selezioni
Restituzione di una Lista semplice
List<string> shipCodes = (from s in shipNames select string.Format("{0}_{1}", s.CompanyCode, s.ShipCode) ).ToList<string>();
List<AisData> lstAIS = (from x in ShipData.Instance.lstAIVDM where x.mmsi == mmsi select x).DefaultIfEmpty().ToList(); if (lstAIS != null && lstAIS.Count > 0 && lstAIS[0] != null // <--- Questo è l'effetto di: .DefaultIfEmpty().ToList(); ) { }
ATTENZIONE se la selezione non prende elementi il ToList ne crea comunque uno ma nullo !
Restituzione di una Lista TIPIZZATA
newValShipAIS.AIVDMInfo = new List<AIVDM_AISWebAPI>(); if (listAllAIVDM_WebApi.Count > 0) { var listAIVDM4Ship = (from a in listAllAIVDM_WebApi where a.ShipNameRefer == itemShipName.ShipCode select a).DefaultIfEmpty(); newValShipAIS.AIVDMInfo = listAIVDM4Ship.ToList<AIVDM_AISWebAPI>(); }
La cosa si può spingere sino a selezionare e creare al volo una lista di oggetti diversi da quelli di partenza, basta aggiungere una "new" dopo la selezione del singolo elemento. Un esempio pratico l'ho dato al punto precedente nell'elenco dei tipi di JOIN. Segue un esempio preso da StackOverflow che spiega con un esempio:
public class DogWithBreed { public Dog Dog { get; set; } public string BreedName { get; set; } } public IQueryable<DogWithBreed> GetDogsWithBreedNames() { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select new DogWithBreed() { Dog = d, BreedName = b.BreedName }; return result; }
LET
Consente di creare una variabile utilizzabile altrove nella query, può essere anche una oggetto ovvero un sottoinsieme di records di una tabella.
E' utile talvolta archiviare il risultato di una sottoespressione per poterlo usare in clausole successive.
Esempio:
public virtual SA_B_SALES_HEADER GetQuickSaleIncludingDetailsAndProducts(FoxRequest<FoxQuickSaleIncludingDetailsAndProducts_Input> request) { var input = request.Input; // Get the header var data = (from header in SA_B_SALES_HEADER_Repository.GetAll(input.Query) //let extension = header.SA_B_SALES_HEADER_EXT_NZL let details = (from detail in header.SA_B_SALES_DETAIL select new { detail, PD_S_PRODUCT = detail.PD_S_PRODUCT, SA_B_SALES_PRICE_SPLIT = detail.SA_B_SALES_PRICE_SPLIT }).ToList() select new { header, //SA_B_SALES_HEADER_EXT_NZL = extension SA_B_SALES_HEADER_EXT_NZL = header.SA_B_SALES_HEADER_EXT_NZL, SA_B_SALES_DETAIL = details }).FirstOrDefault ( qs => qs.header.SALE_DATE == input.SALE_DATE && qs.header.SALE_NUMBER == input.SALE_NUMBER && qs.header.DOCUMENT_TYPE_CODE == FoxSettings.Sales.QuickSales.DocumentType ); return data != null ? data.header : null; }
Distinct
Esempi complessi
What you need is a "distinct-by" effectively. I don't believe it's part of LINQ as it stands, although it's fairly easy to write:
public static IEnumerable<TSource> DistinctBy<TSource, TKey> (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) { HashSet<TKey> seenKeys = new HashSet<TKey>(); foreach (TSource element in source) { if (seenKeys.Add(keySelector(element))) { yield return element; } } }
So to find the distinct values using just the Id property, you could use:
var query = people.DistinctBy(p => p.Id);
var query = people.DistinctBy(p => new { p.Id, p.Name });
Untested, but it should work (and it now at least compiles).
It assumes the default comparer for the keys though - if you want to pass in an equality comparer, just pass it on to the HashSet constructor.
Esempi semplici
Selezione di un campo stringa 'shipCode' da una lista di oggetti 'segments' con distinct delle stringhe restituite in output.
var shipsSelected = (from s in segments select s.ShipCode).Distinct();
TOP n = TAKE n
Per selezionare i primi n items (inserendo un ordinamento!):var elenco = (from t in ctn.Items where t.DeliverySelection == true && t.Delivery.SentForDelivery == null orderby t.Delivery.SubmissionDate select t).Take(4);
Selezione insiemistica IN
CONTAINS
Si può simulare con la funzione CONTAINS ma attenzione si presume si esegua su una lista di tipi semplici (stringhe, interi... etc) non oggetti.
List<int> ids = new List<int>() { 1, 2, 3 }; var result = (from x in scope.Extent<Role>() where ids.Contains(x.RoleId) select x).ToList(); //------ Altro esempio --------- var names = new string[] { "Alex", "Colin", "Danny", "Diego" }; var matches = from person in people where names.Contains(person.Firstname) select person;
ANY
Supponendo di avere due liste (LIST<T>) di oggetti differenti le voglio usare per filtrare su due campi differenti di una lista IQueryable (la 'PD_S_PRODUCT_PRICELIST_Repository.GetAll()').
'ppl' è una lista di Prodotti con listino associato, si vogliono filtrare per determinati prodotti e listinii
IQueryable<PD_S_PRODUCT_PRICELIST> PD_S_PRODUCT_PRICELISTs = (from ppl in PD_S_PRODUCT_PRICELIST_Repository.GetAll() where request.Input.ProductList.Any(p => p.PRODUCT_CODE == ppl.PRODUCT_CODE) && request.Input.ProductPricelistList.Any(p => p.PRICELIST_CODE == ppl.PRICELIST_CODE) select ppl );
Selezioni con lambda expression
C'è un'altra possibilità di accedere ad elementi, liste o attributi di singoli elementi.Usando clausole Where o Select, seguono esempi:
// con la Lambda expression: wp = ShipData.Instance.routeInfo._lstWp.Where(x => x.name == "xxxx").SingleOrDefault(); // che è la stessa cosa di quanto segue: wp = (from r in ShipData.Instance.routeInfo._lstWp where r.name == dWP_Identifier.Value select r).Single();
Selezioni
ATTENZIONE alla selezione di un singolo elemento, ci sono due versioni:
- .Single() seleziona un solo elemento ma se non esiste da Errore, .SingleOrDefault() se l'elemento non esiste restituisce il Default generalmente NULL. Attenzione che se la query restituisce più di un elemento si otterrà una eccezione: "Sequence contains more than one element".
- .FirstOrDefault() si comporta come .SingleOrDefault() ma se la query ne restituisce più di uno non fa scattare eccezione ma seleziona il primo, occorrerà usare opportunamente la .Where condition per selezionare l'elemento più opportuno.
DateTime sourceTimestamp = DateTime.MinValue; // ... sourceTimestamp = entityValueOut.Attributes.First(x => x.AttribName == uaAttributeName4TimeStamp).SourceTimestamp;
Calcoli
SUM
Calcoli e somma finale:
int balance = Account.Select(denom => denom.Currency * denom.BillCount).Sum();
Max e Min
Massima data o minima da una lista
//Retrieve Minimum Date var MinDate = (from d in dataRows select d.Date).Min(); //Retrieve Maximum Date var MaxDate = (from d in dataRows select d.Date).Max();
Verifiche
ANY
Per verificare se una lista ha elementi, con un esempio:
#region Private Properties private static List<AutomationData> _propellersRPM = new List<AutomationData>(); #endregion ... _propellersRPM = AutomationDataDAO.GetLastSenctencesGroup(sentenceNamePattern, lastNrHour, nrLastItems); if (_propellersRPM.Any()) { ... }
Altro esempio in cui, data una lista prodotta dal metodo .GetAll(...)
, si applicano 2 eventuali filtri usando due liste di oggetti (qualcuno obietterebbe sulla lista di oggetti e invece userebbe una lista di stringhe per poi usare la Contains())
IQueryable<PD_S_PRODUCT_PRICELIST> PD_S_PRODUCT_PRICELISTsOUT = (from ppl in PD_S_PRODUCT_PRICELIST_Repository .GetAll(request.Input.Query) where (!request.Input.ProductList.Any() || request.Input.ProductList .Any(p => p.PRODUCT_CODE == ppl.PRODUCT_CODE)) && (!request.Input.ProductPricelistList.Any() || request.Input.ProductPricelistList .Any(p => p.PRICELIST_CODE == ppl.PRICELIST_CODE)) select ppl );
EXISTS
LinQ to Entities
Per verificare se almeno un elemento è in una lista, usando la lambda expression usando Exists:if (ShipData.Instance.lstAIVDM.Exists(x => x.mmsi == mmsi)) // <--- LAMBDA Expression { ok = false; m_logger.Error("Failed to Remove AIS ID='{0}', nr items found {1}.", nodeDisplayName, itemsFound); } else { UAModelHandler.Update_ModelUpdateDateUTC(DateTime.UtcNow); ok = true; m_logger.Debug("Model, for AIS '{0}', updated to last removed items.", nodeDisplayName); }
LinQ to SQL
Si implemente usando la clausolaAny
.if (dc.Users.Any(u => u.Name == name)) {...}
Oppure
if (dc.Users.Where(u => u.ID== Id && u.Company==company).Any()) { }
Operazioni
ForEach()
someValues.ToList().ForEach(x => list.Add(x + 1));
Mappa e Links
C# | Esempi LinQ | LinQ
Parole chiave: Exist FirstOrDefault First SingleOrDefault Single LinQ2SQL