Bnei Brak 51202 IsraelIQueryable • LINQ to SQL isn’t based on IEnumerable • Instead, Table implements IQueryable – Contains an Expression Tree • This expression tree is parsed by the qu
Trang 1LINQ via C# 3.0
Chapter 6 – LINQ to SQL
Trang 2© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
LINQ to Relational Data
Trang 3LINQ to DataSet
• DataSets and DataTables do not
implement IEnumerable
– You could roll your own
• LINQ provides an AsEnumerable()
extension for DataSets in
System.Data.DataSetExtensions.dll
Trang 4© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
from book in books
select book Field<string> ("author");
Array.ForEach(authors.ToArray(),
Console.WriteLine);
• LINQ to DataSet can be used as an
object-based LINQ to XML!
Trang 5LINQ to SQL
• The primary challenge:
– Abstracting away the data source
• DataSets are filled and queried in a
non-standard 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,
Trang 6© 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<T> implements
IQueryable
– Contains an Expression Tree
• This expression tree is parsed by the query provider at runtime
Trang 7Data Source Abstraction
• We still use the same LINQ keywords and query operators
– The query provider translates them into something
else and executes them
– SQL, Web Service calls, REST URLs,
from student in students
WHERE [t1].[StudentId] = [t0].[Id]
) AS [Average]
FROM [dbo].[Students] AS [t0]
LINQ to SQL Provider
students is IQueryable<Studen
t>
Trang 8© 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:
– Data = normalized relational database tables
– Objects = business entities, DAL, data contracts,
• System.Data.Linq.Mapping MappingSour ce
– AttributeMappingSource
– XmlMappingSource
LINQ to SQL is an Object-Relational
Mapper
Trang 9Attribute-Based Mapping
[ Table (Name=“Courses”)]
public class Course
{
[ Column ( IsPrimaryKey =true]
public int Id {get;set;}
Trang 10© 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
using ( DataContext context =
new DataContext (@"D:\Temp\
CourseManagement.mdf")) {
context Log = Console.Out;
var product =
from student in context GetTable<Student> ()
from course in context GetTable<Course> ()
select new { Student=student, Course=course };
Table<Grade> grades = context GetTable<Grade> ();
int lastGradeId =
(from grade in grades
select (int?)grade.Id).Max() ?? 0;
Connection string, mapping source
Obtain data source
Table<T> is IQueryable
Trang 11DataContext Notes
• DataContext is not just a portal to the
entities
• It tracks changes and detects conflicts
• It guarantees object identity
• It is decoupled from the mapping source
• It is lightweight: One per method scope
Trang 12© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
What’s Supported
?
• All language-integrated query operators
• Most standard query operators
• Inserts, updates and deletes
• Custom SQL commands (strings)
• Database creation from mapping
• Transactional work
• Preloading data (local queries)
• Optimistic locking (concurrency control)
Trang 14© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Inserting Data
using (DataContext context ) {
Student alex = new Student { Id = 49, Name = "Alex" };
context.GetTable<Student>() InsertOnSubmit (alex);
//New student gets an automatic 100 in first two courses int lastGradeId = context.GetTable<Grade>().Max(g =>
Trang 15Deleting Data
using (DataContext context ) {
foreach (Student student in
context.GetTable<Student>() Where (
s => s.Grades Average (g => g.Value) < 80)) {
//Manual cascading delete
foreach (Grade grade in
Trang 16© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Conflict Management
• Optimistic locking allows for conflict control when examining and submitting changes
using (CourseManagementDataContext context ) {
ChangeSet changeSet = context GetChangeSet ();
foreach (var conflict in context ChangeConflicts ) { conflict Resolve ( RefreshMode.KeepCurrentValues );
foreach (var memberConflict in
Expensive: Checks all members
Use a timestamp or a version field
[Column(IsVersion=true, …)]
Or avoid altogether
[Column(UpdateCheck=UpdateCheck.Never,
…)]
Trang 18© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Explicit Transaction
• Transactions can be performed explicitly
– Not recommended if distributed transactions are
Trang 19Database Creation from Mapping
• With an initialized context, we can create
the database from the mapping
– Objects-first, schema-later!
– (Schema-first to be discussed shortly)
using (DataContext context =
Table<Grade> grades = context.GetTable<Grade>();
Table<Course> courses = context.GetTable<Course>();
if (context DatabaseExists ())
context DeleteDatabase ();
DROP DATABASE [D:\Temp\CourseManagement.mdf]
CREATE DATABASE [D:\Temp\CourseManagement.mdf] ON PRIMARY (NAME='CourseManagement.mdf', FILENAME='D:\Temp\CourseManagement.mdf') LOG ON
(NAME='CourseManagement.ldf', FILENAME='D:\Temp\CourseManagement.ldf')
SET ARITHABORT ONCREATE TABLE [dbo].[Students](
[Id] Int NOT NULL, [Name] NVarChar(MAX) NOT NULL, CONSTRAINT [PK_dbo.Students] PRIMARY KEY ([Id]) )
CREATE TABLE [dbo].[Grades](
[Id] Int NOT NULL, [StudentId] Int NOT NULL, [CourseId] Int NOT NULL, [Value] Int NOT NULL, CONSTRAINT [PK_dbo.Grades] PRIMARY KEY ([Id]) )
CREATE TABLE [dbo].[Courses](
[Id] Int NOT NULL, [Name] NVarChar(MAX) NOT NULL, CONSTRAINT [PK_dbo.Courses] PRIMARY KEY ([Id]) )
ALTER TABLE [dbo].[Grades]
ADD CONSTRAINT [Course_Grade] FOREIGN KEY ([CourseId]) REFERENCES [dbo].[Courses]([Id])ALTER TABLE [dbo].[Grades]
ADD CONSTRAINT [Student_Grade] FOREIGN KEY ([StudentId]) REFERENCES [dbo].[Students]([Id])
Trang 20© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Custom SQL Commands
• Custom SQL commands are fully
supported: can return mapped entities!
using (DataContext context ) {
var students = context ExecuteQuery<Student> (
"SELECT TOP(2) Id FROM Students WHERE Id > @p0“, 2);
foreach (Student student in students) {
>
Trang 21Stored Procedures
• Stored procedures can be executed as
custom SQL commands:
using (DataContext context ) {
foreach (Student failedStudent in
@FailingGrade int = 70 )
AS SELECT * FROM Students WHERE
(SELECT AVG(Value) FROM Grades
WHERE Grades.StudentId = Students.Id) < @FailingGrade;
Trang 22© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Stored Procedures
• Stored procedures can be mapped to
methods using [Function]
class FailedStudentDataContext : DataContext {
[ Function (Name = "GetFailingStudents")]
public ISingleResult<Student> GetFailingStudents( [ Parameter (Name = "FailingGrade", DbType =
failedStudent.Name + " failed");
}
}
Trang 23• A function can be used inline as part of a
query (unlike a stored procedure)
– Can return a table or a scalar value
class GetBestGradeDataContext : DataContext {
[ Function (Name = "dbo.GetBestGrade", IsComposable = true )]
public int? GetBestGrade(
[Parameter(Name = "CourseId", DbType = "Int")]
Trang 24© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Preloading Data – Local Queries
• Controlling relationship preloading:
– Also
DataContext.DeferredLoadingEnabled=true
using (DataContext context ) {
DataLoadOptions dlo = new
DataLoadOptions();
dlo LoadWith<Course> (c => c Grades );
dlo LoadWith<Grade> (g => g Student );
context LoadOptions = dlo;
With preloading: DB trip penalty
and memory footprint
For filtering, also use
DataLoadOptions.AssociateW
ith
Trang 25Object Tracking
• Object tracking on the DataContext can
be disabled
– Updates won’t be detected, conflicting change
detection will be weaker, identity will not be
guaranteed
• But: better performance for read-only
work, or when implicit updates aren’t
required
DataContext.ObjectTrackingEnabled=false
Trang 26© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Object Tracking
• Entities can be attached to the data
context
– Useful if upper layers in the applications actively
create data entities
– Can attach with original
• Table<T>.Attach
Trang 27Review So Far
• Data retrieval and preloading
• Inserts, updates and deletes
• Custom SQL commands (strings)
• Database creation from mapping
• Transactional work
• Optimistic locking (concurrency control)
Trang 28© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
XML Mapping
• True objects-first approach
– E.g if I have WCF data contracts to map to tables
– Minimizes code duplication (Once and Only Once)
– Don’t want LINQ attributes to pollute business entity classes and properties
– Example: http://tinyurl.com/526abw
• Mapping customization without
recompilation
Trang 29XML Mapping Steps
• Provide XML file with mapping directives
instead of using attributes
< Database Name="CourseManagement“
xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007"> < Table Name="dbo.Courses" Member="Courses">
Trang 30© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Trang 31Best Practices
• Keep multiple mapping files and combine them at runtime (with LINQ to XML)
– Facilitates granular source control
– When the entity changes, its mapping file changes
• Embed the mapping file into your
assembly as a resource
Trang 32© 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
Trang 33Discriminator-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
Trang 34© 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
Trang 35Do I Have to Write This by Hand
?
• Visual Studio Designer to the rescue
– SqlMetal also to the rescue
Trang 36© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Trang 37• Property access to tables from context
• Partial methods for notifications on the
context and on individual objects
• Events from
INotifyPropertyChanging and
INotifyPropertyChanged
Trang 38© 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
Trang 39foreach (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);
Trang 40© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
Example 2
using (CourseManagementDataContext context =
new CourseManagementDataContext()) {
Grade katesGrade =
(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) =>
Trang 41Example 3
partial class Student {
partial void OnNameChanging (string value) {
partial class CourseManagementDataContext {
partial void UpdateStudent (Student instance) {
Trang 42© Copyright SELA Software & Education Labs Ltd 14-18 Baruch Hirsch St Bnei Brak 51202 Israel
More Highlights
• Lazy loading
– Use Link<T>
instead of T
• Validation through OnValidate partial
method (on insert, update and delete)
• Debugger visualization
Trang 43Why 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 do it manually)
• Because wizards always require
customization
Trang 44© 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
Trang 45LINQ to SQL Performance
1 Turn off object tracking for read-only contexts
2 Use compiled queries –
CompiledQuery.Compile
3 Use DataLoadOptions properly
4 Use UpdateCheck.Never if appropriate
5 If possible, select temporary objects instead of
tracked entities (GC and tracking overhead)
6 Use a DataContext per method – do not pool
or share them
LINQ to SQL can be 95% as fast
as Data Readers
http://tinyurl.com/2tkche
Trang 46© 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