.net - studio - ¿Qué puedo hacer para resolver una excepción "Fila no encontrada o modificada" en LINQ to SQL en una base de datos SQL Server Compact Edition?
linq to sql visual studio 2017 (13)
Cuando ejecuto SubmitChanges en el DataContext después de actualizar un par de propiedades con una conexión LINQ a SQL (en comparación con SQL Server Compact Edition) obtengo una "Fila no encontrada o modificada". ChangeConflictException.
var ctx = new Data.MobileServerDataDataContext(Common.DatabasePath);
var deviceSessionRecord = ctx.Sessions.First(sess => sess.SessionRecId == args.DeviceSessionId);
deviceSessionRecord.IsActive = false;
deviceSessionRecord.Disconnected = DateTime.Now;
ctx.SubmitChanges();
La consulta genera el siguiente SQL:
UPDATE [Sessions]
SET [Is_Active] = @p0, [Disconnected] = @p1
WHERE 0 = 1
-- @p0: Input Boolean (Size = 0; Prec = 0; Scale = 0) [False]
-- @p1: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:12:02 PM]
-- Context: SqlProvider(SqlCE) Model: AttributedMetaModel Build: 3.5.21022.8
El problema obvio es WHERE 0 = 1 , después de cargar el registro, he confirmado que todas las propiedades en "deviceSessionRecord" son correctas para incluir la clave principal. Además, al detectar la "ChangeConflictException" no hay información adicional sobre por qué esto falló. También he confirmado que se lanza esta excepción con exactamente un registro en la base de datos (el registro que intento actualizar)
Lo extraño es que tengo una declaración de actualización muy similar en una sección diferente de código y genera el siguiente SQL y de hecho actualiza mi base de datos SQL Server Compact Edition.
UPDATE [Sessions]
SET [Is_Active] = @p4, [Disconnected] = @p5
WHERE ([Session_RecId] = @p0) AND ([App_RecId] = @p1) AND ([Is_Active] = 1) AND ([Established] = @p2) AND ([Disconnected] IS NULL) AND ([Member_Id] IS NULL) AND ([Company_Id] IS NULL) AND ([Site] IS NULL) AND (NOT ([Is_Device] = 1)) AND ([Machine_Name] = @p3)
-- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0) [0fbbee53-cf4c-4643-9045-e0a284ad131b]
-- @p1: Input Guid (Size = 0; Prec = 0; Scale = 0) [7a174954-dd18-406e-833d-8da650207d3d]
-- @p2: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:20:50 PM]
-- @p3: Input String (Size = 0; Prec = 0; Scale = 0) [CWMOBILEDEV]
-- @p4: Input Boolean (Size = 0; Prec = 0; Scale = 0) [False]
-- @p5: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:20:52 PM]
-- Context: SqlProvider(SqlCE) Model: AttributedMetaModel Build: 3.5.21022.8
He confirmado que los valores de los campos primarios adecuados se han identificado tanto en el esquema de la base de datos como en el DBML que genera las clases LINQ.
Supongo que esto es casi una pregunta en dos partes:
- ¿Por qué se lanza la excepción?
- Después de revisar el segundo conjunto de SQL generado, parece que para detectar conflictos sería bueno verificar todos los campos, pero imagino que esto sería bastante ineficiente. ¿Es esta la manera en que esto siempre funciona? ¿Hay una configuración para simplemente verificar la clave principal?
He estado luchando con esto durante las últimas dos horas, por lo que cualquier ayuda sería apreciada.
Después de emplear la respuesta de qub1n, descubrí que el problema para mí era que inadvertidamente había declarado que una columna de base de datos era decimal (18,0). Estaba asignando un valor decimal, pero la base de datos lo estaba cambiando, eliminando la parte decimal. Esto dio lugar a que la fila cambiara de tema.
Solo agregue esto si alguien más se encuentra con un problema similar.
En mi caso, el problema fue con las opciones de usuario de todo el servidor. Siguiendo:
https://msdn.microsoft.com/en-us/library/ms190763.aspx
He habilitado la opción NOCOUNT con la esperanza de obtener algunos beneficios de rendimiento:
EXEC sys.sp_configure ''user options'', 512;
RECONFIGURE;
y esto rompe las comprobaciones de Linq para las Filas Afectadas (todo lo que puedo deducir de fuentes .NET), lo que lleva a la excepción ChangeConflictException
Restablecer las opciones para excluir el 512 bit solucionó el problema.
Eso es desagradable, pero simple:
Compruebe si los tipos de datos para todos los campos en el O / R-Designer coinciden con los tipos de datos en su tabla SQL. ¡Cheque doble para anulables! Una columna debe ser anulable tanto en O / R-Designer como en SQL, o no puede contener nulos en ambas.
Por ejemplo, una columna NVARCHAR "título" está marcada como NULLable en su base de datos y contiene el valor NULL. A pesar de que la columna está marcada como NOT NULL en su Asignación de O / R, LINQ la cargará correctamente y establecerá la columna String en null.
- Ahora cambia algo y llama a SubmitChanges ().
- LINQ generará una consulta SQL que contenga "WHERE [title] IS NULL", para asegurarse de que el título no haya sido modificado por otra persona.
- LINQ busca las propiedades de [título] en la asignación.
- LINQ encontrará [título] NOT NULLable.
- Como [título] NO es NULLable, ¡por lógica nunca podría ser NULL!
- Entonces, optimizando la consulta, LINQ la reemplaza por "donde 0 = 1", el equivalente SQL de "nunca".
El mismo síntoma aparecerá cuando los tipos de datos de un campo no coincidan con el tipo de datos en SQL, o si faltan campos, ya que LINQ no podrá asegurarse de que los datos SQL no hayan cambiado desde la lectura de los datos.
Esto es lo que necesita para anular este error en el código C #:
try
{
_db.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException e)
{
foreach (ObjectChangeConflict occ in _db.ChangeConflicts)
{
occ.Resolve(RefreshMode.KeepChanges);
}
}
Esto también puede ser causado por el uso de más de un DbContext.
Así por ejemplo:
protected void loginUser(string username)
{
var db = new AppDbContext();
var user = db.Users.Single(u => u.Username == username);
user.LastLogin = DateTime.UtcNow;
db.SubmitChanges();
}
protected void doSomething(object obj)
{
string username = "joe";
var db = new AppDbContext();
var user = db.Users.Single(u => u.Username == username);
if (DateTime.UtcNow - user.LastLogin > new TimeSpan(0, 30, 0))
loginUser(username);
user.Something = obj;
db.SubmitChanges();
}
Este código fallará de vez en cuando, en formas que parecen impredecibles, porque el usuario se usa en ambos contextos, se cambia y se guarda en uno, y luego se guarda en el otro. La representación en memoria del usuario que posee "Algo" no coincide con lo que está en la base de datos, y así obtiene este error al acecho.
Sería bueno si hubiera una herramienta que pudiera rastrearlos. Obviamente, sería horrible para el rendimiento en Producción, pero estos pueden ser errores realmente insidiosos una vez que tenga varias capas de lógica interactuando todas con la misma base de datos. Supongo que sería un contenedor que mantiene una tabla hash de objetos que se rastrea y falla pronto tan pronto como vio 2 contextos rastreando el mismo objeto simultáneamente (y arrojó una clara excepción sobre por qué y qué objeto).
Hay un método en DataContext llamado Refresh que puede ayudar aquí. Le permite volver a cargar el registro de la base de datos antes de que se envíen los cambios, y ofrece diferentes modos para determinar qué valores conservar. "KeepChanges" parece ser el más inteligente para mis propósitos; está destinado a fusionar mis cambios con cualquier cambio no conflictivo que ocurra en la base de datos mientras tanto.
Si lo entiendo correctamente :)
Lo arreglé agregando (UpdateCheck = UpdateCheck.Never)
a todas las definiciones de [Column]
.
Sin embargo, no parece una solución adecuada. En mi caso, parece estar relacionado con el hecho de que esta tabla tiene una asociación con otra tabla desde donde se borra una fila.
Esto está en Windows Phone 7.5.
No sé si encontraste alguna respuesta satisfactoria a tu pregunta, pero publiqué una pregunta similar y eventualmente la contesté yo mismo. Resultó que la opción de conexión predeterminada NOCOUNT estaba activada para la base de datos, lo que causó una excepción ChangeConflictException para cada actualización realizada con Linq a Sql. Puedes consultar mi publicación here .
Primero, es útil saber qué está causando el problema. La solución en Google debería ayudar, puede registrar los detalles (tabla, columna, valor antiguo, nuevo valor) sobre el conflicto para encontrar una mejor solución para resolver el conflicto más adelante:
public class ChangeConflictExceptionWithDetails : ChangeConflictException
{
public ChangeConflictExceptionWithDetails(ChangeConflictException inner, DataContext context)
: base(inner.Message + " " + GetChangeConflictExceptionDetailString(context))
{
}
/// <summary>
/// Code from following link
/// https://ittecture.wordpress.com/2008/10/17/tip-of-the-day-3/
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
static string GetChangeConflictExceptionDetailString(DataContext context)
{
StringBuilder sb = new StringBuilder();
foreach (ObjectChangeConflict changeConflict in context.ChangeConflicts)
{
System.Data.Linq.Mapping.MetaTable metatable = context.Mapping.GetTable(changeConflict.Object.GetType());
sb.AppendFormat("Table name: {0}", metatable.TableName);
sb.AppendLine();
foreach (MemberChangeConflict col in changeConflict.MemberConflicts)
{
sb.AppendFormat("Column name : {0}", col.Member.Name);
sb.AppendLine();
sb.AppendFormat("Original value : {0}", col.OriginalValue.ToString());
sb.AppendLine();
sb.AppendFormat("Current value : {0}", col.CurrentValue.ToString());
sb.AppendLine();
sb.AppendFormat("Database value : {0}", col.DatabaseValue.ToString());
sb.AppendLine();
sb.AppendLine();
}
}
return sb.ToString();
}
}
Crear ayuda para envolver su sumbitCambios:
public static class DataContextExtensions
{
public static void SubmitChangesWithDetailException(this DataContext dataContext)
{
try
{
dataContext.SubmitChanges();
}
catch (ChangeConflictException ex)
{
throw new ChangeConflictExceptionWithDetails(ex, dataContext);
}
}
}
Y luego llame al código de cambios de envío:
Datamodel.SubmitChangesWithDetailException();
Finalmente, registre la excepción en su controlador de excepción global:
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
//TODO
}
Recientemente me encontré con este error y descubrí que el problema no estaba en mi contexto de datos, sino con una instrucción de actualización activada dentro de un activador después de que se llamara a Commit en el contexto. El desencadenante intentaba actualizar un campo que no admite nulos con un valor nulo, y estaba causando el error de contexto con el mensaje mencionado anteriormente.
Estoy agregando esta respuesta únicamente para ayudar a otros a lidiar con este error y no encontrar una resolución en las respuestas anteriores.
Resolví este error al volver a dibujar sobre una tabla desde el explorador del servidor al diseñador y volver a construir.
Sé que esta pregunta ha sido respondida hace mucho tiempo, pero aquí he pasado las últimas horas golpeándome la cabeza contra la pared y solo quería compartir mi solución que resultó no estar relacionada con ninguno de los elementos de este hilo:
¡Caché!
La parte select () de mi objeto de datos estaba usando el almacenamiento en caché. Cuando se trataba de actualizar el objeto, aparecía un error de Fila no encontrada o cambiada.
Varias de las respuestas mencionaron el uso de diferentes DataContext y en retrospectiva, esto es probablemente lo que estaba sucediendo, pero no me llevó instantáneamente a pensar en el almacenamiento en caché, así que espero que esto ayude a alguien.
También obtuve este error debido al uso de dos contextos diferentes. Resolví este problema mediante el uso de un solo contexto de datos.