.net - Datacontext Lifetime en el escenario de enlace de WinForm
winforms linq-to-sql (5)
Actualmente estoy intentando implementar un escenario idéntico en nuestra aplicación WinForms SmartClient.
¿Has llegado a una buena solución con esto?
En nuestra aplicación, tenemos el mismo requisito de IU concurrente y la necesidad de actualizar los datos de una fuente compartida, sin embargo, estamos utilizando los servicios de WCF en lugar de LinqToSql. Implementé nuestro propio mapa de identidad y solución de seguimiento de cambios.
Obviamente, es importante que los cambios realizados a su cliente en el inspector se reflejen en las otras vistas. Pero este tipo de da la impresión al usuario de que sus cambios se han guardado.
Trazaría una línea alrededor de la unidad de trabajo para comenzar cuando lea la lista de clientes y finalizará siempre que se hayan guardado los cambios del usuario. Si vuelves a leer la lista de clientes y no hay cambios, esa es una nueva unidad de trabajo. Si hay cambios, deberá cerrar y guardar los cambios realizados por el usuario antes de poder comenzar una nueva unidad de trabajo.
Nuestro problema es que tenemos muchas ventanas abiertas a la vez, y es fácil para un usuario hacer algunas ediciones y no comprometerlas con el servidor. Entonces no es obvio por qué no pueden volver a cargar la lista maestra.
Decidimos en este caso que deberíamos guardar automáticamente los cambios del usuario.
¿Has encontrado mejores soluciones?
Este me ha estado golpeando por un tiempo. Pero no soy un experto Esto es un poco largo ...
Tengo una aplicación WinForms con una interfaz de usuario de estilo de Outlook. Es decir que hay una barra en el panel izquierdo que le permite seleccionar una "pantalla" que es un control de WinForms, por ejemplo, la pantalla del cliente, y en el panel de la derecha aparecerá una lista de clientes (es decir, el cliente). controlar). Yo llamo a esto la interfaz del explorador. Al hacer doble clic en un registro aparecerá un registro de cliente no modal en una ventana adicional de la misma manera que abriría un correo electrónico en Outlook, lo llamamos el inspector. Si haces doble clic en múltiples registros, obtienes múltiples inspectores.
Todo esto se hace usando databinding. Hay un control BindingSource en el control de lista de clientes y hay otro en el inspector de clientes. El cliente controla las noticias de un DataContext estático en su evento de carga y asigna el resultado de una consulta simple de Linq-To a la propiedad del origen de datos BindingControl. Cuando se hace doble clic en la lista de clientes, el evento busca el registro, lo envía a un objeto de cliente de Linq-To-SQL y lo suministra al constructor del formulario de inspector de clientes. El inspector de clientes obtiene el objeto del cliente y le asigna la propiedad del origen de datos de su control BindingSource.
Como el control BindingSource admite IBindingList, el contenido del objeto del cliente no se modifica hasta que se invoca EndEdit, en esta aplicación cuando se hace clic en el botón Aceptar. Como Linq to SQL implementa la interfaz INotifyPropertyChanged, la lista de clientes se actualiza. Guay.
Sin embargo, el problema surge cuando quiero actualizar el contenido de la lista de clientes para recoger los cambios realizados por otros usuarios, lo que quiero que ocurra a intervalos regulares, cada 60 segundos. Si tengo un temporizador y vuelvo a ejecutar la consulta en el mismo contexto de datos, no se recogen los cambios, porque Linq to SQL no quiere aplastar los cambios que se han realizado en los datos bajo el control del contexto de datos. Extrañamente, ejecuta la consulta en la base de datos, pero el seguimiento de la identidad significa que solo se agregan a la lista los nuevos objetos del cliente devueltos por la base de datos. Si vuelvo a crear otro contexto de datos, los inspectores de clientes abiertos ya no utilizan el mismo contexto de datos, por lo que los cambios futuros en el inspector de clientes abierto no se reflejan en la lista de clientes.
Básicamente, los datos son obsoletos porque no estoy usando el patrón de unidad de trabajo. Entonces (si todavía está conmigo), las preguntas son.
1. ¿Cómo demonios puedo hacer que el patrón de unidad de trabajo funcione en esta situación? Es bastante fácil que ASP.NET use un contexto de datos de ámbito de solicitud, es de corta duración, ¿pero en WinForms?
2. ¿Hay algún otro ORM que funcione mejor en este escenario? NHibernate, EF, LLBLGEN, etc.
3. ¿De qué otra manera debería hacerlo?
Y también.
4. Si puedo hacer que Linq to SQL funcione así, alguien ha implementado IBindingList en las clases parciales de Linq a SQL, lo que me evitaría tener que usar los controles de IBindingSource. (No estoy seguro de que me importe esto).
5. Si puedo hacer que Linq to SQL funcione de esta manera, hay alguna forma de usar las notificaciones SQL en SQL 2008 para que me notifiquen cuando los resultados de la consulta subyacente cambian y se vuelven a consultar, en lugar de sondear.
¡Gracias!
PD: soy consciente de que puedo usar
db.Refresh(System.Data.Linq.RefreshMode.KeepChanges, customers)
pero esto hace que se ejecute una consulta en la base de datos para el registro de cada cliente en la lista.
Tal vez un poco viejo ... Pero con respecto a los puntos 4/5 asegúrese de revisar el proyecto Bindable LINQ en CodePlex. Definitivamente hay un buen código ahí que resuelve exactamente tu problema.
@Andronicus
Después de ver decenas de ORMas, actualmente estoy buscando un ORM comercial llamado Genome ( http://www.genom-e.com/ ).
Parece que me permite hacer más de lo anterior, como un enlace de datos más avanzado, y se puede evitar que los registros se vuelvan obsoletos, pero aún así sigo encontrando mi camino. Te dejaré saber cómo me llevo, pero podría ser algún día.
@Perro rojo
Miré BinbleLinq y realmente me gusta. Pero es Linq to Objects y, por lo tanto, no tiene traducciones de Linq to SQL. (A menos que esté equivocado).
¡Muchas gracias!
He intentado algo similar, aquí están mis dos centavos.
No creo que pueda implementar un patrón de unidad de trabajo aquí debido a la forma en que funciona su UI. Como ya sabrá, LinqToSql DataContext está diseñado para ser un objeto liviano y de corta duración. Se une a una "unidad de trabajo" naturalmente. En su caso, la (s) modificación (es) de los cambios a DB es una unidad, los cambios refrescantes de DB son otra unidad. Pero quiere una instancia de DataContext para hacer ambas cosas.
Además, tengo curiosidad sobre lo que se supone que debe hacer su UI cuando un usuario está editando un registro, mientras que otro usuario simplemente realiza algunos cambios en el mismo registro a DB. ¿Cómo se manejan estos conflictos de concurrencia desde el punto de vista de la interfaz de usuario?
Es posible que deba hacer algún compromiso con la UI. Una forma de hacerlo es hacer que la vista de detalles del cliente tenga dos modos "mostrando" y "editando". La visualización es simplemente una vista de solo lectura con un temporizador refrescante en algún intervalo. La edición es una instantánea que permite a los usuarios modificar los datos pero no tienen idea de las actualizaciones de otras personas. Al final, cuando los usuarios envían las actualizaciones, permita que la concurrencia optimista controle el conflicto. Los usuarios no pueden ver los cambios en tiempo real cuando están editando.
Tu punto # 5 es interesante. Lo que hicimos fue tener algo que no hiciera nada más que consultar a DB para recopilar las últimas actualizaciones ya sea en función de un intervalo o de algunas señales. Lo llamamos "servicio editorial". Para que esto funcione, deberá tener una columna de marca de tiempo en su tabla DB.
Con el "servicio de publicación" en su lugar, puede obtener el conjunto delta (actualizaciones y nuevos registros) sin utilizar el DataContext en el control del cliente. Si "fusiona" el delta establecido en su DataBinding DataSource local, la vista de detalles del cliente debería actualizarse. Ahora, la instancia de DataContext en el control del cliente está dedicada a la actualización. Puedes elegir dejar que el usuario decida cuándo comprometerse. O puede hacer que se comprometa siempre que el foco del usuario abandone la fila (durante la validación). Personalmente, haría lo último porque no tengo ganas de tener un DataContext en vivo durante un tiempo imprevisto.
Voy a replantear tu problema para asegurarme de que lo he entendido.
Usted tiene un widget que presenta una lista de entidades (la LISTA). Cuando hace clic en un elemento en la LISTA, aparece otro widget que permite al usuario editar la entidad. Cuando el usuario ha terminado de editar la entidad, sus cambios están comprometidos con la base de datos, y también deben reflejarse en la lista de entidades. Periódicamente, el sistema también debe obtener los cambios que otros usuarios han hecho a los elementos en la LISTA y actualizar la LISTA.
Si esto es correcto, voy a dejar de lado los problemas de simultaneidad de dos usuarios que editan la misma entidad ya que esto no parece ser tu problema, y me centraré en cómo organizar la interfaz de usuario y la unidad de trabajo.
Debe separar las entidades en su LISTA de sus entidades editadas por sus inspectores. El proceso comercial representado por sus inspectores son sus unidades de trabajo, una unidad para cada entidad. Su lista no representa una unidad de trabajo. Es una representación obsoleta, o punto en el tiempo, del trabajo combinado de todas las unidades de trabajo previamente comprometidas. Su LISTA ni siquiera tiene que tratar directamente con sus entidades, puede tener una identificación o cualquier otra forma para que sus inspectores lleguen a la entidad subyacente desde el DB cuando un usuario hace clic en ella. Ahora podrá actualizar la lista cuando lo desee, ya que sus inspectores no comparten instancias con ella.
Para simular a su usuario que cuando están editando una entidad a través de un inspector y para hacer que parezcan estar vinculados a la misma cosa, tiene dos opciones.
1) la lista solo está vinculada a los datos confirmados en el DB. Esto es fácil, cuando un inspector vacía los cambios locales de regreso a la base de datos y se compromete exitosamente, proporciona una manera para que el inspector le diga a la lista que se actualice a sí mismo.
2) list está vinculada a los datos comprometidos + datos locales no comprometidos. Esto es un poco más difícil, necesita exponer los métodos en su lista que permiten a un inspector superar los datos que regresan del Db, y sobrescribirlo con sus propios datos sucios locales.