Marcos de Desarrollo Diseño e implementación de aplicaciones Web con .NET Contenido En este apartado estudiaremos el diseño e implementación de las capas controlador y vista de MiniBank Configuración de la aplicación Web Página maestra de MiniBank Un ejemplo de una acción que realiza una operación que no visualiza resultados Transferencia bancaria Un ejemplo de una acción que realiza una operación y visualiza el resultado de la operación Búsqueda de cuentas bancarias por identificador de cuenta (resultado en una página) o de usuario (resultado en varias páginas) Internacionalización Introducción Configuración: Web.config <?xml version="1.0"?> <configuration> <system.web> <!‐‐ Cultural preferences of the Web site ‐‐> <globalization culture="auto" uiCulture="auto" /> <!‐‐ Valid Options: UseUri (True), UseCookies (False), AutoDetect ‐‐> <sessionState cookieless="AutoDetect" timeout="30" /> <!‐‐ Valid Options: On, Off, RemoteOnly ‐‐> <customErrors mode="RemoteOnly" defaultRedirect="/Errors/InternalError.aspx"> </customErrors> <<...>> </system.web> <!‐‐ Unity configuration block ‐‐> <unity> <!‐‐ Identico a App.config en MiniBank (model) ‐‐> </unity> Configuración: Web.config <applicationSettings> <Es.Udc.DotNet.MiniBank.Web.Properties.Settings> <setting name="MiniBank_defaultCount" serializeAs="String"> <value>2</value> </setting> ... </Es.Udc.DotNet.MiniBank.Web.Properties.Settings> </applicationSettings> No confundir con appSettings No actualizar esta sección directamente sobre Web.config, sino desde la pestaña de configuración del proyecto => actualiza Settings.settings y Settings.designer.cs Configuración: Web.config Pestaña Settings en las propiedades del proyecto Permite acceso "tipado". P. ej.: Settings.Default.MiniBank_defaultCount; Configuración: Global.asax namespace Es.Udc.DotNet.MiniBank.Web { public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { Application.Lock(); IUnityContainer container = new UnityContainer(); UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity"); section.Containers.Default.Configure(container); Application["UnityContainer"] = container; Application.UnLock(); } <<...>> Configuración: otras opciones Contenido En este apartado estudiaremos el diseño e implementación de las capas controlador y vista de MiniBank Configuración de la aplicación Web Página maestra de MiniBank Un ejemplo de una acción que realiza una operación que no visualiza resultados Transferencia bancaria Un ejemplo de una acción que realiza una operación y visualiza el resultado de la operación Búsqueda de cuentas bancarias por: identificador de cuenta (resultado en una página) identificador de usuario (resultado en varias páginas) Internacionalización MiniBank.Master: Vista de diseño MiniBank.Master: Vista de código <%@ Master Language="C#" AutoEventWireup="true" CodeBehind="MiniBank.master.cs" Inherits="Es.Udc.DotNet.MiniBank.Web.MiniBank" %> <!DOCTYPE html PUBLIC "‐//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1‐transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>MiniBank</title> <meta http‐equiv="Content‐Type" content="text/html; charset=iso‐8859‐1"/> <link href="~/Img/MiniBank.ico" rel="Shortcut Icon" /> <link href="~/Css/MiniBank.Styles.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="window"> <!‐‐ Body Header. ‐‐> <div id="header"> <asp:Localize ID="lclHeader" runat="server" meta:resourcekey="lclHeader" /> </div> MiniBank.Master: Vista de código <!‐‐ Main Content. ‐‐> <div id="pageBody"> <div id="sidebar"> <ul> <li> <asp:HyperLink ID="lnkHome" runat="server" meta:resourcekey="lnkHome" NavigateUrl="~/Pages/MainPage.aspx" /> </li> <!‐‐ Mas HyperLinks... ‐‐> </ul> </div> <div id="content"> <asp:ContentPlaceHolder ID="ContentPlaceHolderMain" runat="server"> </asp:ContentPlaceHolder> </div> </div> <!‐‐ Footer. ‐‐> <div id="footer"> <asp:Localize ID="lclFooter" runat="server" meta:resourcekey="lclFooter" /> </div> </div> </body> </html> MiniBank.Master.cs using System; namespace Es.Udc.DotNet.MiniBank.Web { public partial class MiniBank : System.Web.UI.MasterPage { protected void Page_Load(object sender, EventArgs e) { } } } MiniBank.Master.designer.cs namespace Es.Udc.DotNet.MiniBank.Web { public partial class MiniBank { protected global::System.Web.UI.WebControls.Localize lclHeader; protected global::System.Web.UI.WebControls.HyperLink lnkHome; protected global::System.Web.UI.WebControls.HyperLink lnkCreate; <<...>> protected global::System.Web.UI.WebControls.ContentPlaceHolder ContentPlaceHolderMain; protected global::System.Web.UI.WebControls.Localize lclFooter; } } Contenido En este apartado estudiaremos el diseño e implementación de las capas controlador y vista de MiniBank Configuración de la aplicación Web Página maestra de MiniBank Un ejemplo de una acción que realiza una operación que no visualiza resultados Transferencia bancaria Un ejemplo de una acción que realiza una operación y visualiza el resultado de la operación Búsqueda de cuentas bancarias por: identificador de cuenta (resultado en una página) identificador de usuario (resultado en varias páginas) Internacionalización Transferencia Transfer.aspx SuccessfulOperation.aspx Transferencia: Transfer.aspx (Vista de diseño) Transferencia: Transfer.aspx.designer.cs namespace Es.Udc.DotNet.MiniBank.Web.Pages { public partial class Transfer { protected global::System.Web.UI.HtmlControls.HtmlForm TransferForm; protected global::System.Web.UI.WebControls.Localize lclSourceAccId; protected global::System.Web.UI.WebControls.Localize lclDestinationAccId; protected global::System.Web.UI.WebControls.Localize lclAmount; protected global::System.Web.UI.WebControls.TextBox txtSourceAccId; protected global::System.Web.UI.WebControls.TextBox txtDestinationAccId; protected global::System.Web.UI.WebControls.TextBox txtAmount; protected global::System.Web.UI.WebControls.Label lblSourceAccError; protected global::System.Web.UI.WebControls.Label lblDestinationAccError; protected global::System.Web.UI.WebControls.Label lblAmountError; Transferencia: Transfer.aspx.designer.cs protected global::System.Web.UI.WebControls.RequiredFieldValidator rfvSourceAccId; protected global::System.Web.UI.WebControls.RequiredFieldValidator rfvDestinationAccId; protected global::System.Web.UI.WebControls.RequiredFieldValidator rfvBalance; protected global::System.Web.UI.WebControls.RegularExpressionValidator typeSourceAccIdValidator; protected global::System.Web.UI.WebControls.RegularExpressionValidator typeDestinationAccId; protected global::System.Web.UI.WebControls.RegularExpressionValidator typeAmountValidator; protected global::System.Web.UI.WebControls.Button btnTransfer; } } Transferencia: Transfer.aspx (Vista de código) <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Transfer.aspx.cs" Inherits="Es.Udc.DotNet.MiniBank.Web.Pages.Transfer" MasterPageFile="~/MiniBank.Master" %> <asp:Content ID="content" ContentPlaceHolderID="ContentPlaceHolderMain" runat="server"> <div id="form"> <form id="TransferForm" method="post" runat="server"> <div class="field"> <span class="label"> <asp:Localize ID="lclSourceAccId" runat="server" Localización explícita. Acceso a archivo meta:resourcekey="lclSourceAccId" /></span> de recursos global llamado "Common" (Common.resx, Common.es-ES.resx, …). <span class="entry"> <asp:TextBox ID="txtSourceAccId" runat="server" Width="200px" Mensaje: "Invalid data type" Columns="16"></asp:TextBox> <asp:RegularExpressionValidator ID="typeSourceAccIdValidator" runat="server" ControlToValidate="txtSourceAccId" Text="<%$ Resources: Common, typeError %>" ValidationExpression="(\d)*" CssClass="errorMessage" Display="Dynamic"> </asp:RegularExpressionValidator> Transferencia: Transfer.aspx (Vista de código) <asp:RequiredFieldValidator ID="rfvSourceAccId" runat="server" ControlToValidate="txtSourceAccId" Display="Dynamic" Text="<%$ Resources: Common, mandatoryField %>" CssClass="errorMessage"> </asp:RequiredFieldValidator> <asp:Label ID="lblSourceAccError" runat="server" CssClass="errorMessage" meta:resourcekey="lblSourceAccError"> </asp:Label> Localización implícita. Acceso a archivo </span> de recursos local asociado a </div> Transfer.aspx (Transfer.resx, Transfer.es-Es.resx, …) <<...>> Mensaje: "Source Account not Found" <div class="button"> <asp:Button ID="btnTransfer" runat="server" meta:resourcekey="btnTransfer" OnClick="BtnTransfer_Click" /> </div> </form> Indica el nombre del método que tratará </div> el evento OnClick </asp:Content> Transferencia: Transfer.aspx.cs public partial class Transfer : SpecificCulturePage { protected void Page_Load(object sender, EventArgs e) { lblSourceAccError.Visible = false; lblDestinationAccError.Visible = false; lblAmountError.Visible = false; } protected void BtnTransfer_Click(object sender, EventArgs e) { if (Page.IsValid) { /* Get data. */ long sourceAccountIdentifier = Convert.ToInt32(txtSourceAccId.Text); long destinationAccountIdentifier = Convert.ToInt32(txtDestinationAccId.Text); double amount = Convert.ToInt64(txtAmount.Text); Transferencia: Transfer.aspx.cs /* Transfer. */ try { /* Get the Service */ IUnityContainer container = (IUnityContainer)Application["unityContainer"]; IAccountService accountService = container.Resolve<IAccountService>(); accountService.Transfer(sourceAccountIdentifier, destinationAccountIdentifier, amount); Response.Redirect(Response. ApplyAppPathModifier("./SuccessfulOperation.aspx")); } Transferencia: Transfer.aspx.cs catch (InstanceNotFoundException ex) { long key = (long)ex.Key; /* Process errors and show labels */ if (key.Equals(sourceAccountIdentifier)) lblSourceAccError.Visible = true; else lblDestinationAccError.Visible = true; } catch (InsufficientBalanceException ex) { /* lblAmmountError.Text is something like "Insufficient balance * in source account (balance = {0})" */ String labelText = String.Format( (string)GetLocalResourceObject("lblAmountError.Text"), ex.CurrentBalance); lblAmountError.Text = labelText; lblAmountError.Visible = true; } } // if } // BtnTransferClick } // Transfer Class Queremos que se muestre el saldo como parte del mensaje, con soporte para internacionalización Contenido En este apartado estudiaremos el diseño e implementación de las capas controlador y vista de MiniBank Configuración de la aplicación Web Página maestra de MiniBank Un ejemplo de una acción que realiza una operación que no visualiza resultados Transferencia bancaria Un ejemplo de una acción que realiza una operación y visualiza el resultado de la operación Búsqueda de cuentas bancarias por: identificador de cuenta (resultado en una página) identificador de usuario (resultado en varias páginas) Internacionalización Búsqueda de cuentas por identificador de cuenta FindAccounts.aspx ShowAccountByAccID.aspx Búsqueda de cuentas por identificador de usuario FindAccounts.aspx ShowAccountsByUserID.aspx Búsqueda de cuentas: FindAccounts.aspx (Vista de diseño) Búsqueda de cuentas: FindAccounts.aspx (Vista de código) <asp:Content ID="content1" ContentPlaceHolderID="ContentPlaceHolderMain" runat="server"> <div id="form"> <form id="FindForm" method="post" runat="server"> <div class="field"> <span class="label"> <asp:Localize ID="lclIdentifier" runat="server" meta:resourcekey="lblIdentifier" /> </span> <span class="entry"> <asp:TextBox ID="txtIdentifier" runat="server" Width="200px" Columns="16" /> <asp:RegularExpressionValidator ID="typeValidator" runat="server" ControlToValidate="txtIdentifier" ValidationExpression="(\d)*" Text="<%$ Resources: Common, typeError %>" Display="Dynamic" CssClass="errorMessage" /> <asp:RequiredFieldValidator ID="rfvIdentifier" runat="server" ControlToValidate="txtIdentifier" Display="Dynamic" Text="<%$ Resources: Common, mandatoryField %>" CssClass="errorMessage" /> <asp:Label CssClass="errorMessage" ID="lblIdentifierError" runat="server" meta:resourcekey="lblIdentifierError" /> </span> </div> Búsqueda de cuentas: FindAccounts.aspx (Vista de código) <div class="field"> <span class="label"> <asp:Localize ID="lclFindBy" runat="server" meta:resourcekey="lclFindBy" /> </span> <span class="entry"> <asp:DropDownList ID="ddlFindBy" runat="server" Width="200px"> <asp:ListItem Value="accID" Text="<%$ Resources:Common, accId %>" /> <asp:ListItem Value="userID" Text="<%$ Resources:Common, userId %>" /> </asp:DropDownList> </span> </div> <div class="button"> <asp:Button ID="btnFind" runat="server" meta:resourcekey="btnFind" OnClick="BtnFind_Click" /> </div> </form> </div> </asp:Content> Búsqueda de cuentas: FindAccounts.aspx.cs public partial class FindAccounts : SpecificCulturePage { protected void Page_Load(object sender, EventArgs e) { lblIdentifierError.Visible = false; } Búsqueda de cuentas: FindAccounts.aspx.cs protected void BtnFind_Click(object sender, EventArgs e) { if (Page.IsValid) { /* Get data. */ String identifierType = this.ddlFindBy.SelectedValue; Int32 identifier = Convert.ToInt32(this.txtIdentifier.Text); /* Do action. */ if (identifierType == "accID") { FindAccountByAccountIdentifier(identifier); } else { String url = String.Format("./ShowAccountsByUserID.aspx?userID={0}", identifier); Response.Redirect(Response.ApplyAppPathModifier(url)); } } Búsqueda de cuentas: FindAccounts.aspx.cs private void FindAccountByAccountIdentifier(long accountIdentifier) { try { /* Get the Service */ /* Get Account Data */ Account account = accountService.FindAccount(accountIdentifier); /* Attach data to context */ Context.Items.Add("account", account); /* Transfer to visualization WebForm */ Server.Transfer(Response. ApplyAppPathModifier("./ShowAccountByAccID.aspx")); } catch (InstanceNotFoundException) { lblIdentifierError.Visible = true; } } } Búsqueda de cuentas: FindAccounts.aspx.cs private void FindAccountByAccountIdentifier(long accountIdentifier) { try { /* Get the Service */ /* Get Account Data */ Account account = accountService.FindAccount(accountIdentifier); /* Attach data to context */ Context.Items.Add("account", account); Permite almacenar datos que se necesitarán más adelante, durante el mismo postback /* Transfer to visualization WebForm */ Server.Transfer(Response. ApplyAppPathModifier("./ShowAccountByAccID.aspx")); } catch (InstanceNotFoundException) { lblIdentifierError.Visible = true; } } } Búsqueda de cuentas por identificador de cuenta FindAccounts.aspx ShowAccountByAccID.aspx Búsqueda de cuentas: ShowAccountByAccID.aspx (Vista de diseño) Búsqueda de cuentas: ShowAccountByAccID.aspx (Vista de código) <asp:Content ID="content1" ContentPlaceHolderID="ContentPlaceHolderMain" runat="server"> <asp:Table CssClass="accountDetails" ID="TableAccountInfo" runat="server"> <asp:TableRow runat="server"> <asp:TableHeaderCell ID="cellCaptionAccountID" runat="server" Text="<%$ Resources:Common, accId %>"></asp:TableHeaderCell> <asp:TableCell ID="cellAccountID" runat="server"></asp:TableCell> </asp:TableRow> <asp:TableRow runat="server"> <asp:TableHeaderCell ID="cellCaptionUserID" runat="server" Text="<%$ Resources:Common, userId %>"></asp:TableHeaderCell> <asp:TableCell ID="cellUserID" runat="server"></asp:TableCell> </asp:TableRow> <asp:TableRow runat="server"> <asp:TableHeaderCell ID="cellCaptionBalance" runat="server" Text="<%$ Resources:Common, balance %>"></asp:TableHeaderCell> <asp:TableCell ID="cellBalance" runat="server"></asp:TableCell> </asp:TableRow> </asp:Table> </asp:Content> Búsqueda de cuentas: ShowAccountByAccID.aspx.cs protected void Page_Load(object sender, EventArgs e) { Account account = (Account)Context.Items["account"]; cellAccountID.Text = account.accId.ToString(); cellUserID.Text = account.usrId.ToString(); cellBalance.Text = account.balance.ToString(); } Búsqueda de cuentas por identificador de usuario FindAccounts.aspx ShowAccountsByUserID.aspx Búsqueda de cuentas: ShowAccountsByUserID.aspx (Vista de diseño) GridView Tipos de columnas BoundField, CheckBox, ButtonField, HyperLinkField, etc. No es obligatorio indicar las columnas, pueden autogenerarse AutoGenerateColumns=True muestra todo el contenido del origen de datos Code‐Behind: gvXXX.DataSource = <<...>>; gvXXX.DataBind(); // Origen de Datos: DataTable, ArrayList, ... GridView Ofrece la posibilidad de realizar la paginación de los resultados Propiedades AllowPaging: habilita la paginación PageIndex: índice de la página a mostrar (zero‐based) PageSize: número de registros por página … pero recupera la totalidad de los datos y posteriormente los pagina Ideal: recuperar únicamente los datos que serán mostrados GridView Opciones: Opción 1: Separar representación visual de comportamiento Se ilustra en ShowAccountsByUserID Opción 2: ObjectDataSource Se ilustra en ShowAccOperations GridView Opción 1: Separar representación visual de comportamiento Datos se recuperan a partir de un caso de uso que soporta paginación /* Get Accounts Info */ List<Account> accounts = accountService.FindAccountsByUserIdentifier(userID, startIndex, count); GridView visualiza los datos Estableciendo el DataSource adecuado y realizando el DataBind Paginación se delega en sendos linkButtons para avanzar y retroceder página Búsqueda de cuentas: ShowAccountsByUserID.aspx (Vista de código) <asp:Content ID="content1" ContentPlaceHolderID="ContentPlaceHolderMain" runat="server"> <form runat="server"> <p> <asp:Label ID="lblNoUserAccounts" meta:resourcekey="lblNoUserAccounts" runat="server"> </asp:Label> </p> <asp:GridView ID="gvUserAccounts" runat="server" CssClass="userAccounts" GridLines="None" AutoGenerateColumns="False"> <Columns> <asp:BoundField DataField="accId" HeaderText="<%$ Resources:Common, accId %>" /> <asp:BoundField DataField="balance" HeaderText="<%$ Resources:Common, balance %>" /> </Columns> </asp:GridView> </form> Búsqueda de cuentas: ShowAccountsByUserID.aspx (Vista de código) <!‐‐ "Previous" and "Next" links. ‐‐> <div class="previousNextLinks"> <span class="previousLink"> <asp:HyperLink ID="lnkPrevious" Text="<%$ Resources:Common, Previous %>" runat="server" Visible="False"> </asp:HyperLink> </span> <span class="nextLink"> <asp:HyperLink ID="lnkNext" Text="<%$ Resources:Common, Next %>" runat="server" Visible="False"> </asp:HyperLink> </span> </div> </asp:Content> Los enlaces next y previous son controles asp:HyperLink, que pueden gestionarse fácilmente desde CB, controlando si deben mostrarse o no, así como la ruta a la que apuntan. Búsqueda de cuentas: ShowAccountsByUserID.aspx.cs protected void Page_Load(object sender, EventArgs e) { int startIndex, count; lnkPrevious.Visible = false; lnkNext.Visible = false; lblNoUserAccounts.Visible = false; /* Get User Identifier passed as parameter in the request from * the previous page */ long userID = Convert.ToInt32(Request.Params.Get("userID")); /* Get Start Index */ try { startIndex = Int32.Parse(Request.Params.Get("startIndex")); } catch (ArgumentNullException) { startIndex = 0; } Búsqueda de cuentas: ShowAccountsByUserID.aspx.cs /* Get Count */ try { count = Int32.Parse(Request.Params.Get("count")); } catch (ArgumentNullException) { count = Settings.Default.MiniBank_defaultCount; } /* Get the Service */ IUnityContainer container = ... /* Get Accounts Info */ List<Account> accounts = accountService.FindAccountsByUserIdentifier(userID, startIndex, count); if (accounts.Count == 0) { lblNoUserAccounts.Visible = true; return; } Búsqueda de cuentas: ShowAccountsByUserID.aspx.cs this.gvUserAccounts.DataSource = accounts; this.gvUserAccounts.DataBind(); /* Get the number of accounts in order to manage the previous * and next links */ int numberOfAccounts = accountService.GetNumberOfAccounts(userID); /* "Previous" link */ if ((startIndex ‐ count) >= 0) { String url = Settings.Default.MiniBank_applicationURL + "/ShowAccountsByUserID.aspx" + "?userID=" + userID + "&startIndex=" + (startIndex ‐ count) + "&count=" + count; this.lnkPrevious.NavigateUrl = Response.ApplyAppPathModifier(url); this.lnkPrevious.Visible = true; } Búsqueda de cuentas: ShowAccountsByUserID.aspx.cs /* "Next" link */ if ((startIndex + count) < numberOfAccounts) { String url = Settings.Default.MiniBank_applicationURL + "/ShowAccountsByUserID.aspx" + "?userID=" + userID + "&startIndex=" + (startIndex + count) + "&count=" + count; this.lnkNext.NavigateUrl = Response.ApplyAppPathModifier(url); this.lnkNext.Visible = true; } } GridView Opciones: Opción 1: Separar representación visual de comportamiento Se ilustra en ShowAccountsByUserID Opción 2: ObjectDataSource Se ilustra en ShowAccOperations Búsqueda de operaciones bancarias FindAccountOperations.aspx ShowAccOperations.aspx GridView Opción 2: ObjectDataSource Componente que permite recuperar objetos bajo demanda de un repositorio y enlazarlos como un origen de datos ObjectDataSource Propiedades relacionadas con la paginación EnablePaging TypeName: indica el tipo de dato que define los métodos empleados para realizar la paginación e.g.: Es.Udc.DotNet.MiniBank.Model.AccountService.AccountService SelectMethod: nombre del método que realiza la búsqueda mediante Page‐by‐Page e.g.: FindAccountOperationsByDate Los parámetros específicos se indican mediante la colección SelectParameters .SelectParameters.Add("accountIdentifier", DbType.Int64, accID.ToString()); .SelectParameters.Add("startDate", DbType.DateTime, startDate.ToString()); .SelectParameters.Add("endDate", DbType.DateTime, endDate.ToString()); ObjectDataSource Propiedades relacionadas con la paginación (cont.) SelectCountMethod: nombre del método que devuelve el número máximo de registros e.g.: GetNumberofAccountOperations StartRowIndexParameterName: nombre del parámetro que indica el registro a partir del cual se búsqueda e.g.: startIndex MaximumRowsParameterName: Nombre del parámetro que indica el número de registros recuperados por consulta e.g.: count ObjectDataSource Funcionamiento Se crea automáticamente una instancia del tipo declarado en DataSource.TypeName pbpDataSource.TypeName = "Es.Udc.DotNet.MiniBank.Model.AccountService.AccountService"; implica una llamada a new AccountService(); Problema: en MiniBank la instanciación de AccountService implica resolución de dependencias, que de esta forma no se realizarán Solución: Capturar el evento ObjectCreating y realizar las operaciones necesarias para que se resuelvan las dependencias ObjectDataSource Funcionamiento En Page_Load se declara el delegado pbpDataSource.ObjectCreating += this.pbpDataSource_ObjectCreating; Luego se implementa el delegado protected void pbpDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e) { // Aqui se instancia el objeto DataSource.TypeName // Por defecto: new DataSourceTypeName. Por ej: new AccountService() // pero se puede dar implementacion alternativa } ObjectDataSource pbpDataSource_ObjectCreating protected void pbpDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e) { /* Get the Service */ IUnityContainer container = (IUnityContainer) HttpContext.Current.Application["unityContainer"]; IAccountService accountService = new AccountService(); accountService = (IAccountService) container.BuildUp(accountService.GetType(), accountService); e.ObjectInstance = accountService; } Hace que se carguen las dependencias de AccountService. container.Resolve<IAccountService>(); No funciona. Búsqueda de operaciones bancarias: ShowAccOperations.aspx (Vista de código) <asp:Content ID="content" ContentPlaceHolderID="ContentPlaceHolderMain" runat="server"> <p> <asp:Label ID="lblInvalidAccount" meta:resourcekey="lblInvalidAccount" runat="server" Visible=false></asp:Label> </p> <form runat="server"> <asp:GridView ID="gvAccOperations" runat="server" CssClass="accountOperations" AutoGenerateColumns="False" onpageindexchanging="gvAccOperations_PageIndexChanging" ShowHeaderWhenEmpty="True"> <Columns> <asp:BoundField DataField="Date" HeaderText="<%$ Resources:, date %>" /> <asp:BoundField DataField="Type" HeaderText="<%$ Resources:, type %>" /> <asp:BoundField DataField="Amount" HeaderText="<%$ Resources:, amount %>" /> </Columns> </asp:GridView> <br /> </form> Búsqueda de operaciones bancarias: ShowAccOperations.aspx.cs protected void Page_Load(object sender, EventArgs e) { try { pbpDataSource.ObjectCreating += this.pbpDataSource_ObjectCreating; pbpDataSource.TypeName = "Es.Udc.DotNet.MiniBank.Model.AccountService.AccountService"; pbpDataSource.EnablePaging = true; pbpDataSource.SelectMethod = "FindAccountOperationsByDate"; /* Get Account Identifier */ long accID = Convert.ToInt32(Request.Params.Get("accID")); /* Get the start and end date (without time) */ DateTime startDate = Convert.ToDateTime(Request.Params.Get("startDate")); DateTime endDate //... Búsqueda de operaciones bancarias: ShowAccOperations.aspx.cs (cont) pbpDataSource.SelectParameters. Add("accountIdentifier", DbType.Int64, accID.ToString()); pbpDataSource.SelectParameters. Add("startDate", DbType.DateTime, startDate.ToString()); pbpDataSource.SelectParameters. Add("endDate", DbType.DateTime, endDate.ToString()); pbpDataSource.SelectCountMethod = "GetNumberofAccountOperations"; pbpDataSource.StartRowIndexParameterName = "startIndex"; pbpDataSource.MaximumRowsParameterName = "count"; gvAccOperations.AllowPaging = true; int count = Settings.Default.MiniBank_defaultCount; gvAccOperations.PageSize = count; gvAccOperations.DataSource = pbpDataSource; gvAccOperations.DataBind(); } catch (Exception) { lblInvalidAccount.Visible = true; } } Búsqueda de operaciones bancarias: ShowAccOperations.aspx.cs (cont) protected void gvAccOperations_PageIndexChanging(object sender, GridViewPageEventArgs e) { gvAccOperations.PageIndex = e.NewPageIndex; gvAccOperations.DataBind(); } protected void pbpDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e) { /* Get the Service */ IUnityContainer container = (IUnityContainer)HttpContext.Current.Application["unityContainer"]; IAccountService accountService = new AccountService(); accountService = (IAccountService)container.BuildUp(accountService.GetType(), accountService); e.ObjectInstance = accountService; } Contenido En este apartado estudiaremos el diseño e implementación de las capas controlador y vista de MiniBank Configuración de la aplicación Web Página maestra de MiniBank Un ejemplo de una acción que realiza una operación que no visualiza resultados Transferencia bancaria Un ejemplo de una acción que realiza una operación y visualiza el resultado de la operación Búsqueda de cuentas bancarias por identificador de cuenta (resultado en una página) o de usuario (resultado en varias páginas) Internacionalización Selección de idioma y país Cuando cambia el idioma, se actualiza la lista de países y se muestran en el idioma seleccionado, ordenados alfabéticamente SetLocale.aspx Selección de idioma y país: SetLocale.aspx.cs protected void Page_Load(object sender, EventArgs e) { String language; String country; if (!IsPostBack) { /* * We check if exists a locale in the session. In this case, * we get the language and the region/country from the locale. * Other case we use the browser preferences to extract the * language and the region/country */ if (!SessionManager.IsLocaleDefined(Context)) { /* Gets preferred language from browser */ language = GetLanguageFromBrowserPreferences(); country = GetCountryFromBrowserPreferences(); } Selección de idioma y país: SetLocale.aspx.cs else { Locale locale = SessionManager.GetLocale(Context); language = locale.Language; country = locale.Country; } /* Finally we update de data of the "Combo", using the * selected language and region/country. */ UpdateComboLanguage(language); UpdateComboCountry(language, country); } } Selección de idioma y país: SetLocale.aspx.cs private String GetLanguageFromBrowserPreferences() { String language; CultureInfo cultureInfo = CultureInfo.CreateSpecificCulture(Request.UserLanguages[0]); language = cultureInfo.TwoLetterISOLanguageName; return language; } Selección de idioma y país: SetLocale.aspx.cs private String GetCountryFromBrowserPreferences() { String country; CultureInfo cultureInfo = CultureInfo.CreateSpecificCulture(Request.UserLanguages[0]); if (cultureInfo.IsNeutralCulture) { country = ""; } else { // cultureInfoName is something like en‐US String cultureInfoName = cultureInfo.Name; // Gets the last two caracters of cultureInfoname country = cultureInfoName.Substring(cultureInfoName.Length ‐ 2); } return country; } Selección de idioma y país: SetLocale.aspx.cs private void UpdateComboLanguage(String selectedLanguage) { this.comboLanguage.DataSource = Languages.GetLanguages(selectedLanguage); this.comboLanguage.DataTextField = "value"; this.comboLanguage.DataValueField = "text"; this.comboLanguage.DataBind(); this.comboLanguage.SelectedValue = selectedLanguage; } private void UpdateComboCountry(String selectedLanguage, String selectedCountry) { this.comboCountry.DataSource = Countries.GetCountries(selectedLanguage); this.comboCountry.DataTextField = "value"; this.comboCountry.DataValueField = "text"; this.comboCountry.DataBind(); this.comboCountry.SelectedValue = selectedCountry; } Selección de idioma y país: SetLocale.aspx.cs protected void BtnSetLocale_Click(object sender, EventArgs e) { string language = comboLanguage.SelectedValue; string country = comboCountry.SelectedValue; Locale locale = new Locale(language, country); SessionManager.SetLocale(Context, locale); Response.Redirect(Response. ApplyAppPathModifier("~/MainPage.aspx")); } Selección de idioma y país: SetLocale.aspx.cs <asp:DropDownList ID="comboLanguage" runat="server" AutoPostBack="True" Width="100px" OnSelectedIndexChanged="ComboLanguageSelectedIndexChanged"> </asp:DropDownList> SetLocale.aspx protected void ComboLanguage_SelectedIndexChanged(object sender, EventArgs e) { /* After a language change, the countries are printed in the * correct language. */ this.UpdateComboCountry(comboLanguage.SelectedValue, comboCountry.SelectedValue); } SetLocale.aspx.cs NOTA: prueba a cambiar AutoPostBack="False". ¿Qué ocurre? SpecificCulturePage Extiende a System.Web.UI.Page y todas las páginas del sitio deben extenderla protected override void InitializeCulture() { if (SessionManager.IsLocaleDefined(Context)) { Locale locale = SessionManager.GetLocale(Context); String culture = locale.Language + "‐" + locale.Country; CultureInfo cultureInfo; try { cultureInfo = CultureInfo.CreateSpecificCulture(culture); LogManager.RecordMessage("Specific culture created: " + cultureInfo.Name); } catch (ArgumentException) { cultureInfo = CultureInfo.CreateSpecificCulture("en‐US"); } Thread.CurrentThread.CurrentCulture = cultureInfo; Thread.CurrentThread.CurrentUICulture = cultureInfo; } }