Роман Здебский Microsoft rzdebski@microsoft.com http://blogs.msdn.com/roman Зачем нужен LINQ? – проблематика и постановка задачи Основные идеи LINQ Сценарии использования Влияние LINQ на .NET языки программирования Демонстрации Проблема: Data != Objects T/SQL USE Northwind SELECT ProductName,UnitPrice, UnitsInStock FROM products WHERE productID<3 COMPUTE SUM(UnitPrice),SUM(UnitsInStock) ProductName -------------Chai Chang UnitPrice -----------18,00 19,00 UnitsInStock -------------39 17 sum sum --------------------- ----------37,00 56 (3 row(s) affected) Oracle SQL*Plus REPF[OOTER] [PAGE] [printspec [text|variable] ...] | [OFF|ON] REPH[EADER] [PAGE] [printspec [text|variable] ...] | [OFF|ON] COMP[UTE] [function [LAB[EL] text] ... OF {expr|column|alias} ... {expr|column|alias|REPORT|ROW} ...] ON Должен знать и периодически использовать: Relational Algebra T/SQL (SQL Server) API (ADO, OLE DB) XQuery … DataSet DS=new DataSet(); XQueryNavigatorCollection oXQ = new XQueryNavigatorCollection(); SqlConnection nwindConn = new SqlConnection("Data string strXML = ""; Source=localhost;Integrated Security=SSPI;Initial string fileName1="c:\\Test\\T1.xml"; Catalog=northwind"); string alias1 = "MyDataTest.xml"; SqlCommand catCMD = ); oXQ.AddNavigator( fileName1, alias1 string strQuery = "<NewDataSet> { " + nwindConn.CreateCommand(); " let $bbcatCMD.CommandText := document(\"MyDataTest.xml\")/*/* " + CategoryID, = "SELECT " let $cc := document(\"MyDatattt.xml\")/*/* "+ CategoryName FROM Categories“; " for $c in $cc " + nwindConn.Open(); " for $b in $bb " + SqlDataReader myReader = " where $c/kod = $b/kod " + catCMD.ExecuteReader(); " return <Table> { $b/nazv,$b/dat,$c/naim } </Table> " + while (myReader.Read()) " }</NewDataSet> “; { XQueryExpression xExpression = new XQueryExpression(strQuery); Dim xmldoc As New strXML = xExpression.Execute(oXQ).ToXml(); Console.WriteLine("\t{0}\t{1}", System.Xml.XPath.XPathDocument("c:\books.xml") StringReader strReader = new myReader.GetInt32(0), myReader.GetString(1)); DimStringReader(strXML); nav As System.Xml.XPath.XPathNavigator = XmlTextReader reader = newxmldoc.CreateNavigator() XmlTextReader(strReader); } DS.ReadXml(reader); Dim expr As System.Xml.XPath.XPathExpression = myReader.Close(); nav.Compile( "//Publisher[. = DataGrid1.DataSource = DS.Tables[0]; nwindConn.Close(); 'MSPress']/parent::node()/Title" ) DataGrid1.DataBind(); Как сделать эффективный paging списка на web странице? ASP NET 1.x - AllowPaging property - True ASP NET 2.0 - Enable Paging option from the GridView's smart tag А если список большой… ??? SQL Server 2005: USE Northwind SELECT RowNum, EmployeeID, LastName, FirstName FROM (SELECT EmployeeID, LastName, FirstName, ROW_NUMBER() OVER(ORDER BY LastName,FirstName) as RowNum FROM Employees ) USE Northwind as EmployeeInfo SELECT top (@pagesize) EmployeeID, LastName, WHERE RowNum FirstName BETWEEN 1 and 5 SQL Server 2000: FROM ( SELECT TOP (@pagesize*@pagenum) EmployeeID, LastName, FirstName FROM Employees ORDER BY LastName DESC, FirstName DESC ) as EmployeeInfo ORDER BY LastName ASC, FirstName ASC Language Integrated Query Сделать возможности запросов неотъемлемой частью .NET языков Запрашивать, объединять, трансформировать: реляционные данные XML Объекты Коллекции и списки … - в развитии идеи - ВСЁ C# 3.0 VB 9.0 Others… .NET Language Integrated Query LINQ to Objects LINQ to DataSets LINQ to SQL LINQ to Entities LINQ to XML <book> <title/> <author/> <year/> <price/> </book> Objects Relational XML Version = Assembly references + compilers Не создается новый CLR runtime .NET Fx 3.5 .NET Fx 3.0 .NET Fx 3.0 Update .NET Fx 2.0 .NET Fx 2.0 Minor Update .NET Fx 2.0 Minor Update Whidbey Vista Orcas time List<City> locations = GetLocations(); IEnumerable<City> places = Name=Kazan Name=Tver Name=London from city in locations where city.DistanceFromSeattle > 1000 orderby city.Country, city.Name select city; Country=Russia DistanceFromSPB=2000 Country=Russia DistanceFromSPB=1100 Country=UK DistanceFromSPB=4000 Extension methods var citiesSPB2 = locations.Where(c => c.DistanceFromSPB > 1000).OrderBy(c => c.Name).OrderBy(c => c.Country).Select( c => new {Name= c.Name, Country = c.Country}); Формат запроса var citiesSPB = from city in locations where city.DistanceFromSPB > 1000 orderby city.Country, city.Name select new { city.Name, city.Country }; Фактический порядок выполнения запроса Intellisence Уже используется (XQuery) Execution plan for $act in doc("hamlet.xml")//ACT let $speakers := distinctvalues($act//SPEAKER) return … Restriction Where Projection Select, SelectMany Ordering OrderBy, ThenBy Grouping GroupBy Joins Join, GroupJoin Quantifiers Any, All Partitioning Take, Skip, TakeWhile, SkipWhile Sets Distinct, Union, Intersect, Except Elements First, Last, Single, ElementAt Aggregation Count, Sum, Min, Max, Average Conversion ToArray, ToList, ToDictionary Casting OfType<T>, Cast<T> Implicitly typed local variables Anonymous types Extension methods Lambda expressions Object initializers Query expressions Expression trees Local variable type inference Query var contacts = expressions from c in customers where c.State == "WA" select new { c.Name, c.Phone }; Lambda expressions var contacts = customers .Where(c => c.State == "WA") .Select(c => new { c.Name, c.Phone }); Extension methods Anonymous types Object initializers Local variable type inference Dim contacts = From c In customers _ Where c.State = "WA“ _ Select c.Name, c.Phone Query expressions Lambda expressions Dim contacts = _ customers _ .Where(Function (c) c.State = "WA")_ .Select(Function(c) New With { c.Name, c.Phone } Extension methods Anonymous types Object initializers var testVal = 2 * 2; var testVal2 = "hello".ToUpper(); var testVal3 = new City(); Console.WriteLine(testVal.GetType()); Console.WriteLine(testVal2.GetType()); Console.WriteLine(testVal3.GetType()); System.Int32 System.String ConsoleApplication1.City Необходимость трансформации или модификации данных полученных от запроса LINQ позволяет осуществлять “data shaping” с помощью проекций Удобно использовать с анонимными типами “anonymous type”, поддерживаемыми компилятором <>f__AnonymousType0`2[System.Int32,System.String] Extension method namespace MyStuff { public static class Extensions { public static string Concatenate(this IEnumerable<string> strings, string separator) {…} } } Brings extensions into scope using MyStuff; string[] names = new string[] { "Axel", "Mia", "Niels" }; string s = names.Concatenate(", "); IntelliSense! obj.Foo(x, y) XXX.Foo(obj, x, y) static class StaticExtensionClass { public static int toGoInMiles(this City ct) { return (int)(ct.DistanceFromSPB * 1.61) ; } } City ct = new City { Name = "Bor", Country = "RUS", DistanceFromSPB = 100 }; Console.WriteLine(ct.toGoInMiles()); 161 public class Product { public string Name; public decimal Price; } public class Product { string name; decimal price; public string Name { get { return name; } set { name = value; } } public decimal Price { get { return price; } set { price = value; } } } private string □; public class Product { public string Name { get; set; } public decimal Price { get; set; } } public string Name { get { return □; } set { □ = value; } } Must have both get and set public class Point { private int x, y; public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } } Point a = new Point { X = 0, Y = 1 }; Point a = new Point(); a.X = 0; a.Y = 1; Field or property assignments Must implement IEnumerable Must have public Add method List<int> numbers = new List<int> { 1, 10, 100 }; List<int> numbers = new List<int>(); numbers.Add(1); numbers.Add(10); numbers.Add(100); Add can take more than one parameter Dictionary<int, string> spellings = new Dictionary<int, string> { { 0, "Zero" }, { 1, "One" }, { 2, "Two" }, { 3, "Three" } }; C# 2.0 IEnumerable<Customer> locals = EnumerableExtensions.Where(customers, delegate(Customer c) { return c.ZipCode == 98112; }); C# 3.0 Lambda выражение locals.Where(c => c == 98112); Func<int, bool> nonExprLambda = x => (x & 1) == 0; Expression<Func<int, bool>> exprLambda = x => (x & 1) == 0; ParameterExpression xParam = Expression.Parameter(typeof(int), "x"); Expression<Func<int, bool>> exprLambda = Expression.Lambda<Func<int, bool>>( Expression.Equal( Expression.And(xParam, Expression.Constant(1)), Expression.Constant(0)), xParam); Console.WriteLine(exprLambda); x => ( ( x & 1 ) = 0 ) var query = from c in customers where c.State == "WA" select c.Name; var query = customers.Where(c => c.State == "WA").Select(c => c.Name); Source implements IEnumerable<T> Source implements IQueryable<T> System.Query.Enumerable Based on Delegates Objects System.Query.Queryable Based on Expression Trees SQL DataSets Others… Customer[] custs = SampleData.GetCustomers(); var query = from c in custs where c.City == "London" select c.Name; var query = custs.Where(c => c.City == "London").Select(c => c.Name); string[] names = query.ToArray(); custs ID names Name Phone Where c => c.City == "London" Select c => c.Name using System; using System.Query; using System.Collections.Generic; class app { static void Main() { string[] names = { "Allen", "Arthur", "Bennett" }; IEnumerable<string> ayes = names .Where(s => s[0] == 'A'); foreach (string item in ayes) Console.WriteLine(item); names[0] = "Bob"; foreach (string item in ayes) Console.WriteLine(item); } } Allen Arthur Arthur Будьте осторожны с отложенным выполнением запросов и изменением данных // Don't do this! NullReferenceException foreach (var phone in contacts.Descendants("phone")) { phone.Remove(); } foreach (var phone in contacts.Descendants("phone").ToList()) { phone.Remove(); } Accessing data today SqlConnection c = new SqlConnection(…); c.Open(); SqlCommand cmd = new SqlCommand( "SELECT c.Name, c.Phone FROM Customers c WHERE c.City = @p0"); cmd.Parameters.AddWithValue("@p0", "London“); DataReader dr = c.Execute(cmd); while (dr.Read()) { string name = dr.GetString(0); string phone = dr.GetString(1); DateTime date = dr.GetDateTime(2); } dr.Close(); Queries in quotes Loosely bound arguments Loosely typed result sets No compile time checks Accessing data with LINQ public class Customer { … } public class Northwind : DataContext { public Table<Customer> Customers; … } Northwind db = new Northwind(…); var contacts = from c in db.Customers where c.City == "London" select new { c.Name, c.Phone }; Classes describe data Tables are like collections Strongly typed connections Integrated query syntax Strongly typed results Database DataContext Table Class View Class Column Field / Property Relationship Field / Property Stored Procedure Method from c in db.Customers where c.City == "London" select c.CompanyName LINQ Query Application Objects db.Customers.Add(c1); c2.City = “Seattle"; db.Customers.Remove(c3); SubmitChanges() LINQ to SQL SQL Query Rows DML or SProcs INSERT INTO Cust … UPDATE Cust … DELETE FROM Cust … SELECT CompanyName FROM Cust WHERE City = 'London' SQL Server Интегрированный в язык доступ к данным Соответствия (Mapping) Связывает таблицы и записи с классами и объектами Построен поверх ADO.NET и .NET Transactions Определяются атрибутами или во внешнем XML файле Отношения (relations) соответствуют свойствам (Свойство Products у Category и Category у Product) Возможность отложенной или одновременной загрузки данных через отношения (relations) Сохраняемость (Persistence) Автоматическое отслеживание изменений Обновление через SQL или stored procedures Add NorthwindDataContext db = new NorthwindDataContext(); Supplier supplier = new Supplier{CompanyName = “ABC”}; db.Suppliers.Add(supplier); db.SubmitChanges(); Update Product product = db.Products.Single(p => p.ProductName== "Chai"); product.UnitsInStock = 11; product.ReorderLevel = 10; product.UnitsOnOrder = 2; db.SubmitChanges(); Delete var supplier = db.Suppliers.FirstOrDefault(s=>s.CompanyName == “ABC"); if ((supplier != null) && (supplier.Products.Count == 0)) { db.Suppliers.Remove(supplier); db.SubmitChanges(); } Встроенный в C# и VB синтаксис запросов SQL-подобные запросы по любым .NET collection (Все реализующие IEnumerable) Мощный язык запросов Результаты LINQ запросов легко использовать в DataBinding Программирование XML сегодня XmlDocument doc = new XmlDocument(); XmlElement contacts = doc.CreateElement("contacts"); foreach (Customer c in customers) if (c.Country == "USA") { XmlElement e = doc.CreateElement("contact"); XmlElement name = doc.CreateElement("name"); name.InnerText = c.CompanyName; e.AppendChild(name); XmlElement phone = doc.CreateElement("phone"); phone.InnerText = c.Phone; e.AppendChild(phone); contacts.AppendChild(e); <contacts> <contact> } <name>Great Lakes Food</name> <phone>(503) 555-7123</phone> doc.AppendChild(contacts); </contact> … </contacts> Imperative model Document centric No integrated queries Memory intensive Программирование XML с LINQ Declarative model XElement contacts = new XElement("contacts", from c in customers where c.Country == "USA" select new XElement("contact", new XElement("name", c.CompanyName), new XElement("phone", c.Phone) ) ); Element centric Integrated queries Smaller and faster XDocument loaded = XDocument.Load(@"C:\contacts.xml"); contacts var q = from c in loaded.Descendants("contact") where (int)c.Attribute("contactId") < 4 select (string)c.Element("firstName") + “ “ + (string)c.Element("lastName"); Language integrated query для XML Мощь выражений XPath / XQuery Но на C# или VB как языках программирования Использует опыт работы с DOM Element centric, не document centric Быстрее и компактнее XName XNamespace XText XNode XComment XDeclaration XContainer XDocument XElement XAttribute XDocumentType XStreamingElement XProcessingInstruction Часть ParallelFX LINQ to Objects LINQ to XML Распараллеливание LINQ to SQL – SQL Server Использование: Reference System.Concurrency.dll Wrap your data source in an IParallelEnumerable<T> with a call to the System.Linq.ParallelEnumerable.AsParallel extension method. Подходы к распараллеливанию: pipelined processing stop-and-go processing inverted enumeration IEnumerable<T> leftData = ..., rightData = ...; var q = from x in leftData.AsParallel() join y in rightData on x.a == y.b select f(x, y); Language Integrated Query для .NET LINQ to Objects Инфраструктура запросов к реляционным данным LINQ to XML SQL-like запросы к любым .NET коллекциям LINQ to SQL Встроенный в C# 3.0 и VB 9.0 синтаксис запросов Компактный, быстрый XML DOM с поддержкой запросов LINQ to Entities LINQ to Datasets Унифицированный подход к запросам и трансформации объектов, реляционных данных и XML Мощь запросов SQL и XQuery встроенная в C# и VB Проверка типов, IntelliSense, refactoring запросов Модель расширения для других языков программирования и APIs LINQ to WebQueries (MSDN Web sites) LINQ to Amazon (books search) LINQ to RDF Files. LINQ to MySQL LINQ to NHibernate LINQ to LDAP LINQ to Google Desktop LINQ to SharePoint LINQ to Streams (SLinq, Streaming LINQ) LINQ to Expressions (MetaLinq) Что бы мы сделали с paging в LINQ? Ключевое слово – LINQ Вводим в поиск microsoft.com второй результат – The LINQ Project Anders Hejlsberg Chief Designer of C# Architect of LINQ project Для большого числа типовых задач LINQ освобождает от необходимости тратить время на написание запросов Оптимизация запросов и индексирования – по прежнему профессиональная и нужная задача Каждый может более фокусно заниматься тем, что ему интереснее и в чем он видит свое профессиональное развитие и интерес Зона «Спроси Эксперта» 14:00 – 15:00 Роман Здебский Microsoft rzdebski@microsoft.com http://blogs.msdn.com/roman