2011-02-04 17 views
5

Estoy trabajando en una gran aplicación de Silverlight que usa Net.TCP dúplex para hablar con un back-end de WCF. Estoy en el proceso de mover esta aplicación desde un enfoque MVC a MVVM. Sin embargo, estoy luchando con la forma correcta de implementar mis ViewModels. Estamos utilizando los proxies generados por WCF para nuestro modelo, que es bastante complejo, involucra docenas de clases, muchas colecciones y una variedad de relaciones muchos a muchos. Por ejemplo, un Usuario puede pertenecer a muchas Salas, una Sala puede tener muchos Usuarios, un Usuario puede tener muchos Archivos Compartidos, y cada Archivo Compartido puede compartirse con cualquier Sala de la que el Usuario sea parte actualmente. Esa clase de cosas.Práctica recomendada para mantener los modelos y los modelos de vista sincronizados

Además de eso, como estamos utilizando WCF en modo dúplex, los cambios en el modelo pueden ser activados por el usuario final o por el servicio WCF en el back-end. En otras palabras, el modelo que estamos usando es de varios órdenes de magnitud más complicado que el típico "Modelo" que ve representado en cualquiera de los varios libros/artículos/publicaciones de blog de MVVM. Y ahí es donde entra el problema, porque mantener nuestra capa de ViewModel sincronizada con la capa subyacente del Modelo se está volviendo un poco complicada.

Aquí hay un problema típico. Un nuevo "Usuario" se une a una "Habitación", por lo que los servicios WCF disparan una notificación "SessionAdded" a todos los demás usuarios de la sala. La notificación de SessionAdded lleva consigo un objeto de sesión que tiene una habitación vinculada y un objeto de usuario vinculado. Este objeto Room que se deserializa del servicio WCF es básicamente el mismo que el objeto Room en el cliente local, y probablemente tiene la mayoría de los mismos datos, pero ciertamente no tiene todos los mismos datos, y al menos algunos de los datos (como su colección nula Whiteboards) es ciertamente erróneo. Entonces, de alguna manera, debemos tomar estos datos entrantes y fusionarlos en nuestro modelo existente. Y luego tenemos que crear ViewModels en la parte superior de cada uno de los nuevos objetos, y/o actualizar ViewModels existentes con los nuevos objetos y/o sus datos.

En este momento estamos manejando esto haciendo que los diversos modelos de vista respondan a los eventos de notificación WCF relevantes, y hacemos todo lo posible para arreglar sus modelos subyacentes y modelos de vista relacionados. Hemos descubierto algunos trucos, como SynchronizedObservableCollection (vagamente parecido al here) que mira (digamos) Room.Sessions ObservableCollection y crea automáticamente SessionViewModels correspondientes y los coloca en la colección RoomViewModel.SessionViewModels. También estamos usando ViewModelFactory, que almacena en caché los modelos de vista y asegura que, por ejemplo, el SessionViewModel que envuelve una sesión determinada se mantenga igual, incluso si el objeto de sesión subyacente se cambia. (Si es importante, estamos usando un enfoque de primer modelo de vista, ya que una gran parte de lo que necesitamos es crear nuevos elementos de IU en respuesta a los cambios en el modelo de vista activados por nuestras notificaciones de WCF).

Y todo esto funciona Básicamente. La mayor parte del tiempo Ya sabes. Pero es mucho código que mantener, y es fácil equivocarse. Las pruebas unitarias son útiles siempre que pueda recordar lo que se supone que sucederá, pero para el momento en que termine de procesar su 20º evento en cascada CollectionChanged, es difícil hacer un seguimiento de cómo todo esto encaja y lo que estaba probando en primer lugar . En otras palabras, todo es malditamente frágil.

Me parece que este es el tipo de escenario con el que mucha gente debe haberse topado, y me llama la atención cómo otras personas lo han enfrentado. Puedo pensar en un par de enfoques para quizás hacerlo mejor:

(1) Tratar el Modelo del lado del cliente como un tipo de base de datos que debe mantenerse completamente coherente, e implementar una capa de acceso a datos del lado del cliente cuyo trabajo es para mantener el modelo consistente. Todas las actualizaciones del modelo, ya sea del usuario o del servidor, deberían pasar por esta capa. Sería un poco como Entity Framework, en que myRoom.Users.Add(myUser) establecería automáticamente myUser.Room = myRoom, y viceversa, y así sucesivamente. (Esta es especialmente la parte que parece que alguien en algún lugar ya debería haber desarrollado, aunque todavía no la he encontrado.)

(2) Apóyese en algo como Truss o Obtics, para mantener todas las piezas sincronizadas. Todavía no estoy seguro de cómo funcionaría eso, pero suena en teoría como debería ser posible.

Y ... ¿qué más? Tengo curiosidad en cuanto a los patrones o marcos que se han utilizado para resolver este problema.

Respuesta

3

Entiendo su dolor - Actualmente estoy desarrollando una aplicación de visualización de datos complejos usando el patrón MVVM. Una pregunta realmente importante que debe hacerse es: "¿El modelo de vista agrega valor en todas partes donde lo usa?", Dicho de otro modo, ¿hay lugares en los que simplemente esté reenviando propiedades de la capa del modelo a su vista?

A menudo encuentro que hay áreas de código, generalmente en el nivel de detalle (por ejemplo, las propiedades de un objeto Person, Age, Name, Forename), donde el modelo de vista no está realmente agregando ningún valor, mientras que en el más nivel de curso, agrega valor estructurando vistas/ventanas, etc. ...

Tiendo a adoptar un enfoque adaptativo de MVVM, en el nivel superior (Windows, paneles, formularios) Siempre tengo un modelo de vista, pero si partes del modelo de vista son tan simples que un modelo de vista no agrega ningún valor, los expongo directamente a la vista. Además, en algunos casos, debe exponer el modelo directamente a la vista para un mejor rendimiento.

Por último, si usted encuentra que usted tiene que volver a introducir un modelo de vista para resolver un problema de unión complicado, que escribió acerca de un patrón simple, mini-MVVM, que se aplica una vista modelo local:

http://www.scottlogic.co.uk/blog/colin/2009/08/the-mini-viewmodel-pattern/

Espero que ayude.

+0

Eso es básicamente lo que estoy haciendo: no soy purista :-). Cada uno de mis ViewModels tiene una propiedad de Modelo que expone el modelo subyacente. Dado que el Modelo en esta instancia es siempre una clase generada por WCF, implementa INotifyPropertyChanged, por lo que funciona razonablemente bien como fuente de enlace. Las únicas propiedades que agrego a mi ViewModel son las que realmente agregan valor de alguna manera. Pero aún tengo que lidiar con el problema del Modelo que necesita actualizarse cuando el servidor envía un mensaje al cliente con un Modelo actualizado. –

Cuestiones relacionadas