2009-09-30 12 views
6

Aquí hay un pequeño experimento que hice:Valores de datos de almacenamiento en caché de Linq: ¿problema importante de concurrencia?

MyClass obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single(); 
Console.WriteLine(obj.MyProperty); // output = "initial" 
Console.WriteLine("Waiting..."); // put a breakpoint after this line 
obj = null; 
obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single(); // same as before, but reloaded 
Console.WriteLine(obj.MyProperty); // output still = "initial" 
obj.MyOtherProperty = "foo"; 
dataContext.SubmitChanges(); // throws concurrency exception 

Cuando golpeo el punto de interrupción después de la línea 3, voy a una ventana de consulta SQL y cambiar manualmente el valor de "actualización". Entonces sigo corriendo. ¡Linq no recarga mi objeto, sino que reutiliza el que tenía previamente en la memoria! ¡Este es un gran problema para la concurrencia de datos!

¿Cómo se desactiva este caché oculto de objetos que obviamente Linq guarda en la memoria?

EDIT - En pocas palabras, es impensable que Microsoft haya podido dejar un abismo tan grande en el marco de Linq. El código anterior es una versión simplificada de lo que estoy haciendo en realidad, y puede haber pequeñas sutilezas que me he perdido. En resumen, apreciaría que hicieras tu propia experimentación para verificar que mis hallazgos anteriores sean correctos. Alternativamente, debe haber algún tipo de "interruptor secreto" que haga que Linq sea robusto frente a las actualizaciones de datos concurrentes. ¿Pero que?

Respuesta

5

Esto no es un problema que he encontrado antes (ya que no tienden a mantener DataContexts abierta durante largos períodos de tiempo), pero parece que alguien más tiene:

http://www.rocksthoughts.com/blog/archive/2008/01/14/linq-to-sql-caching-gotcha.aspx

+1

+1 ¡Dios mío, realmente es un Gotcha! Tengo que agregar eso al C# Gotcha KB ... –

+1

Peor aún, si sigues sus consejos allí y configura ObjectTrackingEnabled = false, solo puedes leer desde el DB; ¡no puedes hacer SubmitChanges! ¡Eso REALMENTE hace la vida difícil! –

+0

Aquí hay un enlace al C# gotcha KB: http://stackoverflow.com/questions/241134/what-is-the-worst-c-net-gotcha –

2

Establezca la propiedad ObjectTrackingEnabled del DataContext en falso.

Cuando ObjectTrackingEnabled se establece en true, DataContext se comporta como un Unit of Work. Mantendrá cualquier objeto cargado en la memoria para que pueda rastrear los cambios en él. El DataContext tiene que recordar el objeto tal como lo cargó originalmente para saber si se han realizado cambios.

Si está trabajando en un escenario de solo lectura, debe desactivar el seguimiento de objetos. Puede ser una mejora de rendimiento decente.

Si no está trabajando en un escenario de solo lectura, entonces no estoy seguro de por qué quiere que funcione de esta manera. Si ha realizado ediciones, ¿por qué quiere que se retire en estado modificado de la base de datos?

+0

Ya, pero no estoy trabajando en un entorno de solo lectura. ¿Alguna otra idea? –

+0

Esto es por diseño. Está diseñado para tirar de estado, modificar y escribir de nuevo usando concurrencia optamística. Puedes llamar a Refresh en el DataContext pero eso podría matar cualquier cambio que hayas hecho. No está diseñado para proporcionar sincronización en tiempo real. La sincronización y la edición en tiempo real no van bien juntas. –

1

LINQ to SQL utiliza el patrón de diseño de mapa de identidad, lo que significa que siempre devolverá la misma instancia de un objeto para su clave principal dada (a menos que desactive el seguimiento de objetos).

La solución es simplemente utilizar un segundo contexto de datos si no desea que interfiera con la primera instancia o actualice la primera instancia si lo hace.

5

LinqToSql tiene una amplia variedad de herramientas para tratar problemas de simultaneidad.

¡El primer paso, sin embargo, es admitir que hay un problema de concurrencia que resolver!

En primer lugar, se supone que el ciclo de vida de los objetos de DataContext coincide con un UnitOfWork. Si te estás aferrando a uno por períodos prolongados, vas a tener que trabajar mucho más porque la clase no está diseñada para usarse de esa manera.

En segundo lugar, DataContext rastrea dos copias de cada objeto. Uno es el estado original y el otro es el estado cambiado/cambiante. Si solicita MyClass con Id = 1, le devolverá la misma instancia que le dio la última vez, que es la versión modificada/modificable ... no la original.Debe hacer esto para evitar problemas de concurrencia con instancias de memoria ... LinqToSql no permite que un DataContext tenga conocimiento de dos versiones cambiables de MyClass (Id = 1).

En tercer lugar, DataContext no tiene idea si su cambio en la memoria ocurre antes o después del cambio de la base de datos, y por lo tanto no puede arbitrar el conflicto de simultaneidad sin alguna guía. Todo lo que ve es:

  • He leído MyClass (Id = 1) de la base de datos.
  • Programador modificado MyClass (Id = 1).
  • envié MiClase (Id = 1) de vuelta a la base de datos (un vistazo a este SQL para ver la concurrencia optimista en la cláusula where)
    • La actualización tendrá éxito si la versión de la base de datos coincide con el original (concurrencia optimista).
    • La actualización fallará con la excepción de concurrencia si la versión de la base de datos no coincide con el original.

Ok, ahora que el problema se plantea, aquí hay un par de maneras de tratar con él.

Puede tirar el DataContext y empezar de nuevo. Esto es un poco difícil para algunos, pero al menos es fácil de implementar.

Puede solicitar que la instancia original o la instancia modificada/modificable se actualicen con el valor de la base de datos llamando al DataContext.Refresh(RefreshMode, target) (reference docs with many good concurrency links in the "Remarks" section). Esto traerá los cambios del lado del cliente y permitirá que su código funcione, cuál será el resultado final.

Puede desactivar la comprobación de concurrencia en dbml (ColumnAttribute.UpdateCheck). Esto inhabilita la concurrencia optimista y su código pisará fuerte sobre los cambios de cualquier otra persona. También de mano dura, también es fácil de implementar.

Cuestiones relacionadas