El primer prototipo de Proteus
¡Ya está listo y funcionando el primer prototipo de Proteus!
Este prototipo implementa el núcleo de Proteus sobre SQL Server. Permite definir "entidades" complejas en un fichero de configuración ubicado en la capa intermedia,
mediante una aplicación auxiliar (¡ahora están de modas las interfaces inductivas!).
Estas entidades pueden ser utilizadas entonces para recuperar conjuntos de datos y modificar su contenido posteriormente.
En esta versión, las clases y demás tipos se reparten entre dos espacios de nombres:
IntSight.Proteus
IntSight.Proteus.Stargate
El primer espacio de nombres contiene las interfaces abstractas de Proteus; entre ellas, la principal es IDataServer. Dentro del segundo espacio de nombres se implementan las interfaces de Proteus para acceder a SQL Server.
En esta versión, ya están implementados los algoritmos de bloqueo optimista mediante "marcas de tiempo" (timestamps), y el algoritmo más usual utilizado por omisión por Visual Studio. Se ofrece soporte para todos los tipos de datos de SQL Server, incluyendo las columnas de identidad.
Todo esto se logra a velocidad de crucero: los objetos necesarios se configuran al iniciarse la ejecución del servidor, y en ningún momento se sobrecarga el servidor SQL por culpa de consultas sobre el esquema relacional, como sí ocurre con componentes del tipo CommandBuilder.
¿Quiere ver un ejemplo sencillo? Aquí tiene uno:
private void bnTransient_Click(object sender, System.EventArgs e)
{
MergeDataSet(dataServer.SelectFirstPage(
"!products('order details', categories[size: small])",
100, "", "", out transientCookie));
}
private void bnNextTransient_Click(object sender, System.EventArgs e)
{
if (transientCookie != null)
dataSet.Merge(dataServer.SelectNextPage(ref transientCookie));
}
Observe que la cadena que se pasa en el primer parámetro de SelectFirstPage comienza por un signo de admiración. Eso significa que se trata de una entidad definida dinámicamente.
En el ejemplo, con cada llamada recuperamos cien registros de productos, junto con las líneas de detalles asociadas y los registros de categorías de productos. Podríamos también haber usado un filtro adicional para los productos recuperados,
o haber cambiado el orden en que se recuperan los productos (por omisión, de acuerdo a su clave primaria).
Otro detalle: la tabla Categories tiene un atributo size: small. Eso significa que la tabla es lo suficientemente pequeña para leerla en una sola operación. Esto evita añadir carga innecesaria al servidor SQL.
Otros atributos sirven para indicar que una tabla cambia su contenido con muy poca frecuencia, y que es seguro almacenarla en caché en la capa intermedia.
Los atributos permiten también ajustar los algoritmos de bloqueo optimista y de recuperación de valores asignados por el servidor SQL (mediante triggers, por ejemplo).
Es también muy sencillo grabar cambios realizados en la parte cliente:
DataSet result = dataServer.ApplyUpdates(
"pedidos", dataSet.GetChanges(), true);
if (! result.HasErrors) {
dataSet.Merge(result, false);
dataSet.AcceptChanges();
}
else
dataSet.Merge(result, true);
En este caso, he utilizado una entidad predefinida, pedidos, pero el algoritmo es igual para las entidades dinámicas. Primero usamos GetChanges para enviar al servidor sólo los registros con alguna modificación.
Si todo va bien, el servidor devuelve información sobre los cambios realizados por el propio servidor SQL. Por ejemplo, si hemos insertado un pedido, tenemos que saber qué identificador le ha asignado el servidor.
Si por el contrario, han ocurrido errores durante la grabación, el método ApplyUpdates devuelve sólo los registros con problemas, con información específica para cada uno de ellos.
En cuanto a las reglas de empresa, ya está implementado el mecanismo más general posible: se pueden registrar ensamblados especiales con clases que siguen determinadas convenciones, para que intercepten los eventos de grabación de los adaptadores generados,
y algún que otro evento introducido por Proteus (por ejemplo, antes de comenzar la grabación). Estoy estudiando si merece la pena montar algún mecanismo declarativo implementable sobre este modelo básico, pero sólo lo haré si realmente aporta potencia o flexibilidad.
Próximos pasos: vamos a diseñar un modelo de identificación y autorización de usuarios opcional y extensible. Hay que investigar la posibilidad de crear conjuntos de datos con tipos especiales en el lado cliente, mediante extensiones a Visual Studio. Además, hay que adaptar este
módulo básico a .NET Remoting y XML Web Services. Es probable que se reutilice el módulo de caché de XML Web Services cuando se utilice esta técnica de comunicación remota. Y en el momento en que el producto esté maduro, prepararemos demos remotas (¡si nuestros servidores aguantan, claro!).
|