Diseño de la capa modelo - SABIA-Group :: Adaptative...

36
Integración de Sistemas Curso 2008 - 2009 1 Diseño de la capa modelo Integración de Sistemas Integración de Sistemas Parte II. Diseño e implementación de aplicaciones Web con .NET Indice Concetos básicos Método de diseño Pasos a seguir Estudio de un caso práctico: MiniBank Patrón Value Object Patrón Data Access Object

Transcript of Diseño de la capa modelo - SABIA-Group :: Adaptative...

Integración de Sistemas

Curso 2008 - 2009 1

Diseño de la capa modelo

Integración de SistemasIntegración de SistemasParte II. Diseño e implementación de aplicaciones Web con .NET

Indice

Concetos básicos

Método de diseñoPasos a seguir

Estudio de un caso práctico: MiniBank

Patrón Value Object

Patrón Data Access Object

Integración de Sistemas

Curso 2008 - 2009 2

Conceptos básicos

Separación clara de la vista, el modelo y el controlador

Método de diseño

Implementación de la persistencia

Patrones

Value Object (VO)

Data Access Object (DAO)

Implementación

Manual: ADO.NET Generic Factory Model

Automática: ADO.NET Entity Framework (ORM)

Lo veremos apoyándonos en un caso práctico:

MiniBank

Conceptos básicos

La capa modelo debe ser independiente de la interfaz gráfica

Debe ofrecer una API que

Permita invocar de manera sencilla cada caso de uso

Que oculte detalles de implementación

Integración de Sistemas

Curso 2008 - 2009 3

Conceptos básicos

<< Interface >>IService

+UseCase1(…)+UseCase2(…)+UseCase3(…)+UseCase4(…)…

Service

<< Interface >>IEntityDAO

Create(…)Read(…)Update(…)Delete(…)…

<< Uses >>

<< Uses >>

+UseCase1(…)+UseCase2(…)+UseCase3(…)+UseCase4(…)…

<< Interface >>IBarEntityDAO

<< Interface >>IFooEntityDAO

<< Uses >>

<< Uses >>

Método de diseño

Seguiremos el método estudiado en la primera parte de la asignaturaasignatura

Recordatorio:1. Modelar las entidades (clases persistentes)

2. Para cada entidad, crear una abstracción que permita gestionar su persistencia

3. Definir una API sencilla para invocar los casos de uso

4. Implementar los casos de uso

5. Implementar la pruebas de integración de los casos de uso

Lo veremos apoyándonos en un caso práctico: MiniBank

Integración de Sistemas

Curso 2008 - 2009 4

Método de diseño

Estudio de un caso práctico: MiniBank

Implementación en .NET del ejemplo visto en la primera parte de la asignaturaasignatura

Un sencillo ejemplo de una aplicación bancaria con los siguientes casos de uso

Crear una cuenta bancaria

Buscar una cuenta a partir de su identificador

Buscar todas las cuentas de un usuario

Añadir una cantidad de dinero a una cuenta

Retirar una cantidad de dinero de una cuenta

Hacer una transferencia de dinero de una cuenta a otra

Eliminar una cuenta

Buscar todas las operaciones que se han hecho sobre una cuenta entre dos fechas

Datos de una cuenta bancaria

Método de diseño

Estudio de un caso práctico: MiniBank

Identificador de la cuenta (se genera automáticamente)

Identificador del usuario

Balance

Datos de una operaciónIdentificador de la operación (se genera automáticamente)

Identificador de la cuenta

Fecha

Tipo (añadir/retirar)

Cantidad de dinero

Integración de Sistemas

Curso 2008 - 2009 5

Método de diseño

Estudio de un caso práctico: MiniBank

Modelo de excepciones

Método de diseño

1. Modelar las entidades (clases persistentes)

Integración de Sistemas

Curso 2008 - 2009 6

Método de diseño > 1. Modelar las entidades (clases persistentes)

Patrón Value Object (VO)

También conocido como Transfer ObjectRepresenta estado/valor

El estado se almacena en PropertiesIncluyen métodos get y set implícitos

Se suele sobrescribir (override) el método ToString() para imprimir el estado

VOs deben ser objetos serializablesSe indica con el atributo [Serializable] precediendo a la declaración de la clase

Suele utilizarse la notación xxxVO/xxxTO

Método de diseño > 1. Modelar las entidades (clases persistentes)

Patrón Value Object (VO). Ejemplo

[Serializable]public class AccountVO{

private readonly long accountIdentifier;private readonly long userIdentifier;private double balance;

/// <summary>/// Initializes a new instance of the <see cref="AccountVO"/> class./// </summary>/// <param name="accountIdentifier">The account identifier.</param>/// <param name="userIdentifier">The user identifier.</param>/// <param name="balance">The account balance </param>/// <param name= balance >The account balance.</param>public AccountVO(long accountIdentifier, long userIdentifier,

double balance){

this.accountIdentifier = accountIdentifier;this.userIdentifier = userIdentifier;this.balance = balance;

}

Integración de Sistemas

Curso 2008 - 2009 7

Método de diseño > 1. Modelar las entidades (clases persistentes)

Patrón Value Object (VO). Ejemplo

public long AccountIdentifier {{

get { return accountIdentifier; }}public long UserIdentifier{

get { return userIdentifier; }}public double Balance{

get { return balance; }set { balance = value; }

}

public override String ToString(){

String strAccountVO;strAccountVO =

"accountIdentifier = " + accountIdentifier + " | " +"userIdentifier = " + userIdentifier + " | " +"balance = " + balance;

return strAccountVO;}

}

Método de diseño

2. Gestión de la persistencia

Hemos visto una forma de modelar las entidades, pero ¿cómo las leemos guardamos modificamos o borramos de unleemos, guardamos, modificamos o borramos de un almacenamiento persistente (e.g. BD)?

Patrón Data Access Object (DAO)

Integración de Sistemas

Curso 2008 - 2009 8

Método de diseño > 2. Gestión de la persistencia

Patrón Data Access Object (DAO)

Abstracción que permite gestiona la persitencia de una entidad

Oculta cómo se hace el acceso a la fuente de datosOculta cómo se hace el acceso a la fuente de datos

Proporciona operaciones CRUDCreate, Read, Update, Delete

Puede proporcionar operaciones adicionalesE.g.: FindAccountOperationsByDate(…)

Usaremos la notación xxxDAOEl prefijo SQL en el entorno .NET se usa para hacer referencia a SQL Server, no para referirse a SQL estándarestándar

Para evitar confusiones, no utilizaremos el prefijo SQL (siempre trabajaremos con SQL estándar) y utilizaremos SQLServer para referirnos a la BD de Microsoft

Veremos varias formas de implementar este patrón:ADO.NET 2.0

ORM: ADO.NET Entity Framework

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Definimos una interfaz con las operaciones del DAOPodemos tener distintas implementaciones:

Con Generic Factory Model, directamente con ODBC, directament con un Data Provider (e.g. Oracle, MySQL, etc.), trabajando contra ficheros XML, etc.

Proporcionamos una implementación concretaEn el ejemplo utilizaremos Generic Factory Model

El código será independiente del proveedor de acceso a datos

Integración de Sistemas

Curso 2008 - 2009 9

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

<< Interface >>IEntityDAO

Create(…)Read(…)Update(…)Delete(…)…

GFMEntityDAO ODBCEntityDAO Otras posibles implementaciones

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Un caso de uso puede necesitar ejecutar varios métodos de un DAO o de varios (de forma transaccional o no)( )

E.g: Añadir una cantidad de dinero a una cuenta (transaccional)

Leer datos de la cuenta

Actualizar la cuenta

Crear una operación

El cada método de un DAO necesitamosLa conexión, a partir de la cual se creará el comando

command connection CreateCommand()command = connection.CreateCommand()

La transacción, que debe ser asociada a los comandos (se ha explicado en el bloque de diapositivas anterior)

command.Transaction = transaction;

Integración de Sistemas

Curso 2008 - 2009 10

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Consecuencia:C d ét d d l DAO d b ibi á t l ió l t ióCada método del DAO debe recibir como parámetros la conexión y la transacción

public Boolean DAOMethod(DbConnection connection, DbTransaction transaction, ...){

try{

/* Creates the command. */DbCommand command = connection.CreateCommand();/* If transaction exists, command will be added */if (transaction != null){

command.Transaction = transaction;}}

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Interfaz IAccountDAO

Integración de Sistemas

Curso 2008 - 2009 11

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Veamos cómo sería la implementación de un método (1ª versión)

/// <exception cref="InternalErrorException"/>public Boolean Exists(DbConnection connection,

DbTransaction transaction, long accountIdentifier){

DbDataReader dataReader = null;try{

/* Creates the command. */DbCommand command = connection.CreateCommand();/* If transaction exists, command will be added */if (transaction != null){

d i icommand.Transaction = transaction;}command.CommandText =

"SELECT accId FROM Account " +"WHERE accId = @accountIdentifier";

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Veamos cómo sería la implementación de un método (1ª versión) (cont.)

DbParameter accountIdentifierParam = command.CreateParameter();accountIdentifierParam.ParameterName = "@accountIdentifier";accountIdentifierParam.DbType = DbType.Int32;accountIdentifierParam.Value = accountIdentifier;command.Parameters.Add(accountIdentifierParam);command.Prepare();dataReader = command.ExecuteReader();return (dataReader.Read());

}catch (DbException e){

throw new InternalErrorException(e);}finally{

if (dataReader != null){

dataReader.Close();}

}}

Integración de Sistemas

Curso 2008 - 2009 12

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Deseamos que la implementación de IAccountDAO sea independiente del SGBDindependiente del SGBD

Problema:Las clases proporcionadas por el GFM son independientes del Data Provider, pero diferentes Data Providers utilizan distintos dialectos de SQL

En concreto, la forma de definir los parámetros es distinta:

SGDB ParameterSyntax( CommandText)

ParameterName( ParameterName)(.CommandText) (.ParameterName)

SQLServer @parameterName @parameterName

MySQL ?parameterName ?parameterName

Oracle :parameterName :parameterName

Otros ¿? ¿?

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Solución:I l t ódi SQL tá d i d di t d lImplementar IAccountDAO con código SQL estándar e independiente del SGBD en una clase abstracta: AbstractAccountDAO

Delegar en métodos abstractos que se implementarán en clases concretas, dependientes del SGBDprotected abstract String GetParameterSyntax(String parameterName);

protected abstract String GetParameterName(String parameterName);

Integración de Sistemas

Curso 2008 - 2009 13

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0.

2ª versión: independiente del SGDB

/// <exception cref="InternalErrorException"/>public Boolean Exists(DbConnection connection,

DbTransaction transaction, long accountIdentifier){

/* ... */

command.CommandText ="SELECT accId FROM Account " +"WHERE accId = " + GetParameterSyntax("accountIdentifier");

DbParameter accountIdentifierParam = command.CreateParameter();accountIdentifierParam.ParameterName =

GetParameterName("accountIdentifier");accountIdentifierParam.DbType = DbType.Int32;accountIdentifierParam.Value = accountIdentifier;command.Parameters.Add(accountIdentifierParam);

/* ... */

}

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

<< Interface >>IEntityDAO

AbstractEntityDAO

Código independiente del SGBD

Implementa métodos abstractos de AbstractEntityDAO, con código dependiente del SGBD

SQLServerEntityDAO OracleEntityDAO Otras posibles implementaciones

Integración de Sistemas

Curso 2008 - 2009 14

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Implementación de SQLServerAccountDAO

private static readonly String PARAMETER_PREFIX = "@";

protected override String GetParameterSyntax(string parameterName){

return PARAMETER_PREFIX + parameterName;}

protected override String GetParameterName(string parameterName){

return PARAMETER_PREFIX + parameterName;}

En este caso, las implementaciones de GetParameterSyntax y GetParameterName son idénticas, pero no tiene porque ser así

SQLServerAccountDAO podría contener más métodos, dependientes de SQLServer (por necesidad o por optimización)

¿Cómo seleccionamos la implementación concreta que nos interesa (e g OracleEntidadDAO)?

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

(e.g. OracleEntidadDAO)?Mediante el patrón Factory

Implica carga dinámica de clases

Mediante Inyección de Dependencias (lo veremos en el siguiente bloque)

Integración de Sistemas

Curso 2008 - 2009 15

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

IAccountDAO (Adapter)

Define una interfaz para insertar, borrar, actualizar y encontrar AccountVOs

Permite implementar adaptadores para distintas BD

AccountDAOFactory (Factory)

Permite obtener una instancia de un adaptador apropiado para la aplicación

IAccountDAO + AccountDAOFactory

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Es posible hacer plug-n-play (sin recompilar) de adaptadores cuando se cambia de BD

Facilita la instalación/configuración de la aplicación

Integración de Sistemas

Curso 2008 - 2009 16

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

A continuación veremos la implementación del resto de métodos en AbstractSQLAccountDAOAbstractSQLAccountDAO

Find

Update

Remove

Create: requiere Generador de Identificadores

FindByUserIdentifier: patrón Page-By-Page

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

/// <exception cref="InstanceNotFoundException"/>/// <exception cref="InstanceNotFoundException"/>/// <exception cref="InternalErrorException"/>public AccountVO Find(DbConnection connection, DbTransaction transaction,

long accountIdentifier){

DbDataReader dataReader = null;try{

/* << Creates the command >> */

/* << If transaction exists, command will be added >> */command.CommandText =

"SELECT usrId, balance FROM Account " +"WHERE accId = " + GetParameterSyntax("accountIdentifier");

DbParameter accountIdParam = command.CreateParameter();accountIdParam.ParameterName = GetParameterName("accountIdentifier");accountIdParam.DbType = DbType.Int32;accountIdParam.Value = accountIdentifier;command.Parameters.Add(accountIdParam);command.Prepare();

Integración de Sistemas

Curso 2008 - 2009 17

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

/* Execute the Query *//* Execute the Query */dataReader = command.ExecuteReader();if (!dataReader.Read()){

throw new InstanceNotFoundException(accountIdentifier,typeof(AccountVO).FullName);

}Int64 userID = dataReader.GetInt64(0);Double balance = dataReader.GetDouble(1);/* Returns the value object. */return new AccountVO(accountIdentifier, userID, balance);

}catch (DbException e){

throw new InternalErrorException(e);}finally{

if (dataReader != null)dataReader.Close();

}}

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

/// <exception cref "InstanceNotFoundException"/>/// <exception cref="InstanceNotFoundException"/>/// <exception cref="InternalErrorException"/>public void Update(DbConnection connection, DbTransaction transaction,

AccountVO accountVO){

try{

/* << Creates the command >> */

/* << If transaction exists, command will be added >> */

command.CommandText =" UPDATE Account " +" SET usrId = " + GetParameterSyntax("usrId") + ", " +" balance = " + GetParameterSyntax("balance") +" WHERE accId = " + GetParameterSyntax("accId");

DbParameter userIdParam = command.CreateParameter();userIdParam.ParameterName = GetParameterName("usrId");userIdParam.DbType = DbType.Int32;userIdParam.Value = accountVO.UserIdentifier;command.Parameters.Add(userIdParam);

Integración de Sistemas

Curso 2008 - 2009 18

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

DbParameter balanceParam = << ... >>DbP t tIdP << >>DbParameter accountIdParam = << ... >>

command.Prepare();

/* Execute the Query */int updatedRows = command.ExecuteNonQuery();if (updatedRows == 0){

throw new InstanceNotFoundException(accountVO.AccountIdentifier,typeof(AccountVO).FullName);

}if (updatedRows > 1)( p ){

throw new SQLException("Duplicate row for identifier = '" +accountVO.AccountIdentifier + "' in table 'Account'");

}}catch (DbException e){

throw new InternalErrorException(e);}

}

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

/// <exception cref="InstanceNotFoundException"/>/// <exception cref="InstanceNotFoundException"/>/// <exception cref="InternalErrorException"/>public void Remove(DbConnection connection, DbTransaction transaction,

long accountIdentifier){

try{

/* << Creates the command >> */

/* << If transaction exists, command will be added >> */

command.CommandText ="DELETE FROM Account " +"WHERE accId = " + GetParameterSyntax("accId");

DbParameter accountIdParam = << ... >>

command.Prepare();

Integración de Sistemas

Curso 2008 - 2009 19

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

/* Execute query. */int removedRows = command.ExecuteNonQuery();if (removedRows == 0){

throw new InstanceNotFoundException(accountIdentifier,typeof(AccountVO).FullName);

}}catch (DbException e){

th I t lE E ti ( )throw new InternalErrorException(e);}

}

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Implementación de AbstractSQLAccountDAO:CreatefEl identificador de la cuenta debe generarse automáticamente

En general, interesa que la operación create devuelva el identificador generado

Ej.: cuando hay que almacenar una referencia a la fila creada en otra fila de otra tabla (ej.: las operaciones bancarias mantienen una referencia a la cuenta)

Existen distintas estrategias para resolver esto

Columnas Contador (Counter Colum, CC)

Generador de Identificadores (Identifier Generator, IG)

En función de la estrategia utilizada, la implementación del método Create puede variar

Se deja como abstracto en AbstractSQLAccountDAO y se proporciona implementación en la clase que la extienda (dependiente de la estrategia de generación de claves)

Veremos un ejemplo de cómo se haría con CC

Integración de Sistemas

Curso 2008 - 2009 20

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

/// <exception cref "InternalErrorException"/>/// <exception cref="InternalErrorException"/>public override AccountVO Create(DbConnection connection,

DbTransaction transaction, AccountVO accountVO){

DbCommand command;

try{

/* << Creates the command >> */

/* << If transaction exists, command will be added >> */

command.CommandText = "INSERT INTO Account (usrId, balance) " +"values (" + GetParameterSyntax("usrId") + " ," +GetParameterSyntax("balance") + ")";

DbParameter userParameter = << ... >>DbParameter balanceParameter = << ... >>command.Prepare();

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

/* Execute query *// Execute query. /int insertedRows = command.ExecuteNonQuery();if (insertedRows == 0)

throw new SQLException("Can not add row to table 'Account'");if (insertedRows > 1)

throw new SQLException("Duplicate row in table 'Account'");

/* Get account identifier. */IEntityIdentifierRetriever entityIdentifierRetriever =

EntityIdentifierRetrieverFactory.GetRetriever();

Int64 accountIdentifier = entityIdentifierRetriever.GetGeneratedIdentifier(connection, transaction);

/* Return the value object. */return new AccountVO(accountIdentifier,

accountVO.UserIdentifier, accountVO.Balance);}catch (SQLException e){

throw new InternalErrorException(e);}

}

Integración de Sistemas

Curso 2008 - 2009 21

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Implementación de AbstractSQLAccountDAO: FindByUserIdentifierUna primera aproximación

La lista puede ser demasiado grande

Normalmente, interesará recuperar los objetos en bloques

Solución:

List<AccountVO> FindByUserIdentifier(DbConnection connection,DbTransaction transaction, long userIdentifier)

Patrón Page-by-Page Iterator

Solicitar objetos por bloques

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Implementación de AbstractSQLAccountDAO: FindByUserIdentifier

Patrón Page-by-Page IteratorSolicitar objetos por bloques

ImplementaciónDataReader no permite posicionamiento absoluto

Es posible implementarlo con SQL, aunque la implementación puede variar en función del dialecto de SQL

Veremos un ejemplo de implementación en SQLServerVeremos un ejemplo de implementación en SQLServer

Integración de Sistemas

Curso 2008 - 2009 22

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

/// <exception cref="InternalErrorException">If a severe error occurs/// </exception>public override List<AccountVO> FindByUserIdentifier(DbConnection connection,

DbTransaction transaction, long userIdentifier, int startIndex,int count)

{DbDataReader dataReader = null;

try{

/* << Creates the command >> */

/* << If transaction exists, command will be added >> */

command CommandText =command.CommandText =String.Format(

"SELECT TOP {0} accId, balance FROM Account " +"WHERE usrId = {1} " +"AND accId NOT IN (SELECT TOP {2} accId FROM Account " +" WHERE usrId = {3} ORDER BY accID) " +"ORDER BY accId",count, GetParameterSyntax("usrId"), startIndex - 1,GetParameterSyntax("usrId"));

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

DbParameter userIdParam = << ... >>

command.Prepare();

/* Execute the Query */dataReader = command.ExecuteReader();

/* Read objects. */List<AccountVO> accountVOs = new List<AccountVO>();while (dataReader.Read()){

int i = 0;Int64 accId = dataReader.GetInt64(i++);Double balance = dataReader.GetDouble(i);AccountVO account =AccountVO account =

new AccountVO(accId, userIdentifier, balance);accountVOs.Add(account);

}

/* Return value objects. */return accountVOs;

}

<< tratar excepciones y cerrar DataReader >>}

Integración de Sistemas

Curso 2008 - 2009 23

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Un ejemplo de recorrido

/* Get value objects in groups of 10. */int startIndex = 1;int groupCount = 10;List<AccountVO> accountVOs;

do {accountVOs = dao.FindByUserIdentifier(connection,

transaction, userId, startIndex, groupCount);

/* Print value objects. */foreach (AccountVO account in accountVOs) {

LogManager.RecordMessage(account.ToString(),og a age . eco d essage(accou t. oSt g(),LogManager.MessageType.INFO);

}

LogManager.RecordMessage("-------------",LogManager.MessageType.INFO);

startIndex = startIndex + groupCount;

} while (accountVOs.Count == groupCount);

Patrón Page-by-Page Iterator (cont.)

Método de diseño > 2. Gestión de la persistencia > Patrón Data Access Object (DAO)

Implementación con ADO.NET 2.0

Otras soluciones:

Usando DataSet

DbDataAdapter – Método Fill(DataSet, Int32, Int32, String)

adapter.Fill(dataSet, startIndex, count, "Users");

Inconveniente

Eficiencia reducida

Usando DataReader

Dr.Read()

Solución más eficiente que usando un DataSet

Inconveniente

Necesario recorrer todas las filas previas a startIndex

Integración de Sistemas

Curso 2008 - 2009 24

Implementación del patrón Factory en C#

Carga dinámica de clases

Clases que se desean instanciar puedenPertenecer al assembly activo

Estar situadas en un assembly independiente (fichero .dll)

NamespacesSystem.Reflection

System.Runtime.Remoting

Implementación del patrón Factory en C#

Carga dinámica de clases

Clases que pertenecen al assembly activo

// Nombre completo de la clase que se desea instanciarString daoClassName =

"Es.Udc.DotNet.MiniBank.Model.Account.DAO.SQLServerAccountDAO";

// Recuperamos el assembly actualmente en ejecucionAssembly assembly = Assembly.GetExecutingAssembly();

// Creamos instancia del tipo especificado// Parametros: // * nombre del assembly en el que se define el tipo// * tipo que deseamos instanciar// tipo que deseamos instanciarObject theObject = AppDomain.CurrentDomain.

CreateInstanceAndUnwrap(assembly.FullName, daoClassName);

return (IUserProfileDAO)theObject;

Integración de Sistemas

Curso 2008 - 2009 25

Implementación del patrón Factory en C#

Carga dinámica de clases

Clases que pertenecen a un assembly independiente

// Nombre completo de la clase que se desea instanciarString className = "MySql.Data.MySqlClient.MySqlConnection";

// Nombre de la libreria en la que esta definida la claseString driverFile =

"c:\\MySQL Connector Net 1.0.9\\Binaries\\.NET 1.1\\MySql.Data.dll";

// Cargamos el assembly a partir de su nombreAssembly assembly = Assembly.LoadFrom(driverFile);

// Creamos instancia tipo especificado// Creamos instancia tipo especificado// Parametros: // * nombre del assembly en el que se define el tipo// * tipo que deseamos instanciarObject obj = AppDomain.CurrentDomain.

CreateInstanceAndUnwrap(assembly.FullName, className);

return (DbConnection) obj;

Implementación del patrón Factory en C#

Carga dinámica de clases. AccountDAOFactory

namespace Es.Udc.DotNet.MiniBank.Model.Account.DAO{

/// <summary>/// A factory to get <c>IAccountDAO</c> objects./// </summary>public sealed class AccountDAOFactory{

/// <summary>/// Initializes a new instance of the /// <see cref="AccountOperationDAOFactory"/> class./// </summary>/// <remarks>Class constructor must be private, so nobody will be/// allowed to instantiate it </remarks>/// allowed to instantiate it </remarks>private AccountDAOFactory() { }

Integración de Sistemas

Curso 2008 - 2009 26

Implementación del patrón Factory en C#

Carga dinámica de clases. AccountDAOFactory

/// .../// <exception cref="ConfigurationParameterException"/>/// <exception cref="InternalErrorException"/>public static IAccountDAO GetDAO(){

try{

string daoClassName =Settings.Default.AccountDAOFactory_daoClassName;

Assembly assembly = Assembly.GetExecutingAssembly();Object theObject = AppDomain.CurrentDomain.

CreateInstanceAndUnwrap(assembly FullName daoClassName);CreateInstanceAndUnwrap(assembly.FullName, daoClassName);return (IAccountDAO)theObject;

}catch (Exception e){

throw new InternalErrorException(e);}

}}

}

/* Returns an instance of a * System.Data.Common.DbProviderFactory for the

Implementación del patrón Factory en C#

Carga dinámica de clases. Ejemplo de uso

System.Data.Common.DbProviderFactory for the * specified providerName*/

DbProviderFactory dbFactory = DbProviderFactories.GetFactory(providerInvariantName);

/* Create and open the connection */connection = dbFactory.CreateConnection();connection.ConnectionString = connectionString;connection.Open();

/* Get DAO */IAccountDAO dao = AccountDAOFactory.GetDAO();transaction = ...

/* Test DAO.Create */AccountVO accountVO = new AccountVO(...);AccountVO accountVO =

dao.Create(connection, transaction, accountVO);

Integración de Sistemas

Curso 2008 - 2009 27

Acceso a parámetros de configuración

Los parámetros de configuración de un proyecto se almacenan por defecto en:en:

App.config (aplicaciones de consola, librerías, …)

Web.config (aplicaciones Web)

El fichero de configuración:

Almacena pares (variable/valor)

Formato XML

Accesible en tiempo de ejecución (se accede al fichero correspondiente al proyecto en ejecución)

Acceso a parámetros no definidos implica que se lance ConfigurationErrorException

Acceso a parámetros de configuración

Ejemplo de fichero App.config

<?xml version="1.0" encoding="utf-8" ?><configuration>

<appSettings>

<!-- ****** Parameters for DataBase Connection ******** --><!-- Data Provider --><add key="AccountDAOFactory/providerInvariantName"

value="System.Data.SqlClient"/>

</appSettings>

</configuration>

Integración de Sistemas

Curso 2008 - 2009 28

Acceso a parámetros de configuración

Ejemplo de acceso

using System.Configuration;

try {

// Gets the AppSettings section for the current applicattion’s// default configuration and access to the parameter by keyString providerInvariantName =

ConfigurationSettings.AppSettings["AccountDAOFactory/providerInvariantName"];

<< ... >>

} catch(ConfigurationErrorException e) {

Debug.WriteLine("Parameter not found");}

Acceso a parámetros de configuración

Framework 2.0 introduce nueva forma de acceso a las propiedadesAccesible a través de: Propiedades Proyecto > SettingsAccesible a través de: Propiedades Proyecto > SettingsFuncionamiento

Namespace Xxx.PropertiesXxx es el namespace por defecto del proyecto

Encapsula App.config en la clase Settings.csSe accede como propiedades (no a elementos de un array)

Accesibles en tiempo de compilación a través del diseñador

En ejecución, por defecto se accede al fichero correspondiente al proyecto que se está ejecutando

Es posible sobreescribir parámetros de otros ficheros de configuración

Integración de Sistemas

Curso 2008 - 2009 29

Acceso a parámetros de configuración

Acceso a parámetros de configuración

Ejemplo<?xml version="1 0" encoding="utf 8" ?><?xml version= 1.0 encoding= utf-8 ?><configuration>

<configSections><sectionGroup name="applicationSettings" ... >

<section name="Es.Udc.DotNet.MiniBank.Properties.Settings" ... /><section name="Es.Udc.DotNet.Util.Properties.Settings" ... />

</sectionGroup></configSections>

<applicationSettings><Es.Udc.DotNet.MiniBank.Properties.Settings>p g

<setting name="UserProfileDAOFactory_providerInvariantName" serializeAs="String"><value>System.Data.SqlClient</value>

</setting>

<< ... >>

</Es.Udc.DotNet.MiniBank.Properties.Settings>

Integración de Sistemas

Curso 2008 - 2009 30

Acceso a parámetros de configuración

Ejemplo (cont.)

<Es.Udc.DotNet.Util.Properties.Settings><setting name="LogManager_FileLocation" serializeAs="String">

<value>s:\\MiniBankLog</value></setting>

</Es.Udc.DotNet.Util.Properties.Settings>

</applicationSettings></configuration>

La sección "Es.Udc.DotNet.Util.Properties.Settings" sobreescribe los valores declarados en el fichero de configuración de ese proyecto/librería

Misma filosofía que la estudiada en la primera parte de la asignatura

Método de diseño

3. Definición API modelo

1. Agrupar casos de uso de manera lógica

Distintos criterios

2. Por cada grupo definir una interfaz

En general, un método por cada caso de uso

En cada método, los argumentos corresponden a los datos de entrada del caso de uso y el valor de retorno a los datos de salida

Cada una de estas interfaces corresponde a la aplicación del patrón de diseño Facade

Integración de Sistemas

Curso 2008 - 2009 31

Método de diseño

3. Definición API modelo

Se ha definido como una interfaz (IAccountService)

Método de diseño

3. Definición API modelo

No expone la tecnología subyacente

Permite tener implementaciones alternativas

A continuación veremos algunos ejemplos de implementación utilizando ADO.NET 2.0 y GFM

Se puede ver una implementación completa en

http://sabia.tic.udc.es/docencia/is/old/2007-2008/sourcecode/DotNet Examples src 1 5 0 zipsourcecode/DotNet-Examples-src-1.5.0.zip

En el siguiente bloque veremos una implementación completa utilizando ADO.NET Entity Framework

La capa vista sólo trabaja con IAccountService

Integración de Sistemas

Curso 2008 - 2009 32

Método de diseño

4. Implementación de los casos de uso

Método de diseño > 4. Implementación de los casos de uso

Implementación de un caso de uso no transaccional

/// i f " l i "//// <exception cref="InternalErrorException"/>/// <exception cref="InstanceNotFoundException"/>public AccountVO FindAccount(long accountIdentifier){

DbConnection connection = null;try{

/* Creates the connection. */connection = dbProviderFactory.CreateConnection();connection.ConnectionString = connectionString;connection.Open();

return AccountDAO.Find(connection, null, accountIdentifier);etu ccou t O. d(co ect o , u , accou t de t e );}catch (InstanceNotFoundException){

throw;}catch (InternalErrorException){

throw;}

Integración de Sistemas

Curso 2008 - 2009 33

Método de diseño > 4. Implementación de los casos de uso

Implementación de un caso de uso no transaccional

catch (Exception e){

throw new InternalErrorException(e);}finally{

try{

if (connection != null){

connection.Close();}

}catch (Exception e){

throw new InternalErrorException(e);}

}}

Método de diseño > 4. Implementación de los casos de uso

Implementación de un caso de uso transaccional

/// <exception cref="InternalErrorException"/>/// <exception cref="InstanceNotFoundException"/>public void AddToAccount(long accountIdentifier, double amount){

DbConnection connection = null;DbTransaction transaction = null;Boolean rollback = false;

try{

/* Creates the connection. */connection = dbProviderFactory.CreateConnection();connection.ConnectionString = connectionString;connection.Open();

/* Starts a new transaction. */transaction =

connection.BeginTransaction(IsolationLevel.Serializable);

Integración de Sistemas

Curso 2008 - 2009 34

Método de diseño > 4. Implementación de los casos de uso

Implementación de un caso de uso transaccional

/* Find account. */AccountVO accountVO =

AccountDAO.Find(connection, transaction, accountIdentifier);

/* Update account. */accountVO.Balance = accountVO.Balance + amount;AccountDAO.Update(connection, transaction, accountVO);

/* Register account operation. */AccountOperationVO accountOperationVO =

new AccountOperationVO(-1, accountIdentifier, DateTime.Now, AccountOperationType.ADD,amount);

AccountOperationDAO.Create(connection, transaction, accountOperationVO);

}

Método de diseño > 4. Implementación de los casos de uso

Implementación de un caso de uso transaccional

catch (ModelException){

rollback = true;throw;

}catch (InternalErrorException e){

rollback = true;throw e;

}catch (Exception e){{

rollback = true;throw new InternalErrorException(e);

}

Integración de Sistemas

Curso 2008 - 2009 35

Método de diseño > 4. Implementación de los casos de uso

Implementación de un caso de uso transaccional

finally{

try{

/* Commits or rollbacks, and finally, closes connection. */if (connection != null){

if (rollback){

transaction.Rollback();}else{

transaction.Commit();transaction.Commit();}connection.Close();

}}catch (Exception e){

throw new InternalErrorException(e);}

}}

Existe bastante código que podría ser factorizado, especialmente en los casos de uso transaccionales

Método de diseño

4. Implementación de los casos de uso

Solución:

Implementar cada caso de uso en una clase Action

Implementar un Procesador de Acciones

Distingue entre acciones transaccionales y no transaccionales

Ejecuta operaciones comunes y procesa la acción

En la versión de los ejemplos DotNet-Examples-src-1.5.0, disponible en http://sabia.tic.udc.es/docencia/is/old/2007-2008/sourcecode/DotNet-Examples-src-1.5.0.zip, se puede consultar:

MiniPortal, implementado sin Procesador de Acciones

MiniBank, implementado con Procesador de Acciones

Integración de Sistemas

Curso 2008 - 2009 36

En este curso, estudiaremos la implementación de MiniBank y MiniPortal utilizando:

Método de diseño

4. Implementación de los casos de uso

utilizando:Entity Framework para la persistencia de entidades

Unity para la inyección de dependencias y la definición de transacciones siguiendo un enfoque declarativo

Método de diseño

5. Pruebas de integración de los casos de uso

En la primera parte de la asignatura se han explicado los tipos de pruebas más usualesmás usuales

Pruebas de unidad

Pruebas de integración sobre la capa modelo

Pruebas funcionales sobre la interfaz gáfica

Pruebas de carga

Y la importancia de que estén automatizadas

Para esto, utilizaremos TestProject, integrado en VisualStudio

Nos centraremos en la construcción de pruebas de integración sobre la capa modelo