martes 23 de septiembre de 2008

Tutorial Disconnected Service Agent

Esta documentación trata sobre cómo crear, implementar y usar DSA. Para llevar a cabo esta implementación se necesitan los siguientes componentes:

- SCSF May 2007

- Microsoft SQL Server Compact Edition 3.5

- Visual Studio 2005 con C#

Al momento de crear una aplicación con Composite Aplication Block lo que obtenemos en el Solution Explorer de Visual Studio lo podemos apreciar en la siguiente imagen.

clip_image002[4]

El Solution Explorer contiene los siguientes directorios:

- Infrastructure: Es te directorio contiene el stuff de Smart Client Software Factory.

- Business Modules: Acá se encuentra la GUI que consume los web services con WCF.

- WCFService: Contiene la definición del web services. Esto es opcional si es que lo quieren implementar en la misma solución que los demás directorios, también se puede crear en una solución totalmente aparte.

Bien, ahora los pasos a seguir para llevar a cabo la implementación de DSA son los siguientes:

Agregar Librerías de SQL

DSA necesita una base de datos para poder guardar los request queue, para ello tenemos que agregar unas librerías en nuestra solución, que las podemos encontrar en el siguiente directorio C:\Program Files\Microsoft SQL Server Compact Edition\v3.5.

Las librerías a agregar son:

- sqlceca35.dll

- sqlcecomact35.dll

- sqlceer35EN.dll

- sqlceme35.dll

- sqlceoledb35.dll

- sqlceqp35.dll

- sqlcese35.dll

- System.Data.SqlServerCe.Entity.dll

Nos quedaría el Shell como se muestra en la siguiente imagen:

clip_image002[6]

Agregar Base de Datos

Necesitamos de una base de datos para poder guardar nuestros request del web services. Para ello agregamos nuestra base de datos de tipo SQL Compact Edition (.sdf) al proyecto “Infrastructure.Module”.

Crear el DSA

La forma de crear el DSA es bastante simple gracias a que podemos usar el SCSF recipe para hacerlo. La forma de hacerlo es la siguiente.

Hacemos click con el botón derecho en la referencia del web services, que puede estar ubicada dentro de nuestro Business Module o también dentro de nuestro Shell, todo eso va depender donde tengamos ubicadas nuestras referencias a los web services. Luego seleccionamos “Smart Client Factory” y después “Create Disconnected Service Agent”, y aparecerá la siguiente ventana:

clip_image002[14]

Hacemos click en el botón “…” para elegir el tipo de proxy, el proxy fue generado cuando agregamos las referencias de nuestros web services, y elegimos la clase que contiene el “Client” del proxy.

clip_image002[10]

Luego de hacer esta tarea nos aparecerá el tipo de métodos que contiene el web services, a través del Client y también el Preview del Solution Explorer.

clip_image002[12]

Apretamos el botón Finish y podremos ver que se ha creado un nuevo directorio que contiene la carpeta DSA.

clip_image002[16]

Y así hemos creado nuestro Service Disconnected Agent.

Modificar el App.config

Ahora abrimos nuestro archivo de configuración de la aplicación que está en el Shell. Y configuramos las siguientes líneas de nuestro XML.

Agregamos en la sección de configuración lo siguiente:

<configSections>

<section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings,

Microsoft.Practices.EnterpriseLibrary.Logging, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

<section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings,

Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />

<section name="ConnectionMonitor" type="Microsoft.Practices.SmartClient.ConnectionMonitor.Configuration.ConnectionSettingsSection,

Microsoft.Practices.SmartClient.ConnectionMonitor" />

<section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings,

Microsoft.Practices.EnterpriseLibrary.Data" />

</configSections>

Luego modificamos o agregamos la siguiente modificación:

<dataConfiguration defaultDatabase="QueueDatabase">

<providerMappings>

<add databaseType="Microsoft.Practices.SmartClient.EnterpriseLibrary.SmartClientDatabase, Microsoft.Practices.SmartClient.EnterpriseLibrary"

name="System.Data.SqlServerCe" />

</providerMappings>

</dataConfiguration>

Configuramos nuestro Connection Monitor:

<ConnectionMonitor>

<Networks>

<add Name="Internet" Address="http://www.carlodiban.com" />

</Networks>

</ConnectionMonitor>

Esto puede ser a elección de cada uno según los requerimientos que se necesiten.

Y por último configuramos nuestro Connection String:

<connectionStrings>

<remove name="LocalSqlServer" />

<add name="QueueDatabase" connectionString="DataSource=Requests.sdf" providerName="System.Data.SqlServerCe" />

</connectionStrings>

En DataSource tiene que ir el nombre de la base de datos que se creó. En mi caso es Requests.sdf

Request Manager y ConnectionMonitor

Nosotros para poder inicializar nuestro RequestManager lo haremos desde el proyecto “Insfractructure.Module” y abrimos el archivo “ModuleController.cs”. Luego agregamos los using correspondientes que son:

using microsoft.Practices.SmartClient.DisconnectedAgent;

using Microsoft.Practices.SmartClient.EnterpriseLibrary;

Después de hacer esta operación creamos una variable privada de tipo ConnectionMonitor y le decimos que cree su valor desde la configuración esto se hace de la siguiente manera:

private readonly ConnectionMonitor _connectionMonitor = ConnectionMonitorFactory.CreateFromConfiguration();

Después creamos una variable privada de tipo RequestManager.

private RequestManager _requestManager;

Y por ultimo modificamos el método AddServices() de la siguiente manera:

private void AddServices()

{

//TODO: add services provided by the Module. See: Add or AddNew method in WorkItem.Services collection or

// See: ms-help://MS.VSCC.v80/MS.VSIPCC.v80/ms.practices.2005Nov.cab/CAB/html/03-020-Adding%20Services.htm

_requestManager = DatabaseRequestManagerIntializer.Initialize();

_connectionMonitor.Connections.ConnectionStatusChanged += new EventHandler<ConnectionEventArgs>(Connections_ConnectionStatusChanged);

WorkItem.RootWorkItem.Services.Add(_requestManager.RequestQueue);

WorkItem.RootWorkItem.Services.AddNew<Agent>();

}

void Connections_ConnectionStatusChanged(object sender, ConnectionEventArgs e)

{

if (_connectionMonitor.IsConnected)

_requestManager.StartAutomaticDispatch();

}

Con esto inicializamos el Request Manager, luego si la conexión sufre algún cambio de estado se notificara y si estamos conectados se ejecutara el método StartAutomaticDispatch(), para enviar todos nuestros Request pendientes. Y si no hay conexión los Request seguirán almacenados en nuestra base de datos.

Y al final del método agregamos el servicio del Agent a nuestro rootWorkItem.

Consumir el DSA

Para consumir nuestro DSA lo que hay que hacer es asignarle a una variable de tipo Agent que cree una nueva instancia de este y le pasamos un Request Queue para que se cree en la clase Agent. Luego creamos una variable de tipo OfflineBehavior y le asignamos como valor el comportamiento que debe tener nuestro offlineBehavior según sea nuestro método del web services y por último llamamos al método que corresponde desde la clase Agent y le pasamos los parámetros, si es que el método requiere y nuestro offlineBehavior.

public IdCodeNameList GetAllCategorias()

{

IdCodeNameList result = null;

if (_connectionMonitor.IsConnected)

{

result = _coreDataClient.GetAllCategorias();

}

else

{

_agent = new Agent(RequestManager.Instance.RequestQueue);

OfflineBehavior offlineBehavior = Agent.GetGetAllCategoriasDefaultBehavior();

_agent.GetAllCategorias(offlineBehavior);

}

return result;

}

En el código muestro un ejemplo de lo recién redactado. Lo que estoy haciendo en el código, es preguntar si es que tengo conexión, llamo al servicio y todo funcionaria normal. Si no tengo conexión encolo mi requerimiento para el servicio, y así cuando se recupere la conexión sea enviado.

Clases del DSA

Nuestro directorio DSA contiene dos clases como pudieron ver una es la clase Agent.cs y la otra es la clase Callback.cs.

En la clase Agent los cambio que hay que hacer son casi nulos gracias al SCSF récipe, ya que al momento de configurarlo se generan todas las configuraciones de esta clase, si posteriormente queremos hacer algún cambio, por ejemplo el cambio del EndPoint lo debemos hacer en la clase Agent.

En la clase Callback, pueden haber dos sucesos el primero es que si obtenemos el resultado de nuestro método del servicio, podemos realizar una acción en el método de la clase Callback.cs On”NombreDelMetododelServicio”Return(Request request, object[] parameters, object returnValues)

Y en el segundo caso podemos recibir un error que haya ocurrido, por ende podemos manejarlo en el método On”NombreDelMetodoDelServicio”Exception(Request request, Exception ex).

Y ahí podemos lanzar un mensaje de erro o lo que casa usuario estime conveniente.

Un ejemplo de la clase CallBack.cs es el siguiente:

public class Callback : CallbackBase

{

public static event EventHandler<EventArgs<IdCodeNameList>> GetAllCategoriasReturn;

#region GetAllCategorias

public override void OnGetAllCategoriasReturn(Request request, object[] parameters, IdCodeNameList returnValue)

{

GetAllCategoriasReturn(this, new EventArgs<IdCodeNameList>(returnValue));

}

public override OnExceptionAction OnGetAllCategoriasException(Request request, Exception ex)

{

throw new NotImplementedException("Not implemented", ex);

}

#endregion GetAllCategorias

}

3 comentarios:

gaby dijo...

hola,muy bueno el blog,si queres,ingresa a mi web,a publicar un comentario. saludos
base de datos
http://tvinternet08.blogspot.com/

Javier Caballero Alias(Cuaco) dijo...

Hola, muy bueno el ejemplo.
Solo que he batallado al implementar el tipo IdCodeNameList en mi codigo. Yo lo puse en la capa de presentación y me presenta el siguiente error:
Error 4 The type or namespace name 'IdCodeNameList' could not be found.
Are you missing a using directive or an assembly reference?

Podrías ayudarme?

Carlo Diban B. dijo...

Javier, lo que pasa es que el tipo IdCodeName es una tipo de clase ejemplo que use para desarrolar el tutorial, tu en vez de ese tipo tienes que generar una clase hecha po ti, con todas sus porpiedades.

Saludos.