Lenguaje Integrado de consultasLINQEduardo Quintás
Guía
•Novedades en C# 3.0•Fundamentos de LINQ•Linq To Objects•Linq To XML•Linq To Entities•Recursos
Novedades en C#3.0Lenguaje Integrado de Consultas: LINQ
Evolución de .NET2002 2003 2005 2006 2007 2010
Herramienta(Visual Studio)
VS 2002 VS 2003 VS2005VS2005
+ Extensiones
VS2008 VS2010
LenguajeC# v1.0VB.NET (v7.0)
C# v1.1VB.NET (v7.1)
C# v2.0VB2005 (v8.0)
como antes
C# v3.0VB9
(v9.0)
C# 4.0VB10
(v10.0)
Librerías Framework
NetFx v1.0
NetFx v1.1
NetFx v2.0
NetFx v3.0
NetFx v3.5
NetFxV4.0
Engine (CLR) CLR v1.0 CLR v1.1 CLR v2.0 como
antescomo antes
CLR v4.0
C# 3.0 - Objetivos
Integrar objetos, datos relacionales y XML
Y:
Hacer el lenguaje más conciso
Añadir constructores de programación
funcional
No ligar el lenguaje a APIs específicas
Mantenerse 100% compatible hacia atrás
Resultado:
•Métodos extensores, tipos anónimos, constructores sin parámetros, inferencia de tipos, expresiones lambda, árboles de expresión
•Más un poquito de azucar sintáctico …
from p in Passengerswhere p.Citizenship == “ES”select new { p.Id, p.Name };
Nuevas características
•Inicializadores de objetos•Inferencia de tipos•Tipos anónimos•Métodos extensores•Expresiones lambda•Árboles de expresión•LINQ!!!
Inicializadores de Objetospublic class Passenger{ public string Id {get; set;} public string Name {get; set;} public DateTime BirthDate {get; set;}}
Passenger p = new Passenger () { Id = “A4”, Name = “Cabezabolo, Manolo” };
Passenger p = new Passenger();p.Id = “A4”;p.Name = “Cabezabolo, Manolo”;
Asignación de Campos o
Propiedadespublic Passenger(string id, string name, DateTime birthDate) // OPCIONAL!!!!{ Id=id; Name=name; BirthDate = birthDate; }
Inferencia de Tiposint i = 666;string s = “Hola";double d = 3.14;int[] numbers = new int[] {1, 2, 3};Dictionary<int,Pedido> pedidos = new Dictionary<int,Pedido>();
var i = 666;var s = “Hola";var d = 3.14;var numbers = new int[] {1, 2, 3};var pedidos = new Dictionary<int,Pedido>();
“El tipo en el lado de la derecha”
Tipos Anónimos
var o = new { Name = “Pantoja”, Age= 75 };
class XXX{ public string Name; public int Age;}
XXX
Métodos Extensoresnamespace MisCosas{ public static class Extensiones { public static string Concatenar(this IEnumerable<string> strings, string separador) {…} }}
using MisCosas;
string[] nombres = new string[] { “Edu", “Juan", “Manolo" };string s = nombres.Concatenar(", ");
Método extensor
Incluir extensiones en el ámbito
obj.Foo(x, y)
XXX.Foo(obj, x, y)
IntelliSense!
Expresiones Lambda
public delegate bool Predicate<T>(T obj);
public class List<T>{ public List<T> FindAll(Predicate<T> test) { List<T> result = new List<T>(); foreach (T item in this) if (test(item)) result.Add(item); return result; } …}
Delegado genérico
Tipogenérico
Expresiones Lambda
public class MiClase{ public static void Main() { List<Cliente> clientes = ObtenerListaClientes(); List<Cliente> locales = clientes.FindAll( new Predicate<Cliente>(CiudadIgualCoruna) ); }
static bool CiudadIgualCoruna(Cliente c) { return c.Ciudad == “A Coruña"; }}
Expresiones Lambda
public class MiClase{ public static void Main() { List<Cliente> clientes = ObtenerListaClientes (); List<Cliente> locales = clientes.FindAll( delegate(Cliente c) { return c.Ciudad == “A Coruña"; } ); }}
DelegadoAnónimo
Expresiones Lambda
public class MiClase{ public static void Main() { List<Cliente> clientes = ObtenerListaClientes (); List<Cliente> locales = clientes.FindAll( (Clientes c) => {return c.Ciudad == “A Coruña";} ); }}
ExpresiónLambda
Expresiones Lambda
public class MiClase{ public static void Main() { List<Cliente> clientes = ObtenerListaClientes (); List<Cliente> locales = clientes.FindAll(c => c.Ciudad == “A Coruña"); }}
ExpresiónLambda
Introduciendo LINQ
•Todos estos nuevos aspectos se trasladan a métodos extensores sobre colecciones:▫Pueden transformarse en árboles de
expresiones
from p in passengerswhere p.Citizenship== “ES"select new { p.Id, p.Name };
passengers.Where(p => c.Citizenship == “ES").Select(p => new { p.Id, p.Name });
var pasajerosNacionales = from p in passengers where p.Citizenship == “ES" select new {p.Id, p.Name};
var pasajerosNacionales = passengers .Where(p => p.Citizenship== “ES") .Select(p => new { p.Id, p.Name});
Métodos Extensore
s
Expresiones
Lambda
Expresiones de Consulta
Inicializadores de Objetos
Tipos Anónimos
Inferencia Tipos
Variables Locales
Introduciendo LINQ
Fundamentos de LINQLenguaje Integrado de Consultas: LINQ
LINQ: Lenguage Integrated Query
• Lenguaje de consulta universal de primer orden en C# y VB9▫ Reducir el conocimiento de distintas formas de consulta.▫ Parecido a lo que ya conocemos : SQL▫ Basado en Lambda Cálculo, Tipado fuerte, Ejecución
retrasada (deferred) Utiliza características nuevas del lenguaje como: Inferencia de tipos, Tipos anónimos, Métodos extensores y Inicialización de objetos
▫ Altamente extensible
• Múltiples proveedores Objects, XML, DataSets, SQL, Entities WMI, Sharepoint, Excel… incluso para Google, Flickr,
Amazon
Language INtegrated Query (LINQ)
LINQ enabled data sources
LINQTo Objects
Objects
LINQTo XML
<book> <title/> <author/> <price/></book>
XML
LINQ enabled ADO.NET
LINQTo DataSets
LINQTo SQL
LINQTo Entities
Relational
Others…VB C#
.NET Language-Integrated Query
Arquitectura de LINQ
System.Linq.EnumerableBasado en delegados
Fuente implementaIEnumerable<T>
var query = from p in passengers where p.Citizenship== “ES" select p
System.Linq.QueryableBasado en árbol de
expresión
Fuente implementaIQueryable<T>
SQL DataSetsObjetos EntitiesXML
Expresión de Consulta
from id in source{ from id in source | join id in source on expr equals expr [ into id ] | let id = expr | where condition | orderby ordering, ordering, … } select expr | group expr by key[ into id query ]
Empieza con from
Cero o más from, join, let, where, o orderby
Termina conselect o group
by
Continuación into opcional
Expresiones de ConsultaProyección Select <expr>
Filtrado Where <expr>, Distinct
Comprobación
Any(<expr>), All(<expr>)
Union <expr> Join <expr> On <expr> Equals <expr>
Agrupación Group By <expr>, <expr> Into <expr>, <expr>Group Join <decl> On <expr> Equals <expr> Into <expr>
Agregación Count(<expr>), Sum(<expr>), Min(<expr>), Max(<expr>), Avg(<expr>)
Partición Skip [ While ] <expr>, Take [ While ] <expr>
Conjunto Union, Intersect, Except
Ordenación Order By <expr>, <expr> [ Ascending | Descending ]
Operadores de ConsultaExpresión de consulta de Linq
Where(), Select(), SelecMany(), OrderBy(), ThenBy(), OrderByDescending(), ThenByDescending(), GroupBy(), Join(), GroupJoin()
Partición Take(), Skip(), TakeWhile(), SkipWhile()
Conjunto Distinct(), Union(), Intersect(), Except()
Conversión ToArray(), ToList(), ToDictionary(), ToLookup(), AsEnumerable(), Cast<T>(), OfType<T>()
Generación Range(), Repeat<T>(), Empty<T>(), Concat(), Reverse()
Cuantificación Any(), All(), Contains(), SequenceEqual()
Elementos First(), Last(), Single(), ElementAt(), DefaultIfEmpty(). {método}OrDefault()
Agregados Count(), LongCount(), Max(), Min(), Sum(), Average(), Aggregate()
LINQ to ObjectsLenguaje Integrado de Consultas: LINQ
LINQ To Objects• Aplicable en colecciones genéricas y arrays que
están en memoria (heap)• Métodos extensores para colecicones y arrays
▫ Using System.Linq;• Las expresiones de consulta devuelven
IEnumerable<T>• Fundamental para gestionarlas de modo flexible
Ejemplos de consultas
LINQ to XMLLenguaje Integrado de Consultas: LINQ
XML en .Net Framework• Clases XmlTextReader y XmlTextWriter
▫ L/E secuencial: Eficiente pero complejo• Serialización y Deserialización:
System.Xml.Serialization▫ Atributos en clases, S/D contra streams (ficheros,
memoria…)• Clases XmlDocument, XmlNode … en System.Xml
▫ Implementan un DOM por árboles• Ahora: LINQ To XML
▫ Extensión de LINQ de .Net▫ Simple, Flexible, Potente, nuevo DOM.▫ Manipulación y consulta con LINQ
LINQ To XML• Importando System.Xml.Linq• Nuevo DOM:
▫ Construcción funcional: XElement• Independencia del documento
▫ Permite crear fragmentos XML sin asociarlos a un XDocument
• Texto como valor▫ Hojas del árbol XML se convierten a tipos por valor
de .Net
Clase XElement
.Load() / .Save() Cargan o Guardan de un stream un documento XML
.Elements() Secuencia de elementos contenidos
.ElementsBeforeSelf(), .ElementsAfterSelf()
Elementos “hermanos” anteriores o posteriores en el mismo nivel del árbol
.Descendants{AndSelf}()
Secuencia aplanada de todos los elementos hijos
.Ancestors{AndSelf}()
Secuencia aplanada de todos los elementos padres
.Attributes() Atributos del elemento
• Los métodos están sobrecargados para localizar elementos concretos.
• Métodos más representativos
Creación de documentos• Directamente con XElement
XElement contacts = new XElement("Contacts", new XElement("Contact", new XElement("Name", "Patrick Hines"), new XElement("Phone", "206-555-0144", new XAttribute("Type", "Home")), new XElement("phone", "425-555-0145", new XAttribute("Type", "Work")), new XElement("Address", new XElement("Street1", "123 Main St"), new XElement("City", "Mercer Island"), new XElement("State", "WA"), new XElement("Postal", "68042"))));contacts.Save("contacts.xml");
Consultas• Load(uri) (archivos, http…)• Métodos Elements, Attributes, Element, Attribute …• Se puede utilizar Xpath (método
XPathSelectElements())
XElement element = XElement.Load(Server.MapPath(@"~\XmlFiles\rssMiniNova.xml"));…XElement element = XElement.Load(@"http://www.mininova.org/rss.xml?cat=8");
var query = from i in element.Elements("channel").Elements("item") select new { Title = i.Element("title").Value, Posted = DateTime.Parse(i.Element("pubDate").Value), Size = Convert.ToDecimal(i.Element("enclosure").
Attribute("length").Value) Link = i.Element("enclosure").Attribute("url").Value, Category = i.Element("category").Value };
Transformación de documentos• Utilizando Linq y XElement• Método Save(stream/fichero… ), ToString() …
XElement nuevo = new XElement("TSAInformationForm", new XAttribute("Date", DateTime.Now), new XElement("SourceID", "883829HFGHMT"), new XElement("PassengerList", from p in pasajeros select new XElement("Passenger", new XAttribute("DocumentId", p.Id), new XElement("Name", p.Name), new XElement("Country", p.Citizenship), new XElement("Flight", new XAttribute("Code",p.Code), new XElement("ArrivalDate",p.Arrival)
))));
nuevo.Save("TsaSendFile.xml");
<TSAInformationForm Date="..."> <SourceID>...</SourceID> <PassengerList> <Passenger DocumentId="..."> <Name>..</Name> <Country>..</Country> <Flight Code="..."> <ArrivalDate>..</ArrivalDate> </Flight> </Passenger> ... </PassengerList> </TSAInformationForm>
LINQ to Entities• Arquitectura Entitiy Framework (EF)• LINQ to Entities • Ejemplos
Arquitectura de EF
•OR/M + Objetos de Servicio▫Soporte de varios SGDB:
EntityClientProvider▫Herramientas y lenguaje para mapeado▫Modelo Conceptual: EntityObject▫Contextos tipados: ObjectContext▫Gestión de Entidades:
ObjectStateManager▫Consulta: eSQL y LINQ To Entities
Patrón arquitectónico empresarial típico
UI / UICWebforms,
Console App,
ASP.NETWeb
Services
ADO.NET 3.0: Entity Framework
FachadaStateless,Short lived contexts
Modelo LógicoClases EntityObjects
Gestión Modelo
ObjectContext, ObjectStateManager
Oracle
SqlServer
Interface Lógica de negocio Almacén
ADO.NET 3.0
Def. Visual del Modelo
Archivo .EDMX | Edmgen.exe
EFDataProviderSqlServer, Oracle, MySQL, DB2, etc.
MetadataArchivos CSDL, MSL y SSDL
SGBD
CSDL<Schema Namespace="BlogsSample.BusinessLogic.ADONET30.Model" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm"> <EntityContainer Name="BlogContext"> <EntitySet Name="BlogPosts" EntityType="BlogsSample.BusinessLogic.ADONET30.Model.BlogPost" /…MSL<?xml version="1.0" encoding="utf-8"?><Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS"> <EntityContainerMapping StorageEntityContainer="dbo" CdmEntityContainer="BlogContext"> <EntitySetMapping Name="BlogPosts"> <EntityTypeMapping TypeName="IsTypeOf(BlogsSample.BusinessLogic.ADONET30.Model.BlogPost)"> <MappingFragment StoreEntitySet="BlogPosts"> <ScalarProperty Name="BlogPostID" ColumnName="BlogPostID" />…
SSDL<?xml version="1.0" encoding="utf-8"?><Schema Namespace="BlogsSample.BusinessLogic.BlogsModel.Store" Alias="Self" ProviderManifestToken="09.00.3042" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl"> <EntityContainer Name="dbo"> <EntitySet Name="BlogComments" EntityType="BlogsSample.BusinessLogic.BlogsModel.Store.BlogComments" />
public partial class BlogContext : ObjectContext {… public ObjectQuery<BlogPost> BlogPosts { get { if ((this._BlogPosts == null)) { this._BlogPosts =
base.CreateQuery<BlogPost>("[BlogPosts]"); } return this._BlogPosts; } }… public void AddToBlogs(Blog blog) { base.AddObject("Blogs", blog); }
public partial class Blog : EntityObject { public static Blog CreateBlog(int blogID, string seriesTitle, bool needsReviewer) { Blog blog = new Blog(); blog.BlogID = blogID; blog.SeriesTitle = seriesTitle; blog.NeedsReviewer = needsReviewer; return blog; } public int BlogID { get { return this._BlogID; } set { this.OnBlogIDChanging(value); this.ReportPropertyChanging("BlogID"); this._BlogID = StructuralObject.SetValidValue(value); this.ReportPropertyChanged("BlogID"); this.OnBlogIDChanged(); } }
... }
public class Facade{ public static IList<Blog> GetAllBlogUpdatedSince(UpdatedSince period) { DateTime date = FacadeHelper.getPeriod(period);
using (BlogContext ctx = new BlogContext()) { IQueryable<Blog> blogs = from blog in ctx.Blogs where blog.BlogPosts.Any(p => p.BlogDate > date) select blog; return blogs.ToList<Blog>(); } }}
using BlogsSample.BusinessLogic.ADONET30;…protected void Button2_Click(object sender, EventArgs e){ GridView2.DataSource = Facade.GetAllBlogUpdatedSince( UpdatedSince.LastYear); GridView2.DataBind();}
Modelo Conceptual en EF
• Clases prescriptivas▫Structural Object > EntityObject ▫Gestión de identidad (EntityKey)▫Gestión de cambios (TrackingEntity event)▫Soporte para relaciones (EntityCollection)▫Estado (EntityState)▫Son clases parciales
• Posibilidad de clases IPOCO▫Implementar IEntityWithKey,
IEntityWithChangeTracker, IEntityWithRelationship
Object Context• Clase derivada (generada) de ObjectContext• Tipado Fuerte: Manipulación del Modelo Conceptual
▫ Consultas/Recuperación: Blogs: ObjectQuery;▫ Inserciones: .AddToBlog(Blog b); .AddObject(…), ▫ Borrado: .DeleteObject▫ Persistencia: .SaveChanges();
• Gestión de la conexión• Metadata (a partir de CSDL)• Almacen o caché en memoria de objetos• Tracking de estado objetos:
▫ .Attach(..), .Dettach(..) ▫ ObjectStateManager▫ MergeOption
ObjectStateManager•Seguimiento del estado de entidades•Gestiona entradas EntityStateEntry para
cada Entidad en almacen en memoria.▫Cuando se cargan (Query, Attach): Unchanged▫Cuando se crean (AddObject): Added▫Cuando se modifican: Changed▫Cuando se borran: Deleted▫Cuando se destruye el ObjectContext:
Detached▫Al aplicar ObjectContext.SaveChanges() en
Added, Changed, cambia a Unchanged.
Diseño: Entity FrameworkEntityObject ObjectContext
BloggerCtx
Blogs: ObjectQueryBlogPosts: ObjectQueryAddToBlogs(…)AddToBlogPosts(…)Hereda:Attach(..)Dettach(..)Add(..)Delete(..),Refresh(..)SaveChanges(..) etc.
ObjectStateManager1
Caché de EntidadesGestión de Identidad, estado ycambios en las Entidades
1 EntityStateEntry
CurrentValuesOriginalValuesStateIsRelationship
Blog BlogPost*
BlogPostId: intBlogEntry: string…
EntityKey EntityState:Added, Deleted, Detached, Changed, UnchangedPropertyChanged
Consultas•Entity SQL
▫Dialecto SQL indep. de gestor con soporte para: Tipos enriquecidos, relaciones, herencia… Strings que se resuelven en tiempo de ejecución
•LINQ to Entities▫Todas las ventajas de LINQ (tipado fuerte,
expresiones lambda)▫Lenguaje que se resuelve en tiempo de
compilación▫Aprovechamos el tipado fuerte, la inferencia y
el Intellisense de Visual Studio▫Menos errores en ejecución
IQueryable<Blog> query = from posts in ctx.BlogPosts where posts.BlogDate > date select posts.Blogs;
ObjectQuery<Blog> query = ctx.CreateQuery<Blog>( "SELECT VALUE bp.Blogs FROM BlogPosts as bp WHERE bp.BlogDate > @date", new ObjectParameter("date",date));
LINQ To Entities• Diferencias con LINQ To Objects y To XML• Las consultas tienen el tipo ObjectQuery<T>
▫ Implementa IQueryable<T> y no IEnumerable<T>▫Necesita Árboles de expresión para construir el
SQL final.• Cuando se enumeran los resultados:
▫query1.FirstOrDefault(), query1.ToList() …▫Se ejecuta la consulta SQL y devuelve un
IEnumerable<T>• No están disponibles todos los operadores de
Linq To Objects o To XML
Arquitectura de LINQ To Entities
Operadores disponiblesExpresión de consulta de Linq
Where(), Select(), SelecMany(), OrderBy(), ThenBy(), OrderByDescending(), ThenByDescending(), GroupBy(), Join(), GroupJoin()
Partición Take(), Skip()
Conjunto Distinct(), Union(), Intersect(), Except()
Conversión ToArray(), ToList(), ToDictionary(), ToLookup(), AsEnumerable(), Cast<T>(), OfType<T>()
Generación N/A
Cuantificación Any(), All()
Elementos First(), Last(), ElementAt(). {método}OrDefault()
Agregados Count(), LongCount(), Max(), Min(), Sum(), Average()
Cómo hacer una consulta• Establecer el contexto
▫Using (FlightContext ctx = new FlightContext()) { …}
• Dentro del contexto construír la consulta▫Obtener los IQueryable<T> del Contexto▫IQueryable<Flight> query =
from f in ctx.Flightswhere p.To==“MAD”select f;
• Ejecutar la consulta▫Con un operador de conversión
query.ToList(); query.FirstOrDefault() …
Otras consideraciones• Pensar la consulta con el modelo conceptual
▫ Navegar por las relaciones y no con joins• Los objetos recuperados dentro del contexto son
gestionados por el ObjectStateManager (tracking)• Cuando se cierra el contexto NO.• Carga Perezosa de relaciones (dentro de un
contexto)▫ ¡¡No nos lo vamos a traer todo!!▫ Si en el where se utiliza una relación, ésta se carga▫ Carga implícita
from f in ctx.Flights.Include(“Aircraft”) select f;▫ Carga explícita
if (!aflight.Aircraft.IsLoaded) aflight.Aircraft.Load();
Modelo ER
Modelo EF
RecursosLenguaje Integrado de Consultas: LINQ
Recursos• MSDN: http//Msdn.microsoft.com• 101 Linq Samples:
http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx • Blogs:
▫ ADO.NET Team Blog: http://blogs.msdn.com/adonet/ ▫ Blog de Dany Simons: http://blogs.msdn.com/dsimmons/ ▫ Blog de Unai Zorrilla: http://geeks.ms/blogs/unai/ ▫ Blog de Octavio Hdez.: http://geeks.ms/blogs/ohernandez/
• Libros:▫ “C# 3.0 y LINQ”, Octavio Hernández. Krasis Press 2007.
ISBN: 978-84-935489-1-9▫ “ADO.NET Entity Framework”, Unai Zorrilla, Octavio
Hernández, Eduardo Quintás. Krasis Press 2008. ISBN: 978-84-935489-9-5