LINQ via C# 3.0 Chapter – LINQ to SQL © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel LINQ to Relational Data • LINQ to DataSet – Leveraging the existing investment • LINQ to SQL – Designer-generated mapping – Custom mapping, ORM facilities • LINQ to Entities – VS2008 SP1 • ADO.NET Data Services – VS2008 SP1 © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel LINQ to DataSet • DataSets and DataTables not implement IEnumerable – You could roll your own • LINQ provides an AsEnumerable() extension for DataSets in System.Data.DataSetExtensions.dll © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel LINQ to DataSet DataSet dataSet = new DataSet("Library"); dataSet.ReadXml(libraryStream); var books = dataSet.Tables[0].AsEnumerable(); var authors = from book in books select book.Field("author"); Array.ForEach(authors.ToArray(), Console.WriteLine); • LINQ to DataSet can be used as an object-based LINQ to XML! © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel LINQ to SQL • The primary challenge: – Abstracting away the data source • DataSets are filled and queried in a nonstandard way • But LINQ to Objects != DB access – Retrieve 15 rows from a 1,000,000-row table – Retrieve items based on DB indexes – Support DB-paging, partitioning, views, sprocs, © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel IQueryable • LINQ to SQL isn’t based on IEnumerable • Instead, Table implements IQueryable – Contains an Expression Tree • This expression tree is parsed by the query provider at runtime © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Data Source Abstraction • We still use the same LINQ keywords and query operators students is – The query provider translates them into something else IQueryable and executes them – SQL, Web Service calls, REST URLs, from student in students SELECT [t0].[Name], ( select new { SELECT AVG([t1].[Value]) LINQ to SQL student.Name, FROM [dbo].[Grades] AS [t1] Provider Average = WHERE [t1].[StudentId] = student.Grades.Average( [t0].[Id] g => g.Value) ) AS [Average] } FROM [dbo].[Students] AS [t0] © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Mapping Data to Objects • LINQ to Objects: – Data = objects LINQ to SQL is an • LINQ to SQL: Object-Relational Mapper – Data = normalized relational database tables – Objects = business entities, DAL, data contracts, • System.Data.Linq.Mapping.MappingSource – AttributeMappingSource – XmlMappingSource © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Attribute-Based Mapping [Table(Name=“Courses”)] public class Course { [Column(IsPrimaryKey=true] [Table(Name=“Grades”)] public int Id {get;set;} public class Grades [Column( )] { public string Name {get;set;} [Column(IsPrimaryKey=true] } public int Id {get;set;} [Column( )] public int Value {get;set;} [Association( )] [Table(Name=“Students”)] public EntityRef Student public class Student [Association( )] { public EntityRef Course [Column(IsPrimaryKey=true] } public int Id {get;set;} [Column( )] public string Name {get;set;} } © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel The Data Context • The DataContext class is the LINQ to SQL orchestrator Connection string, mapping source using (DataContext context = new DataContext(@"D:\Temp\CourseManagement.mdf")) { context.Log = Console.Out; var product = from student in context.GetTable() Table is from course in context.GetTable() IQueryable select new { Student=student, Course=course }; Table grades = context.GetTable(); int lastGradeId = (from grade in grades Obtain select (int?)grade.Id).Max() ?? 0; data source © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Inheritance Mapping • Inheritance is a very non-DB concept • Multiple ways to model it: – A single table with combined columns and a discriminator – A table for the base and for each derived type – A table for each derived type with repeated columns from the base type © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Discriminator-Based Mapping [Table(Name = "Vehicles")] [InheritanceMapping(Code = "C", Type = typeof(Car))] [InheritanceMapping(Code = "T", Type = typeof(Tank))] [InheritanceMapping(Code = "V", Type = typeof(Vehicle), IsDefault = true)] public class Vehicle { [Column(IsPrimaryKey = true)] public int LicenseNumber { get; set; } [Column(IsDiscriminator = true, DbType = "NChar(1) NOT NULL")] public string Discriminator { get; set; } } public class Car : Vehicle { [Column(DbType = "NVarChar(60)")] public string ModelName { get; set; } [Column] public DateTime? Sold { get; set; } } //More types in the samples © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Querying Inheritance • The OfType query operator – The LINQ to SQL query provider translates it to a WHERE on the discriminator column //LINQ: var cars = from car in context.GetTable() OfType() select car; //SQL: WHERE ([t0].[Discriminator] = @p0) @p0 = “C” © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Do I Have to Write This by Hand? • Visual Studio Designer to the rescue – SqlMetal also to the rescue © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Designer-Generated Code [Database(Name="CourseManagement")] public partial class CourseManagementDataContext : DataContext [Table(Name="dbo.Grades")] public partial class Grade : INotifyPropertyChanging, INotifyPropertyChanged DataBase ModeL © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Highlights • Property access to tables from context • Partial methods for notifications on the context and on individual objects • Events from INotifyPropertyChanging and INotifyPropertyChanged © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Highlights • Easy customization for Insert, Delete and Update methods to become sprocs © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Example using (CourseManagementDataContext context = new CourseManagementDataContext()) { var averages = (from student in context.Students select new { student.Name, Average = student.Grades.Average(g => g.Value) }); foreach (var avg in averages) Console.WriteLine("{0} has an average of {1}", avg.Name, avg.Average); foreach (Student failedStudent in context.GetFailingStudents(78)) { Console.WriteLine("{0} failed", failedStudent.Name); } } © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Example using (CourseManagementDataContext context = Grade implements new CourseManagementDataContext()) { INotifyPropertyChanging, Grade katesGrade = INotifyPropertyChanged (from grade in context.Grades where grade.Student.Name == "Kate" select grade).First(); katesGrade.PropertyChanging += (o, e) => Console.WriteLine(e.PropertyName + " changing"); katesGrade.PropertyChanged += (o, e) => Console.WriteLine(e.PropertyName + " changed"); katesGrade.Value -= 10; context.SubmitChanges(); } © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Example partial class Student { partial void OnNameChanging(string value) { Console.WriteLine("Student: Name '{0}' to '{1}'", this.Name, value); } partial void OnCreated() { Console.WriteLine("Student: Created '{0}'", this.Name); } } partial class CourseManagementDataContext { partial void UpdateStudent(Student instance) { Console.WriteLine("DataContext: Student '{0}' updated", instance.Name); } } © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel More Highlights • Lazy loading – Use Link instead of T • Validation through OnValidate partial method (on insert, update and delete) ã Debugger visualization â Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Why Haven’t You Mentioned It Before?! • Because the underlying mechanism is more important than drag-and-drop • Because the designer doesn’t support XML mapping (must it manually) ã Because wizards always require customization â Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel SqlMetal • An SDK tool for generating DBML, code and XML mapping files sqlmetal /functions /sprocs /code:Types.cs /map:Mapping.xml D:\temp\CourseManagement.mdf © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel LINQ to SQL Performance Turn off object tracking for read-only contexts Use compiled queries – CompiledQuery.Compile Use DataLoadOptions properly Use UpdateCheck.Never if appropriate If possible, select temporary objects instead of tracked entities (GC and tracking overhead) Use a DataContext per method – not pool or share them LINQ to SQL can be 95% as fast as Data Readers http://tinyurl.com/2tkche © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel Chapter Summary • LINQ to Relational Data – DataSets, XML via DataSets – LINQ to SQL • LINQ to SQL as an Object-Relational Mapper – Custom mapping, designer-generated mapping – Inheritance mapping © Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel .. .LINQ to Relational Data • LINQ to DataSet – Leveraging the existing investment • LINQ to SQL – Designer-generated mapping – Custom mapping, ORM facilities • LINQ to Entities – VS2008... Software & Education Labs Ltd 1 4-1 8 Baruch Hirsch St Bnei Brak 51202 Israel Mapping Data to Objects • LINQ to Objects: – Data = objects LINQ to SQL is an • LINQ to SQL: Object-Relational Mapper – Data... DataSets – LINQ to SQL • LINQ to SQL as an Object-Relational Mapper – Custom mapping, designer-generated mapping – Inheritance mapping © Copyright SELA Software & Education Labs Ltd 1 4-1 8 Baruch