Capitulo 4: LINQ to SQL (Parte 1)

Lección 1: ¿Qué es LINQ to SQL?

LINQ to SQL es un mapeo objeto-relacion (ORM), permite no sólo consultar, sino también insertar, actualizar o borrar los datos.

Modelado de sus datos
La mejor manera de comprender  LINQ to SQL es empezar con algunos modelos de datos. Esto también ayuda al proporcionar un modelo visual de las clases y cómo se relacionan el uno al otro.

Generación de un modelo de LINQ to SQL desde una base de datos existente
La forma más sencilla de empezar a trabajar con LINQ to SQL es generar un modelo a partir de una base de datos existente. Se puede hacer haciendo clic en: Explorador de soluciones --> Agregar | Nuevo elemento | Clases de LINQ to SQL y colocarle un nombre “Northwind.dbml” al modelo .

clip_image001

Desde el Explorador de servidores, el panel de la izquierda, se pueden arrastrar las tablas y soltar, (requiere tener una conexión configurada para Microsoft SQLServer). Si no se tiene una conexión a la base de datos Northwind  , se puede hacer clic en agregar nueva Conexion de datos, seleccionar Microsoft SQL Server, y clic en Aceptar.

clip_image002  clip_image003

En la ventana Agregar conexión, escribir el nombre del servidor (por ejemplo,. \ SQLExpress para la instancia local de SQL Express), seleccionar la base de datos Northwind y haga clic en Aceptar.

 clip_image004
También puede arrastrar los procedimientos almacenados para el panel de la derecha y soltar.

Para este ejemplo se va a usar las tablas Customer, Order, Employee y Order_Detail.

clip_image005

 

Se puede reemplazar el nombre propuesto de cualquier clase, haciendo clic en la clase en la ventana de diseño y abrir la ventana Propiedades. Las propiedades de inserción, actualización y eliminación están predeterminadas para utilizarse en tiempo de ejecución y generar la lógica apropiada, pero esto se puede cambiar para ejecutar un procedimiento almacenado.
El diseñador de LINQ to SQL también importa las relaciones en el diagrama.

Examinar los resultados del diseño
Al guardar y cerrar el diseñador de LINQ to SQL, que crean los tipos de la aplicación que pueden tener acceso a las entidades y los procedimientos almacenados en su diagrama. Estos tipos se pueden ver haciendo clic en el signo más al lado del archivo Northwind.dbml o haciendo clic en el botón Mostrar todos los archivos en la parte superior de la ventana del Explorador de soluciones.

Examinando una clase entidad
Una clase de entidad está adornada con los atributos, como se muestra en el ejemplo de código:

--------------------------------------VB------------------------------------------

<Global.System.Data.Linq.Mapping.TableAttribute(Name:="dbo.Customers"), _

Global.System.Runtime.Serialization.DataContractAttribute()> _

Partial Public Class Customer

Implements System.ComponentModel.INotifyPropertyChanging,

System.ComponentModel.INotifyPropertyChanged

' codigo

End Class

--------------------------------------CS------------------------------------------

[global::System.Data.Linq.Mapping.TableAttribute(Name = "dbo.Customers")]

[global::System.Runtime.Serialization.DataContractAttribute()]

public partial class Customer : INotifyPropertyChanging, INotifyPropertyChanged

{

//codigo

}

El primer atributo es TableAttribute, que LINQ to SQL utiliza para identificar la tabla en SQL Server, la tabla dbo.Customers de la base de datos.
El segundo atributo es DataContractAttribute, lo que permite la serialización de la clase cliente cuando se utiliza con Windows Communication Foundation (WCF). Este atributo existe porque la propiedad de la serialización en NorthwindDataContext se establece en unidireccional. Si no se establece la propiedad en modo de serialización, no se verá este atributo.
La clase implementa la interfaz del cliente INotifyPropertyChanging, que define
un evento PropertyChanging. Si no se implementa INotifyPropertyChanging, LINQ toSQL changetracker asume que todos los objetos consultados van a cambiar, y mantiene automáticamente una copia de todos los objetos consultados.
La clase  también implementa la interfaz INotifyPropertyChanged, que tiene un
Evento PropertyChanged. Si el objeto no esta enlazado a datos, no necesita esta implementación de la interfaz.

Despues , la clase Customer tiene campos privados y propiedades públicas para cada columna en la tabla de base de datos. Ej:

--------------------------------------VB------------------------------------------

Private _CustomerID As String

<Global.System.Data.Linq.Mapping.ColumnAttribute(Storage:="_CustomerID", _

DbType:="NChar(5) NOT NULL", CanBeNull:=False, IsPrimaryKey:=True), _

Global.System.Runtime.Serialization.DataMemberAttribute(Order:=1)> _

Public Property CustomerID() As String

Get

Return Me._CustomerID

End Get

Set(ByVal value As String)

If (String.Equals(Me._CustomerID, value) = False) Then

Me.OnCustomerIDChanging(value)

Me.SendPropertyChanging()

Me._CustomerID = value

Me.SendPropertyChanged("CustomerID")

Me.OnCustomerIDChanged()

End If

End Set

End Property

--------------------------------------CS------------------------------------------

private string _CustomerID;

[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_CustomerID",

DbType="NChar(5) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]

[global::System.Runtime.Serialization.DataMemberAttribute(Order=1)]

public string CustomerID

{

get

{

return this._CustomerID;

}

set

{

if ((this._CustomerID != value))

{

this.OnCustomerIDChanging(value);

this.SendPropertyChanging();

this._CustomerID = value;

this.SendPropertyChanged("CustomerID");

this.OnCustomerIDChanged();

}

}

}

ColumnAttribute: Este atributo identifica a cada propiedad con persistencia. Sin este atributo, CustomerID no se guardará en la base de datos.

DataMemberAttribute: indica a los servicios de WCF que los datos de esta propiedad se pueden serializar.

Getter: devuelve el valor de la propiedad que es privada.

Setter: tiene un código que llama a los métodos parciales OnCustomerIDChanging y OnCustomerChanged (serán llamados automáticamente para que notifiquen antes y después de un cambio). El Setter también tiene código para activar los eventos PropertyChanged y  PropertyChanging.
También tiene un campo adicional privado y la propiedad pública para cada tabla secundaria a la que se hace referencia.

--------------------------------------VB------------------------------------------

Private _Orders As EntitySet(Of [Order])

<Global.System.Data.Linq.Mapping.AssociationAttribute(Name:="Customer_Order", _

Storage:="_Orders", ThisKey:="CustomerID", OtherKey:="CustomerID"), _

Global.System.Runtime.Serialization.DataMemberAttribute(Order:=12, _

EmitDefaultValue:=False)> _

Public Property Orders() As EntitySet(Of [Order])

Get

If (Me.serializing _

AndAlso (Me._Orders.HasLoadedOrAssignedValues = False)) Then

Return Nothing

End If

Return Me._Orders

End Get

Set(ByVal value As EntitySet(Of [Order]))

Me._Orders.Assign(value)

End Set

End Property

--------------------------------------CS------------------------------------------

private EntitySet<Order> _Orders;

[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Customer_Order", Storage="_

Orders", ThisKey="CustomerID", OtherKey="CustomerID")]

[global::System.Runtime.Serialization.DataMemberAttribute(Order=12,

EmitDefaultValue=false)]

public EntitySet<Order> Orders

{

get

{

if ((this.serializing && (this._Orders.HasLoadedOrAssignedValues == false)))

{

return null;

}

return this._Orders;

}

set

{

this._Orders.Assign(value);

}

}

 

AssociationAttribute: identifica Customers_Order como una relación de la tabla Customers a la tabla Order. El atributo también identifica la clave (s) que se utilizan en las tablas Customers y Order.

EntitySet: es una colección especializada que proporciona la carga y el mantenimiento de la relación de uno a muchos y las relaciones uno-a-uno. Tiene un evento ListChanged a los que se puede suscribir si se desea recibir una notificación cuando se le hace una tarea a esta colección.
Getter: retorna nothing (C # null) si el cliente está siendo serializado para evitar también la serialización de pedidos. También devuelve nothing (C #null) si no hay un valor asignado a esta propiedad o si esta propiedad no se ha cargado.
Setter: pasa el valor de entrada al método Assign.

Examinando la clase DataContext
La clase NorthwindDataContext fue creado por el diseñador de LINQ to SQL. Esta clase hereda de la clase DataContext que es parte de. NET Framework. La clase DataContext es el objeto principal para mover datos desde y hacia la base de datos. Se debe crear una instancia de la clase NorthwindDataContext y utilizar sus propiedades y métodos para facilitar el acceso a la base de datos.

--------------------------------------VB------------------------------------------

<Global.System.Data.Linq.Mapping.DatabaseAttribute(Name:="Northwind")> _

Partial Public Class NorthwindDataContext

Inherits System.Data.Linq.DataContext

Private Shared mappingSource As System.Data.Linq.Mapping.MappingSource = _

New AttributeMappingSource()

'miembros

End Class

--------------------------------------CS------------------------------------------

[global::System.Data.Linq.Mapping.DatabaseAttribute(Name="Northwind")]

public partial class NorthwindDataContext : System.Data.Linq.DataContext

{

private static System.Data.Linq.Mapping.MappingSource mappingSource = _

new AttributeMappingSource();

//miembros

}

Esta clase tiene un campo estático llamado mappingSource, que por defecto es una instancia de la clase AttributeMappingSource. Este campo contiene la correspondencia entre las clases en el dominio y la base de datos según lo especificado por los atributos en las clases de entidad. Se puede optar por sustituir este objeto con una instancia de XmlMappingSource, lo que permitirá externalizar las asignaciones en un archivo XML.

La clase NorthwindDataContext contiene una propiedad pública para cada entidad. Ejemplo de la propiedad de Customer:

--------------------------------------VB------------------------------------------

Public ReadOnly Property Customers() As System.Data.Linq.Table(Of Customer)

Get

Return Me.GetTable(Of Customer)()

End Get

End Property

--------------------------------------CS------------------------------------------

public System.Data.Linq.Table<Customer> Customers

{

get

{

return this.GetTable<Customer>();

}

}

La clase NorthwindDataContext también contiene métodos parciales que podrían implementarse para insertar, actualizar y eliminar objetos en cualquiera de las propiedades de las tablas de esta clase.

¿Cómo se conecta LINQ to SQL  a la base de datos?
Cuando se agregan los elementos del Explorador de servidores, también se agrega automáticamente la cadena de conexión de base de datos al proyecto . Si nos fijamos en el web.config, se encuentra la siguiente configuración:

<connectionStrings>

<add name="LinqToSqlSampleCode.Properties.Settings.NorthwindConnectionString"

connectionString="Data Source=.;Initial Catalog=Northwind;Integrated

Security=True"

providerName="System.Data.SqlClient" />

</connectionStrings>

El DataContext tiene una propiedad de conexión, y algunos de los constructores de la NorthwindDataContext que aceptan una cadena de conexión. 

--------------------------------------VB------------------------------------------

Public Sub New()

MyBase.New(Global.LinqToSqlSampleCode.MySettings.Default.NorthwindConnectionString, _

mappingSource)

OnCreated()

End Sub

--------------------------------------CS------------------------------------------

public NorthwindDataContext() :

base(global::LinqToSqlSampleCode.Properties.Settings.Default.

NorthwindConnectionString,

mappingSource)

{

OnCreated();

}

Que se envía al SQL Server, y cuándo se envía?
El siguiente ejemplo es  una consulta LINQ  que recupera una lista de los empleados con apellidos que comienzan con "D" y lo asigna a un datagrid de una aplicación de Windows Presentation
Foundation (WPF).

--------------------------------------VB------------------------------------------

Dim ctx = New NorthwindDataContext()

Dim employees = From emp In ctx.Employees

Where emp.LastName.StartsWith("D")

Select emp

dg.ItemsSource = employees

End Sub

--------------------------------------CS------------------------------------------

var ctx = new NorthwindDataContext();

var employees = from emp in ctx.Employees

where emp.LastName.StartsWith("D")

select emp;

dg.ItemsSource = employees;

clip_image006

Para ver cuales fueron las consultas que se realizaron se puede usar SQL Server Profiler para capturar todas las instrucciones SQL enviadas a SQL Server. (esta herramienta no se incluye con SQL Server Express).
Para utilizar el SQL Server Profiler, debe tener privilegios de administrador en SQL Server o el administrador de SQL Server puede conceder permisos para ejecutar la herramienta de perfiles.

clip_image007

 

Eager Loading vs. Lazy Loading (carga ansiosa vs carga perezosa o diferida)

Cuando se especifican las propiedades o las asociaciones para realizar consultas en  una entidad, se puede elegir la carga ansiosa o carga diferida. Carga diferida es también conocida como la carga con delay. Carga ansiosos también se conoce como carga con pre-fetch . El comportamiento predeterminado es realizar la carga ansiosa de las propiedades, lo que significa que una propiedad se carga solo cuando se ejecuta una consulta que hace referencia a esta propiedad.

clip_image008

Si todas las propiedades fueran cargadas perezosamente, no habría un costo asociado con el establecimiento de la conexión y la transferencia de los datos. La elección de su uso depende de la cantidad de datos que se transferirán y cómo se van a utilizar los datos.

El siguiente ejemplo muestra un cargado con Retardo:

--------------------------------------VB------------------------------------------

Dim ctx = New NorthwindDataContext()

Dim sw = New StringWriter()

ctx.Log = sw

Dim employee = (From emp In ctx.Employees

Where emp.LastName.StartsWith("D")

Select emp).First()

MessageBox.Show(sw.GetStringBuilder().ToString())

sw = New StringWriter()

ctx.Log = sw

Dim photo = New MemoryStream(Employee.Photo.ToArray())

MessageBox.Show(sw.GetStringBuilder().ToString())

--------------------------------------CS------------------------------------------

var ctx = new NorthwindDataContext();

var sw = new StringWriter();

ctx.Log = sw;

var employee = (from emp in ctx.Employees

where emp.LastName.StartsWith("Davolio")

select emp).First();

MessageBox.Show(sw.GetStringBuilder().ToString());

sw = new StringWriter();

ctx.Log = sw;

var photo = new MemoryStream(employee.Photo.ToArray());

MessageBox.Show(sw.GetStringBuilder().ToString());

Este ejemplo recupera un solo empleado y muestra la instrucción SQL.

--------------------------------------VB------------------------------------------

Dim ctx = New NorthwindDataContext()

Dim sw = New StringWriter()

ctx.Log = sw

Dim employee = (From emp In ctx.Employees

Where emp.LastName.StartsWith("D")

Select emp).First()

MessageBox.Show(sw.GetStringBuilder().ToString())

sw = New StringWriter()

ctx.Log = sw

Dim photo = New MemoryStream(Employee.Photo.ToArray())

MessageBox.Show(sw.GetStringBuilder().ToString())

--------------------------------------CS------------------------------------------

var ctx = new NorthwindDataContext();

var sw = new StringWriter();

ctx.Log = sw;

var employee = (from emp in ctx.Employees

where emp.LastName.StartsWith("Davolio")

select emp).First();

MessageBox.Show(sw.GetStringBuilder().ToString());

sw = new StringWriter();

ctx.Log = sw;

var photo = new MemoryStream(employee.Photo.ToArray());

MessageBox.Show(sw.GetStringBuilder().ToString());

 

La primera consulta que se corrió no incluye las fotos, aunque la expresión LINQ solicitó
el empleado entero. La propiedad de la Foto no se incluyó debido a que la propiedad Delay Loaded
está en true. Cuando se accedió a la propiedad fotos, LINQ to SQL hizo un llamado para recuperar fotos. En la segunda consulta, cuando se utiliza lazy loading, básicamente estás apostando a que no vamos a necesitar todas las columnas de contenido ", pero si los datos son necesarios, automáticamente se traerán.

clip_image009

Resumen de la lección
En esta lección se proporciona información detallada sobre el diseñador de LINQ to SQL.
■ Se puede crear un modelo de entidad fácilmente arrastrando y soltando las tablas de base de datos del Explorador de servidores.
■ Una tabla que se deja caer en el diseñador de LINQ to SQL crea una clase de entidad que representa cada fila de la tabla.
■ Puede arrastrar y soltar los procedimientos almacenados a la superficie del diseñador de LINQ to SQLque crea los métodos que se pueden llamar desde la aplicación.
■ las clases de entidad y implementan INotifyPropertyChanging INotifyPropertyChanged para
un seguimiento de manera eficiente por el servicio de seguimiento de objetos.
■ El objeto DataContext proporciona una propiedad para cada tabla y un método para cada uno
procedimientos almacenados.
■ LINQ to SQL admite la carga ansiosa (eager loading) por defecto, pero se puede habilitar la carga diferida, también conocido como lazy loading, para aplazar la carga de las propiedades de la entidad hasta que realmente se referencie a la propiedad en el código.

 

No hay comentarios:

Publicar un comentario