Capitulo 4: LINQ to SQL (Parte 3)

Lección 3: Enviando cambios a la base de datos
Usando DataContext para controlar los cambios y los objetos de caché

El DataContext administra la identidad del objeto, las filas recuperadas de la tabla de base de datos se registran automáticamente en la tabla del DataContext y la clave principal de la fila cuando se crea el objeto. Si la misma fila se recupera de nuevo por este objeto DataContext, se devuelve la instancia original. Si, por ejemplo se,recupera una fila en la aplicación y se modifica esta misma fila en la tabla de base de datos, no se obtendrán los datos actualizados, porque la fila se muestra desde la caché del DataContext.
La razón de este comportamiento es que LINQ to SQL necesita este comportamiento para apoyar la concurrencia optimista. DataContext contiene un método llamadoSubmitChanges, que envía a todos sus cambios de vuelta a la base de datos.

Si la base de datos contiene una tabla sin una clave principal, LINQ to SQL permite las consultas sobre la tabla pero no permite actualizaciones, porque no puede identificar la fila a actualizar, dada la falta de una clave única.
El ciclo de vida de una entidad
Cuando se recupera un objeto con una consulta LINQ to SQL, se puede modificar, quizás varias veces, hasta que la aplicación está lista para enviar los cambios al servidor. Si la entidad ya no se referencia a en la aplicación, el objeto será reclamado por el garbage collector. Cuando el DataContext usa el  método SubmitChanges, los datos se envían y se guardan en la base de datos. Debido a la persistencia de la base de datos, se puede consultar el objeto DataContext para los mismos datos, y se recibirá un objeto que parece y se comporta como el objeto original.
El DataContext ofrece un servicio de rastreo que sigue el estado de sus objetos. Los objetos LINQ to SQL siempre tienen un estado.  En términos de rendimiento y uso de recursos, el seguimiento de objetos utilizando el servicio de rastreo de la identidad no es costoso, pero hacer cambios en el objeto mediante el servicio de seguimiento de cambios pueden ser costosos.

Estado Descripción
Untracked
Un Objeto de LINQ to SQL que no realiza un seguimiento. Algunos ejemplos son:
  • Un objeto no consultado a través del DataContext actual (como un objeto recientemente creado).
  • Un objeto creado a través de la deserialización.
  • Un objeto consultado a través de un DataContext diferente.
Unchanged
Un objeto recuperado utilizando el DataContext actual y que se desconoce si se ha modificado desde que se creó.
PossiblyModified
Objeto asociado a un DataContext. Para obtener más información, vea Recuperación de datos y operaciones CUD en aplicaciones de niveles N (LINQ to SQL).
ToBeInserted
Objeto no recuperado utilizando el DataContext actual. Esto produce una operación INSERT en la base de datos durante la ejecución de SubmitChanges.
ToBeUpdated
Objeto que se sabe que se modificó desde que se recuperó. Esto produce una operación UPDATE en la base de datos durante la ejecución deSubmitChanges.
ToBeDeleted
Objeto marcado para la eliminación, que produce una operación DELETE en la base de datos durante la ejecución de SubmitChanges.
Deleted
Objeto eliminado de la base de datos. Este estado es definitivo y no permite transiciones adicionales.
Modificando entidades existentes
Cuando LINQ to SQL crea un objeto nuevo, el objeto se encuentra en estado Unchanged. Si se modifica, el DataContext va a cambiar el estado del objeto a ToBeUpdated.
Esto es lo que el DataContext utiliza para determinar qué objetos deben conservarse al momento de enviar los cambios.
¿Cómo funciona el DataContext saber cuándo cambiar su objeto?
Las notificaciones de cambios se llevan a cabo a través del evento PropertyChanging. Cuando DataContext recibe una notificación de cambio, crea una copia del objeto y cambia su estado a ToBeUpdated.
Cuando se llama a SubmitChanges, DataContext se comparan los valores actuales y los originales, campo por campo, para decidir si el objeto ha sido cambiado. Por ej :
-----------------------------------VB------------------------------------
customer.ContactName = "Marty " + DateTime.Now.ToLongTimeString()
ctx.SubmitChanges()
-----------------------------------CS------------------------------------
customer.ContactName = "Marty " + DateTime.Now.ToLongTimeString();
ctx.SubmitChanges();


Agregando nuevas entidades al DataContext
Un nuevo objeto de una instancia es desconocido para  el DataContext y se encuentra en estado sin seguimiento. Se puede utilizar el método InsertOnSubmit, o bien puede asignar el nuevo objeto a un objeto que ya está conectado, y el DataContext descubrirá el nuevo objeto de modo que se pueden guardar en la base de datos. También puede llamar al método InsertAllOnSubmit cuando se tiene muchos objetos a insertar:

-----------------------------------VB------------------------------------
Dim ctx = New NorthwindDataContext()
Dim sw = New StringWriter()
ctx.Log = sw
Dim employee = New Employee With
{
.FirstName = "John",
.LastName = "Smith"
}
ctx.Employees.InsertOnSubmit(employee)
ctx.SubmitChanges()
MessageBox.Show(sw.GetStringBuilder().ToString())

-----------------------------------CS------------------------------------
var ctx = new NorthwindDataContext();
var sw = new StringWriter();
ctx.Log = sw;
var employee = new Employee
{
FirstName = "John",
LastName = "Smith"
};
ctx.Employees.InsertOnSubmit(employee);
ctx.SubmitChanges();
MessageBox.Show(sw.GetStringBuilder().ToString());

Y genera el siguiente SQL:

INSERT INTO [dbo].[Employees]([LastName], [FirstName], [Title],
[TitleOfCourtesy], [BirthDate], [HireDate], [Address], [City], [Region],
[PostalCode], [Country], [HomePhone], [Extension], [Photo],
[Notes], [ReportsTo], [PhotoPath])
VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8,
@p9, @p10, @p11, @p12, @p13, @p14, @p15, @p16)
SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]

En este ejemplo, se crea un empleado  y se carga  con datos. El mpleado se agrega a la tabla Customers en el DataContext mediante el método de InsertOnSubmit. Finalmente, el método SubmitChanges del DataContext  envia el nuevo empleado a la tabla de base de datos Customers.

Eliminando Entidades
Cuando se desean eliminar filas de una tabla de base de datos, puede llamar a los métodos o DeleteOnSubmit o DeleteAllOnSubmit sobre la tabla apropiada del DataContext.

Eliminar, normalmente requiere que se busque el elemento o los elementos que desean eliminar y luego se pasan al método. Por ej el empleado con ID 10:

-----------------------------------VB------------------------------------
Dim ctx = New NorthwindDataContext()
Dim sw = New StringWriter()
ctx.Log = sw
Dim employee = (From emp In ctx.Employees
Where emp.EmployeeID = 10
Select emp).First()
ctx.Employees.DeleteOnSubmit(employee)
ctx.SubmitChanges()
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.EmployeeID == 10
select emp).First();
ctx.Employees.DeleteOnSubmit(employee);
ctx.SubmitChanges();
MessageBox.Show(sw.GetStringBuilder().ToString());

SQL que genera:

SELECT TOP (1) [t0].[EmployeeID], [t0].[LastName], [t0].[FirstName],
[t0].[Title], [t0].[TitleOfCourtesy], [t0].[BirthDate], [t0].[HireDate],
[t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode], [t0].[Country],
[t0].[HomePhone], [t0].[Extension], [t0].[Notes],
[t0].[ReportsTo], [t0].[PhotoPath]
FROM [dbo].[Employees] AS [t0]
WHERE [t0].[EmployeeID] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [10]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
DELETE FROM [dbo].[Employees] WHERE ([EmployeeID] = @p0) AND
([LastName] = @p1) AND
([FirstName] = @p2) AND ([Title] IS NULL) AND
([TitleOfCourtesy] IS NULL) AND ([BirthDate] IS NULL) AND
([HireDate] IS NULL) AND ([Address] IS NULL) AND
([City] IS NULL) AND ([Region] IS NULL) AND
([PostalCode] IS NULL) AND ([Country] IS NULL) AND
([HomePhone] IS NULL) AND ([Extension] IS NULL) AND
([ReportsTo] IS NULL) AND ([PhotoPath] IS NULL)
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [10]
-- @p1: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [Smith]
-- @p2: Input NVarChar (Size = 4000; Prec = 0; Scale = 0) [John]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1

LINQ to SQL no admite ni reconoce las operaciones de eliminación en cascada. Si desea eliminar una fila en una tabla que tiene restricciones en su contra, hay dos opciones: 
Establecer la regla ON DELETE CASCADE en la restricción (Constraint) de la clave foránea en la base de datos (Esto elimina automáticamente las filas hijas cuando una fila de padres se elimina).
La otra opción es escribir nuestro código para eliminar primero los objetos secundarios que representen un obstáculo para el objeto principal para ser eliminados.



Uso de procedimientos almacenados
Los procedimientos almacenados se agregan al diseñador y se convierten en métodos del DataContext.
El siguiente ejemplo muestra una llamada a al procedimiento almacenado CustOrderHist, que contiene una instrucción SELECT que devuelve una lista de todos los productos que un cliente ha comprado:

-----------------------------------VB------------------------------------
Dim ctx = New NorthwindDataContext()
Dim sw = New StringWriter()
ctx.Log = sw
dg.ItemsSource = ctx.CustOrderHist("ALFKI")
MessageBox.Show(sw.GetStringBuilder().ToString())

-----------------------------------CS------------------------------------
var ctx = new NorthwindDataContext();
var sw = new StringWriter();
ctx.Log = sw;
dg.ItemsSource = ctx.CustOrderHist("ALFKI");
MessageBox.Show(sw.GetStringBuilder().ToString());

El SQL generado seria:

EXEC @RETURN_VALUE = [dbo].[CustOrderHist] @CustomerID = @p0
-- @p0: Input NChar (Size = 5; Prec = 0; Scale = 0) [ALFKI]
-- @RETURN_VALUE: Output Int (Size = -1; Prec = 0; Scale = 0) [Null]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1


Usando DataContext para enviar los cambios

Ningún cambio se envía a la base de datos hasta que se llama el método SubmitChanges del objeto DataContext. Cuando se llama al método SubmitChanges, el DataContext trata de traducir los cambios en los comandos SQL.

Lo primero que el procesador de cambio va a hacer es examinar el conjunto de objetos conocidos para determinar si los objetos nuevos se han atachado. Si es así, se agregan a los objetos rastreados.

A continuación, todos los objetos que tienen cambios pendientes se ordenan en una secuencia basada en las dependencias entre los objetos. Objetos cuyos cambios dependen de otros objetos se encuentra después de sus dependencias.

El objeto DataContext inicia una transacción para encapsular la inserción individual, actualizar y eliminar comandos.

Finalmente, los cambios a los objetos se envían al servidor de base de datos, uno por uno, como comandos de SQL. Si se encuentran errores y se produce una excepción la transacción deshace todos los cambios a la base de datos pero el DataContext tiene todavía todos los cambios, por lo que se puede tratar de corregir el problema y volver a llamar a SubmitChanges si es necesario.


Enviando cambios en una transacción
LINQ to SQL admite tres modelos de transacción para enviar los cambios de vuelta a la base de datos:

Transacción local explícita: si la propiedad Transaction se establece en una transacción (IDbTransaction), la llamada a SubmitChanges se ejecuta en el contexto de la misma transacción.

Es nuestra responsabilidad confirmar o revertir la transacción después de una correcta ejecución. La conexión que corresponde a la transacción debe coincidir con la conexión utilizada para construir DataContext. Si se utiliza una conexión diferente, se inicia una excepción.

Transacción distribuible explícita :  Se puede llamar a las API de LINQ to SQL (incluidas, entre otras, SubmitChanges) en el ámbito de un objeto Transaction activo. LINQ to SQL detecta que la llamada está en el ámbito de una transacción y no crea una nueva transacción. LINQ to SQL también impide el cierre de la conexión en ese caso. Se puede ejecutar consultas y el método SubmitChanges en el contexto de este tipo de transacción.

Transacción implícita: Al llamar a SubmitChanges, LINQ to SQL comprueba si la llamada se encuentra en el ámbito de una Transaction o si la propiedad Transaction (IDbTransaction) está establecida en una transacción local iniciada por el usuario. Si no encuentra ninguna de estas transacciones, LINQ to SQL inicia una transacción local (IDbTransaction) y la usa para ejecutar los comandos SQL generados. Cuando se han completado todos los comandos SQL correctamente, LINQ to SQL confirma la transacción local y devuelve un valor.


Resumen de la lección

■ El DataContext contiene el servicio de seguimiento de la identidad que sigue objetos creados por éste.
■ Se llama al método SubmitChanges del objeto DataContext para enviar todos los cambios a la base de datos.
■ Para hacer DataContext consciente de un nuevo objeto,se  llama al método InsertOnSubmit en la tabla correspondiente.
■ Para borrar los datos de la base de datos, se  llama al método DeleteOnSubmit en latabla correspondiente, con el objeto de entidad que desea eliminar.
■ Los cambios se pueden enviar de vuelta a la base de datos en una transacción implícita, pero también se puede crear una transacción explícita para cuando se necesitan otras acciones para formar parte de la misma transacción.
________________________________________________________________________________

Una Yapa:
A continuación les dejos un par de videos que encontré hace un año, cuando recien comenzaba con Linq, son simples, fáciles de entender  y nos va a servir para ver un pequeño ejemplo de una aplicación winform usando linq.


Parte 1 - LinQ to SQL


Parte 2- LinQ to SQL

No hay comentarios:

Publicar un comentario