5

En nuestra aplicación comercial de niveles múltiples tenemos ObservableCollections de Entidades de seguimiento automático que se devuelven de las llamadas de servicio.¿Cómo hacer un seguimiento de los objetos eliminados de un ObservableCollection en escenarios CRUD?

La idea es que queremos poder obtener entidades, agregarlas, actualizarlas y eliminarlas del lado del cliente de recopilación, y luego enviar estos cambios al lado del servidor, donde se conservarán en la base de datos.

Entidades de seguimiento propio, como su nombre podría sugerir, realizar un seguimiento de su estado. Cuando se crea un STE nuevo, tiene el estado Agregado, cuando modifica una propiedad, establece el estado Modificado, también puede tener el estado Eliminado, pero este estado no se establece cuando la entidad se elimina de un ObservableCollection (obviamente). Si desea este comportamiento, debe codificarlo usted mismo.

En mi implementación actual, cuando una entidad se elimina de la ObservableCollection, lo guardo en una colección de sombra, por lo que cuando el ObservableCollection se envía de vuelta al servidor, puedo enviar los elementos eliminados a lo largo de, por lo Entity Framework sabe para borrarlos

Algo a lo largo de las líneas de:

protected IDictionary<int, IList> DeletedCollections = new Dictionary<int, IList>(); 

protected void SubscribeDeletionHandler<TEntity>(ObservableCollection<TEntity> collection) 
{ 
    var deletedEntities = new List<TEntity>(); 
    DeletedCollections[collection.GetHashCode()] = deletedEntities; 

    collection.CollectionChanged += (o, a) => 
     { 
      if (a.OldItems != null) 
      { 
       deletedEntities.AddRange(a.OldItems.Cast<TEntity>()); 
      } 
     }; 
} 

Ahora bien, si el usuario decide guardar sus cambios en el servidor, que puede obtener la lista de elementos eliminados, y enviarlos junto:

ObservableCollection<Customer> customers = MyServiceProxy.GetCustomers(); 

customers.RemoveAt(0); 

MyServiceProxy.UpdateCustomers(customers); 

En este punto, el método UpdateCustomers verificará mi colección de instantáneas si se eliminaron algunos elementos y los enviará al lado del servidor.

Este enfoque funciona bien, hasta que comienzas a pensar en el ciclo de vida de estas colecciones de sombras. Básicamente, cuando el ObservableCollection es basura recolectada, no hay forma de saber que tenemos que eliminar la colección de sombras de nuestro diccionario.

Se me ocurrió una solución complicada que básicamente hace la gestión manual de la memoria en este caso. Guardo un WeakReference en el ObservableCollection y cada pocos segundos compruebo si la referencia está inactiva, en cuyo caso elimino la colección de sombras.

Pero esto parece una solución terrible ... Espero que el genio colectivo de StackOverflow pueda arrojar luz sobre una mejor solución.

EDIT:

Al final me decidí a ir con la subclasificación de la ObservableCollection. Se genera el código proxy del servicio, por lo que era una tarea relativamente simple cambiarlo para devolver mi tipo derivado.

¡Gracias por toda la ayuda!

+0

¿Por qué no mantener referencias débiles a las entidades en sí, en lugar de la colección? Entonces no necesitas jugar con la colección ya que las entidades son eliminadas; solo asegúrese de que las referencias estén en vivo cuando itere. –

+0

Mantener referencias débiles a las entidades, en lugar de agregarlas a una colección paralela, efectivamente permitiría que sean basura, y quiero poder reutilizarlas en el punto donde necesito actualizar su estado a la base de datos. Solo tiene sentido dejar que sean basura, cuando el ObservableCollection de donde vinieron está fuera del alcance. –

+0

Podría subclasificar ObservableCollection y agregar IDispose que notifique su código. Sin embargo, sería una carga para el código de su cliente llamar a IDispose. –

Respuesta

1

En lugar de rodar su propia lógica de "referencia débil + encuesta ¿Está muerto, está vivo?", Podría usar el HttpRuntime.Cache (disponible para todos los tipos de proyectos, no solo para proyectos web).

Agregue cada colección de instantáneas a la memoria caché, ya sea con un tiempo de espera generoso, o un delegado que puede verificar si la colección original aún está activa (o ambas).

No es terriblemente diferente a su propia solución, pero sí utiliza componentes .Net probados y confiables.

Aparte de eso, usted está buscando a extender ObservableCollection y el uso de esa nueva clase en lugar (que me imagino que no es pequeño cambio), o el cambio/envolver UpdateCustomers método para eliminar el formulario de recogida de sombra DeletedCollections

Lo siento, no puedo pensar en nada más, pero espero que esto ayude.
BW

+0

Gracias por la respuesta. Su respuesta me está haciendo reconsiderar enfoques que había rechazado previamente, como la extensión de ObservableCollection. Necesitará investigar las implicaciones de extenderlo ... –

+0

Al final decidí subclasificar el 'ObservableCollection'. Las clases proxy de servicio se generan de todos modos, por lo que la cantidad de cambios se redujo a un mínimo. ¡Gracias por la ayuda! –

+0

Excelente, me alegro de ser de ayuda, gracias :) –

1

Si va a reemplazar ObservableCollection es una posibilidad (por ejemplo, si está utilizando una fábrica común para todos los casos colecciones), entonces usted podría subclase ObservableCollection y añadir un método Finalize que limpia los elementos eliminados que pertenece a esta colección.

Otra alternativa es cambiar la forma en que calcula qué elementos se eliminan. Puede mantener la colección original y darle al cliente una copia superficial. Cuando la colección vuelve, puede comparar los dos para ver qué elementos ya no están presentes. Si las colecciones están ordenadas, entonces la comparación se puede hacer en tiempo lineal en el tamaño de la colección. Si no están ordenados, los valores de colección modificados se pueden poner en una tabla hash y eso solía buscar cada valor en la colección original. Si las entidades tienen una identificación natural, entonces usarla como la clave es una manera segura de determinar qué elementos no están presentes en la colección devuelta, es decir, se han eliminado. Esto también se ejecuta en tiempo lineal.

De lo contrario, su solución original no suena tan mal. En Java, una WeakReference puede registrar una devolución de llamada que se llama cuando se borra la referencia. No existe una característica similar en .NET, pero el uso de encuestas es una aproximación cercana. No creo que este enfoque sea tan malo, y si funciona, ¿por qué cambiarlo?

Como nota aparte, ¿no le preocupa que GetHashCode() devuelva el mismo valor para distintas colecciones? Usar una referencia débil a la colección podría ser más apropiado como clave, entonces no hay posibilidad de una colisión.

+0

Al crear subclases, no tendría que molestarme con Finalizar. Nunca se hace referencia a la colección de sombras desde otro lugar, y su alcance está vinculado al 'Observablecollection' derivado. Mantener el estado en el servidor no es una posibilidad, y este enfoque no me permitiría solo enviar de vuelta las entidades eliminadas para reducir la carga útil de transferencia, por ejemplo. Sin embargo, la parte sobre 'GetHashCode()' que colisiona es algo que necesitaré abordar si decido no subclasificar 'ObservableCollection'. Otra razón más para considerar lo último :) ¡Gracias por la respuesta! –

+0

Tiene razón acerca de no necesitar finalizar si subclases: puede hacer que los miembros eliminados incluyan un miembro de la colección. Como ha notado, esto soluciona el problema de hashCode, ya que eso ya no es necesario. Pero su solución actual, sondear referencias débiles en un hilo de fondo, ¿es tan mala? – mdma

1

Creo que estás en un buen camino, consideraría refactorizar en esta situación. Mi experiencia es que en el 99% de los casos, el recolector de basura hace que la administración de memoria sea impresionante, casi no se necesita trabajo real.

pero en el 1% de los casos se necesita que alguien se dé cuenta de que tienen que subir la apuesta e ir a la "vieja escuela" reafirmando su administración de memoria/caché en esas áreas. felicitaciones por darte cuenta de que estás en esa situación y por tratar de evitar los trucos IDispose/WeakReference. Creo que realmente ayudarás al siguiente tipo que trabaja en tu código.

En cuanto a conseguir una solución, creo que tienes un buen agarre sobre la situación

-be claro cuando los objetos se deben crear -be claro cuando los objetos tienen que ser destruidos -be claro cuando tus objetos necesitan ser empujados al servidor

¡buena suerte! díganos cómo va :)

+0

Gracias, al final terminé tirando el código sin embargo. La creación de subclases tiene más sentido ya que vincula el alcance de la colección de sombras al 'ObservableCollection'. –

Cuestiones relacionadas