Diseño de la capa modelo - SABIA-Group :: Adaptative...
-
Upload
trinhtuyen -
Category
Documents
-
view
214 -
download
0
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