Está en la página 1de 99

Titulo del libro

subtítulo del libro

Nombre del autor


PUBLICADO POR

DevDiv, .NET y Visual Studio equipos produc Una división


de Microsoft Corporation One Microsoft Way

Redmond, Washington 98052-6399

Copyright © 2017 por Microsoft Corporation

Todos los derechos reservados. Ninguna parte del contenido de este libro puede ser reproducida o transmitida en cualquier forma o por
cualquier medio sin el permiso por escrito del editor.

Este libro se proporciona “tal cual” y expresa puntos de vista y las opiniones del autor. Los puntos de vista, opiniones e informaciones expresadas en este
libro, incluidas las direcciones URL y otras referencias a sitios web de Internet, pueden cambiar sin previo aviso.

Algunos ejemplos descritos en el presente documento se proporcionan solamente con ilustración y son ficticios. No se encontró asociación o conexión real se
pretende ni se debe inferir.

Microsoft y las marcas que figuran en http://www.microsoft.com en la página web “marcas” son marcas comerciales del grupo de
compañías de Microsoft. Todas las demás marcas son propiedad de sus respectivos dueños.

Autor: David Britch


Desarrollador: Javier Suarez Ruiz (Plain Concepts)
Los participantes y los colaboradores: Craig Dunn, Tom Opgenorth
Editor: John Meade (Populus Group)
Contenido
Prefacio ................................................. .................................................. iv ........................ ........................ Propósito
.................................................. .................................................. iv ......
Lo que queda fuera del alcance de esta guía ........................................... .................................................. . iv quién va dirigida
esta guía ........................................... .................................................. ................ iv Cómo utilizar esta guía ............................
.................................................. ........................................ v

Introducción ................................................. .................................................. ................. 1


Ejemplo de aplicación ................................................ .................................................. ................. 2
arquitectura de la aplicación de la muestra ............................................... .................................................. 2 .....

Aplicación movil ................................................ .................................................. ..................................... 4


proyecto eShopOnContainers.Core .................................................. ................................................. 5
proyectos de plataforma .................................................. .................................................. ........................ 6

Resumen ................................................. .................................................. .............................. 6

MVVM ................................................. .................................................. .......................... 7


El patrón MVVM ............................................... .................................................. ................. 7
Ver ................................................. .................................................. .............................................. 8
ViewModel ................................................. .................................................. .................................... 8
Modelo ................................................. .................................................. ............................................ 9
Conexión de modelos de vista de vistas ............................................. ................................................ 9
La creación de un modelo de vista declarativa ............................................. ................................................ 10

La creación de un modelo de vista mediante programación ............................................. ........................................ 10

Creación de una vista definida como una plantilla de datos .......................................... ........................................ 11

crear automáticamente un modelo de vista con una vista de modelo localizador ........................................ ......... 11

Actualización de vistas en respuesta a cambios en el modelo de vista subyacente o modelo ....................... 12

de interacción utilizando comandos y comportamientos de interfaz de usuario ............................................ ............................ 13

La implementación de los comandos ................................................ .................................................. ............ 14

La implementación de comportamientos ................................................ .................................................. ..............15

Resumen ................................................. .................................................. ............................ 17

Inyección de dependencia ................................................ .................................................. 18 ..


Introducción a la inyección de dependencias .............................................. ....................................... 18
Registro ................................................. .................................................. ........................ 20
Resolución ................................................. .................................................. ........................... 22
La gestión de la vida útil de los objetos resueltos ............................................ ................................... 22
Resumen ................................................. .................................................. ............................ 23

yo
La comunicación entre los componentes débilmente acoplados ............................................. ..... 24
Introducción a MessagingCenter ............................................... ............................................. 24
La definición de un mensaje ............................................... .................................................. ................ 26
La publicación de un mensaje ............................................... .................................................. ............. 26
La suscripción a un mensaje .............................................. .................................................. ........ 27
Darse de baja de un mensaje .............................................. .................................................. 27
Resumen ................................................. .................................................. ............................ 27

Navegación ................................................. .................................................. .................. 28


La navegación entre las páginas ............................................... .................................................. ..... 29
La creación de la instancia NavigationService .............................................. ......................................... 29
Manejar las solicitudes de navegación ............................................... .................................................. ....... 30

Navegando cuando se inicia la aplicación ............................................ ............................................... 32


Paso de parámetros durante la navegación .............................................. ............................................ 33
La invocación de comportamientos de navegación utilizando .............................................. ............................................... 34

Confirmar o cancelar la navegación .............................................. ................................................ 34


Resumen ................................................. .................................................. ............................ 35

Validación ................................................. .................................................. ................... 36


Especificación de las reglas de validación ............................................... .................................................. ..... 37

Adición de reglas de validación a una propiedad ............................................ .......................................... 38

Desencadenar la validación ................................................ .................................................. ............. 39


Desencadenar la validación manual ............................................... .................................................. ..... 39
Activación de validación cuando propiedades cambian ............................................. ................................. 40

Viendo los errores de validación ............................................... .................................................. ... 40


Destacando un control que contiene datos no válidos ........................................... ............................... 41
Mostrar mensajes de error ............................................... .................................................. ............ 44
Resumen ................................................. .................................................. ............................ 45

gestión de la configuración ................................................ ........................................... 46


Crear una clase de configuración .............................................. .................................................. .......... 46
Al agregar un valor ............................................... .................................................. .................... 47
El enlace de datos de configuración del usuario ............................................. .................................................. ... 48

Resumen ................................................. .................................................. ............................ 50

microservicios en contenedores ................................................ ........................................... 51


Microservicios ................................................. .................................................. ...................... 52
El uso de contenedores ................................................. .................................................. .................. 53
La comunicación entre el cliente y el microservicios ............................................. ..................... 55
La comunicación entre microservicios ............................................... ................................... 56
Resumen ................................................. .................................................. ............................ 58

Autenticacion y autorizacion ............................................... ................................... 59


Autenticación ................................................. .................................................. .................... 59
La emisión de fichas al portador utilizando IdentityServer 4 ............................................ .................................... 60

Añadiendo IdentityServer a una aplicación web ............................................ ...................................... 60


Configuración IdentityServer ................................................ .................................................. .......... 61

ii
Realizar la autenticación ................................................ .................................................. .......... 64
Autorización ................................................. .................................................. ...................... 69
Configuración IdentityServer para realizar la autorización ............................................. ...................... 70
Hacer peticiones de acceso a las API ............................................. .................................................. ...... 71
Resumen ................................................. .................................................. ............................ 71

Acceder a datos remotos ............................................... .................................................. 73 ..


Introducción a la Transferencia de estado representacional ............................................. ......................... 73

El consumo de API REST ............................................... .................................................. ........ 74


Hacer peticiones web ............................................... .................................................. ................... 74
El almacenamiento en caché de datos ................................................ .................................................. ........................ 81

La gestión de los datos de caducidad ............................................... .................................................. ............ 82

Almacenamiento en caché de imágenes ................................................ .................................................. ............................ 82

El aumento de la capacidad de recuperación ................................................ .................................................. ............. 83

patrón de reintentar ................................................ .................................................. ............................... 83


patrón disyuntor ............................................... .................................................. ................. 84
Resumen ................................................. .................................................. ............................ 85

Examen de la unidad ................................................ .................................................. .................. 86


La inyección de dependencia y la unidad de pruebas ............................................. ....................................... 86

Prueba de aplicaciones MVVM ............................................... .................................................. ... 87


Prueba de funcionalidad asincrónica ............................................... ................................................ 88
Las pruebas INotifyPropertyChanged implementaciones ............................................... ........................ 88
mensaje basado en las pruebas de comunicación ............................................. .......................................... 89

Prueba de manejo de excepciones ............................................... .................................................. ........... 89

Las pruebas de validación ................................................ .................................................. ........................ 90

Resumen ................................................. .................................................. ............................ 91

iii
Prefacio

Propósito
Este libro proporciona una guía sobre la creación de aplicaciones empresariales multiplataforma utilizando Xamarin.Forms. Xamarin.Forms es un conjunto de
herramientas de interfaz de usuario multi-plataforma que permite a los desarrolladores crear fácilmente diseños de interfaz de usuario nativas que pueden ser
compartidos a través de plataformas, incluyendo iOS, Android y la plataforma Windows universal (UWP). Proporciona una solución completa para su Negocio a
empleado (B2E), empresa a empresa (B2B) y Business de aplicaciones (B2C) de consumo, proporcionando la capacidad de compartir el código en todas las
plataformas de destino y ayudar a reducir el coste total de propiedad (TCO ).

La guía proporciona una guía de arquitectura para el desarrollo de aplicaciones empresariales Xamarin.Forms adaptables, fáciles de mantener y
comprobables. Se proporciona orientación sobre cómo implementar MVVM, inyección de dependencias, la navegación, validación y gestión de la
configuración, manteniendo al mismo tiempo la articulación flexible. Además, también hay orientación sobre cómo realizar la autenticación y
autorización con IdentityServer, accediendo a datos de microservicios en contenedores, y las pruebas unitarias.

La guía viene con código fuente para el aplicación móvil eShopOnContainers Y el código fuente para el
aplicación de referencia eShopOnContainers . La aplicación móvil eShopOnContainers es una aplicación de empresa multiplataforma desarrollado utilizando
Xamarin.Forms, que conecta con una serie de microservicios en contenedores conocidos como referencia de aplicación los eShopOnContainers. Sin
embargo, los eShopOnContainers de aplicaciones móviles se pueden configurar para consumir datos de los servicios simuladas para aquellos que desean
evitar el despliegue de los microservicios en contenedores.

Lo que queda fuera del alcance de esta guía

Esta guía está dirigida a los lectores que ya están familiarizados con Xamarin.Forms. Para una introducción detallada a Xamarin.Forms,
consulte la documentación Xamarin.Forms en el Centro de desarrolladores de Xamarin, y Creación de Aplicaciones Móviles con
Xamarin.Forms .

La guía es complementaria a .NET microservicios: Arquitectura de Aplicaciones .NET en contenedores , Que se centra en el desarrollo y
despliegue microservicios en contenedores. Otras guías incluyen la pena leer Architecting y desarrollo de aplicaciones Web con ASP.NET
modernos Core y Microsoft Azure , Contenedores estibador del ciclo de vida de aplicaciones con Microsoft Plataforma y Herramientas y Plataforma
de Microsoft y herramientas de desarrollo de aplicaciones móviles .

Quién debería usar esta guía


La audiencia de esta guía se debe principalmente a los desarrolladores y arquitectos que quieran aprender cómo diseñar e implementar
aplicaciones empresariales multiplataforma usando Xamarin.Forms.

iv Prefacio
Una audiencia secundaria es que toman las decisiones técnicas que les gustaría recibir una visión general de la arquitectura y la tecnología antes de
decidir qué enfoque para seleccionar de entre plataformas de desarrollo de aplicaciones empresariales utilizando Xamarin.Forms.

Como usar esta guia


Esta guía se centra en la creación de aplicaciones empresariales multiplataforma utilizando Xamarin.Forms. Como tal, debe ser leído en su totalidad para
proporcionar una base para entender este tipo de aplicaciones y sus consideraciones técnicas. La guía, junto con su aplicación de ejemplo, también puede servir
como un punto de partida o de referencia para la creación de una nueva aplicación de empresa. Utilizar la aplicación de ejemplo asociada como una plantilla para la
nueva aplicación, o para ver cómo organizar partes componentes de una aplicación. A continuación, hacer referencia a esta guía para la orientación arquitectónica.

Siéntase libre de enviar esta guía para los miembros del equipo para ayudar a asegurar una comprensión común del desarrollo de aplicaciones
empresariales multiplataforma usando Xamarin.Forms. Tener todos los que trabajan a partir de un conjunto común de terminología y los principios
subyacentes ayudarán a garantizar una aplicación coherente de patrones y prácticas arquitectónicas.

v Prefacio
CAPÍTULO 1

Introducción
Independientemente de la plataforma, los desarrolladores de aplicaciones empresariales se enfrentan a varios retos:

• requisitos de aplicaciones que pueden cambiar con el tiempo.

• Nuevas oportunidades de negocio y retos.

• retroalimentación continua durante el desarrollo que puede afectar significativamente el alcance y los requisitos de la
aplicación.

Con esto en mente, es importante para construir aplicaciones que se pueden modificar o ampliar fácilmente con el tiempo. Diseñar para tal capacidad de
adaptación puede ser difícil ya que requiere una arquitectura que permite a las partes individuales de la aplicación a desarrollar de forma independiente y
se ensayaron en forma aislada sin afectar al resto de la aplicación.

Muchas de las aplicaciones empresariales son lo suficientemente complejo como para requerir más de un desarrollador. Puede ser un reto importante para decidir
cómo diseñar una aplicación para que varios desarrolladores pueden trabajar con eficacia en diferentes piezas de la aplicación de forma independiente, al tiempo
que garantiza que las piezas encajan perfectamente cuando está integrado en la aplicación.

El enfoque tradicional para el diseño y la construcción de una aplicación da como resultado lo que se conoce como una
monolítico aplicación, donde los componentes están estrechamente vinculadas con la clara separación entre ellos. Por lo general, este enfoque
monolítico conduce a aplicaciones que son difíciles y poco eficiente de mantener, ya que puede ser difícil de resolver errores sin romper otros
componentes en la aplicación, y puede ser difícil añadir nuevas funciones o para reemplazar las funciones existentes.

Un remedio eficaz para estos retos es dividir una aplicación en componentes discretos, débilmente acoplados que pueden
integrarse fácilmente juntos en una aplicación. Dicho enfoque ofrece varias ventajas:

• Permite funcionalidad individuo a ser desarrollado, probado, extendida, y mantenido por diferentes individuos o
equipos.

• Promueve la reutilización y una separación limpia de las preocupaciones entre las capacidades horizontales de la aplicación, tales como la autenticación
y el acceso a los datos, y las capacidades verticales, tales como la funcionalidad de negocio específico de aplicaciones. Esto permite que las
dependencias e interacciones entre los componentes de la aplicación para ser manejados con mayor facilidad.

• Ayuda a mantener una separación de funciones al permitir que diferentes individuos o equipos, para concentrarse en una tarea o una pieza de
funcionalidad de acuerdo a sus conocimientos específicos. En particular, se proporciona una separación más limpia entre la interfaz de usuario y
la lógica de negocio de la aplicación.

Sin embargo, hay muchos problemas que deben ser resueltos al particionar una aplicación en componentes discretos, de forma flexible.
Éstas incluyen:

• Decidir cómo proporcionar una separación limpia de las preocupaciones entre los controles de interfaz de usuario y su lógica. Una de las
decisiones más importantes al crear una aplicación empresarial Xamarin.Forms es la posibilidad de incluir la lógica de negocio en
archivos de código subyacente, o si se debe crear una separación limpia de las preocupaciones entre los controles de interfaz de usuario
y su lógica, con el fin de

1 CAPÍTULO 1 | Introducción
hacer la aplicación más fácil de mantener y comprobable. Para más información, ver Modelo-ViewViewModel .

• La determinación de si usar un contenedor de inyección de dependencia. Dependencia contenedores de inyección de reducir el


acoplamiento de dependencia entre los objetos, proporcionando una instalación para la construcción de instancias de clases con sus
dependencias inyectados, y gestionar su tiempo de vida basado en la configuración del recipiente. Para más información, ver Inyección
de dependencia .

• La elección entre la plataforma proporcionado concurso completo y la comunicación basada en mensajes de acoplamiento flexible
entre los componentes que son un inconveniente para enlazar por objeto y tipo referencias. Para obtener más información, consulte
Introducción a la La comunicación entre los componentes débilmente acoplados .

• Decidir cómo navegar entre las páginas, incluyendo cómo invocar la navegación, y donde la lógica de navegación
debe residir. Para más información, ver Navegación .

• Determinar cómo validar la entrada del usuario para su corrección. La decisión debe incluir la forma de validar la entrada del usuario, y
cómo notificar al usuario acerca de los errores de validación. Para más información, ver Validación .

• Decidir cómo realizar la autenticación, y la forma de proteger los recursos con autorización. Para más información,
ver Autenticacion y autorizacion .

• La determinación de cómo acceder a los datos alejadas de los servicios web, incluyendo cómo recuperar datos de forma fiable, y la forma de
caché de datos. Para más información, ver El acceso a datos remotos .

• Decidir cómo poner a prueba la aplicación. Para más información, ver Examen de la unidad .

Esta guía proporciona orientación sobre estos temas, y se centra en los patrones básicos y la arquitectura para la construcción de una aplicación
empresarial multiplataforma usando Xamarin.Forms. La guía tiene como objetivo ayudar a producir código adaptable, fácil de mantener, y comprobable,
abordando Xamarin.Forms comunes escenarios de desarrollo de aplicaciones de la empresa, y mediante la separación de las preocupaciones de
presentación, lógica de presentación, y las entidades a través del apoyo para el Modelo-Vista-ViewModel (MVVM ) patrón.

Ejemplo de aplicación
Esta guía incluye una aplicación de ejemplo, eShopOnContainers, eso es una tienda online que incluye las siguientes funcionalidades:

• Autenticación y autorización en contra de un servicio de back-end.

• Navegar por un catálogo de camisas, tazas, y otros artículos de marketing.

• Filtrar el catálogo.

• Pedir artículos del catálogo.

• Viendo el historial de pedidos del usuario.

• Configuración de los ajustes.

arquitectura de la aplicación de la muestra

Figura 1-1 proporciona una visión general de alto nivel de la arquitectura de la aplicación de la muestra.

2 CAPÍTULO 1 | Introducción
Figura 1-1: eShopOnContainers arquitectura de alto nivel

Los barcos de aplicación de ejemplo con tres aplicaciones de cliente:

• Una aplicación MVC desarrollado con ASP.NET Core.

• Una sola aplicación de página (SPA) utilizando como base angular 2 y Letra de imprenta. Este enfoque para aplicaciones web
Evita la realización de una ida y vuelta al servidor con cada operación.

• Una aplicación móvil desarrollada con Xamarin.Forms, que es compatible con iOS, Android y la plataforma Windows universal
(UWP).

Para obtener información acerca de las aplicaciones Web, consulte Architecting y desarrollo de aplicaciones Web con ASP.NET
modernos Core y Microsoft Azure .

La aplicación de ejemplo incluye los siguientes servicios de back-end:

• Un microService identidad, que utiliza ASP.NET Core Identidad y IdentityServer.

• Un microService catálogo, que es una basada en datos crear, leer, actualizar, eliminar el servicio (mantenimiento) que consume una base de datos de SQL
Server utilizando ADO.NET Entity Framework Core.

• Un microService pedido, que es un servicio de dominio impulsada que utiliza patrones de diseño de dominio impulsada.

• Un microService cesta, que es un servicio CRUD impulsado por los datos que utiliza Redis caché.

Estos servicios de backend se implementan como microservicios utilizando ASP.NET Core MVC, y se despliegan contenedores como únicos
dentro de un único host acoplable. En conjunto, estos servicios backend se conocen como referencia de aplicación las eShopOnContainers.
aplicaciones cliente se comunican con los servicios de back-end a través de una interfaz web Transferencia de estado representacional (REST).
Para obtener más información acerca de microservicios y estibador, véase microservicios en contenedores .

Para obtener información acerca de la implementación de los servicios de back-end, consulte .NET microservicios: Arquitectura de
Aplicaciones .NET en contenedores .

3 CAPÍTULO 1 | Introducción
Aplicación movil

Esta guía se centra en la construcción de la empresa multiplataforma aplicaciones usando Xamarin.Forms, y utiliza los eShopOnContainers
aplicación móvil como un ejemplo. La Figura 1-2 muestra las páginas de los eShopOnContainers aplicación móvil que proporcionan la
funcionalidad esbozado antes.

Figura 1-2: Los eShopOnContainers de aplicaciones móviles

La aplicación móvil consume los servicios de servidor proporcionados por la aplicación de referencia eShopOnContainers. Sin embargo, se puede
configurar para consumir datos de los servicios simuladas para aquellos que desean evitar el despliegue de los servicios de back-end.

La aplicación móvil eShopOnContainers ejerce las siguientes funciones Xamarin.Forms:

• XAML

• controles

• encuadernaciones

• convertidores

• estilos

4 CAPÍTULO 1 | Introducción
• animaciones

• comandos

• comportamientos

• disparadores

• efectos

• renderizadores personalizados

• MessagingCenter

• Los controles personalizados

Para obtener más información sobre esta funcionalidad, consulte la documentación Xamarin.Forms en el Centro de desarrolladores de Xamarin, y Creación
de Aplicaciones Móviles con Xamarin.Forms .

Además, se proporcionan pruebas de unidad para algunas de las clases de la aplicación móvil eShopOnContainers.

solución de aplicaciones móviles

La solución de aplicaciones móviles eShopOnContainers organiza el código fuente y otros recursos en los proyectos. Todos los
proyectos utilizan carpetas para organizar el código fuente y otros recursos en categorías. La siguiente tabla muestra los proyectos
que componen los eShopOnContainers aplicación móvil:

Proyecto Descripción

eShopOnContainers.Core Este proyecto es el proyecto de biblioteca de clases portátil (PCL) que contiene el

código compartido y la interfaz de usuario compartida.

eShopOnContainers.Droid Este proyecto tiene un código específico Android y es el punto de entrada de la

aplicación para Android.

eShopOnContainers.iOS Este proyecto tiene un código específico iOS y es el punto de entrada para la

aplicación de iOS.

eShopOnContainers.UWP Este proyecto tiene la plataforma de Windows universal (UWP) código específico y

es el punto de entrada para la aplicación de Windows.

eShopOnContainers.TestRunner.Droid Este proyecto es el corredor de prueba de Android para el proyecto


eShopOnContainers.UnitTests.

eShopOnContainers.TestRunner.iOS Este proyecto es el corredor de prueba de iOS para el

proyecto eShopOnContainers.UnitTests.

eShopOnContainers.TestRunner.Windows Este proyecto es la prueba de plataforma Windows universal


corredor para el proyecto eShopOnContainers.UnitTests.
eShopOnContainers.UnitTests Este proyecto contiene las pruebas unitarias para el

proyecto eShopOnContainers.Core.

Las clases de los eShopOnContainers aplicación móvil se puede volver a utilizarse en cualquier Xamarin.Forms aplicación con poca o ninguna modificación.

proyecto eShopOnContainers.Core
El proyecto eShopOnContainers.Core PCL contiene las siguientes carpetas:

5 CAPÍTULO 1 | Introducción
Carpeta Descripción

animaciones Contiene clases que permiten animaciones para ser consumidos en XAML.
comportamientos Contiene los comportamientos que están expuestos a la vista clases.

controles Contiene controles personalizados utilizados por la aplicación.

convertidores Contiene convertidores de valores que se aplican a una lógica personalizada de unión.

efectos Contiene el EntryLineColorEffect clase, que se utiliza para cambiar el color del borde de concreto Entrada
controles.
excepciones Contiene la costumbre ServiceAuthenticationException.
extensiones Contiene métodos de extensión para el VisualElement y IEnumerable <T> clases.
ayudantes Contiene clases de ayuda para la aplicación.

modelos Contiene las clases del modelo para la aplicación.

propiedades contiene AssemblyInfo.cs, un archivo de metadatos NET.


Servicios Contiene interfaces y clases que implementan los servicios que se prestan a la aplicación.

disparadores Contiene el BeginAnimation gatillo, que se utiliza para invocar una animación en XAML.

validaciones Contiene clases que participan en la validación de entrada de datos.

ViewModels Contiene la lógica de la aplicación que está expuesto a las páginas. Puntos de vista

Contiene las páginas de la aplicación.

proyectos de plataforma

Los proyectos de plataforma contienen implementaciones efecto, las implementaciones intérprete personalizado y otros recursos específicos de la
plataforma.

Resumen
Multiplataforma aplicación móvil de herramientas y plataformas de desarrollo de Xamarin proporcionan una solución completa para B2E, B2B, y aplicaciones de
cliente móvil B2C, proporcionando la capacidad de compartir el código en todas las plataformas de destino (iOS, Android y Windows) y ayudando a reducir el coste
total de propiedad. Las aplicaciones pueden compartir su código de usuario y la lógica de la interfaz de aplicaciones, al tiempo que conserva el aspecto y la
sensación plataforma nativa.

Los desarrolladores de aplicaciones empresariales se enfrentan a varios retos que pueden alterar la arquitectura de la aplicación durante el desarrollo. Por lo
tanto, es importante para construir una aplicación para que pueda ser modificado o ampliado con el tiempo. Diseñar para tal capacidad de adaptación puede ser
difícil, pero por lo general implica la partición de una aplicación en componentes discretos, débilmente acoplados que pueden integrarse fácilmente juntos en una
aplicación.

6 CAPÍTULO 1 | Introducción
CAPÍTULO 2

MVVM
La experiencia Xamarin.Forms desarrollador normalmente implica la creación de una interfaz de usuario en XAML y, a continuación, añadir código subyacente que
funciona en la interfaz de usuario. A medida que se modifican aplicaciones, y crecen en tamaño y alcance, los problemas de mantenimiento complejas pueden surgir.
Estos problemas incluyen el acoplamiento apretado entre los controles de interfaz de usuario y la lógica de negocio, lo que aumenta el costo de hacer modificaciones de
interfaz de usuario, y la dificultad de la unidad de pruebas tal código.

El (MVVM) patrón Model-View-ViewModel ayuda a separar limpiamente la lógica de negocio y presentación de una solicitud desde su interfaz de usuario (UI). El
mantenimiento de una separación limpia entre la lógica de la aplicación y la interfaz de usuario ayuda a hacer frente a numerosos problemas de desarrollo y
puede hacer una aplicación más fácil de probar, mantener y evolucionar. También puede mejorar en gran medida las oportunidades de reutilización de código y
permite a los desarrolladores y diseñadores de interfaz de usuario para mayor facilidad colaboran en el desarrollo de sus respectivas partes de una aplicación.

El patrón MVVM
Hay tres componentes principales en el patrón MVVM: el modelo, la vista, y el modelo de vista. Cada uno tiene un propósito
distinto. La figura 2-1 muestra las relaciones entre los tres componentes.

Figura 2-1: El patrón MVVM

Además de comprender las responsabilidades de cada uno de los componentes, también es importante entender cómo interactúan
entre sí. En un nivel alto, la vista "conoce" el modelo de vista, y el modelo de vista "conoce" el modelo, pero el modelo no es
consciente del modelo de vista, y el modelo de vista no es consciente de la vista. Por lo tanto, el modelo de vista aísla la vista desde el
modelo, y permite que el modelo de evolucionar independientemente de la vista.

Los beneficios de usar el patrón MVVM son los siguientes:

• Si hay una aplicación modelo existente que encapsula la lógica de negocio existente, puede ser difícil o arriesgada para
cambiarlo. En este escenario, el modelo de vista actúa como un adaptador para las clases del modelo y le permite evitar
hacer cambios importantes en el código del modelo.

7 CAPÍTULO 2 | MVVM
• Los desarrolladores pueden crear pruebas unitarias para el modelo de vista y el modelo, sin necesidad de utilizar la vista. Las pruebas unitarias para
el modelo de vista pueden ejercer exactamente la misma funcionalidad que se utiliza por la vista.

• La aplicación de interfaz de usuario puede ser rediseñado sin tocar el código, siempre que la vista se implementa completamente en
XAML. Por lo tanto, una nueva versión de la vista debe trabajar con el modelo de vista existente.

• Los diseñadores y los desarrolladores pueden trabajar de forma independiente y al mismo tiempo en sus componentes durante el proceso de desarrollo.
Los diseñadores pueden centrarse en la vista, mientras que los desarrolladores pueden trabajar en los componentes de vista del modelo y modelo.

La clave para usar MVVM se encuentra efectivamente en la comprensión de cómo factorizar código de aplicación en las clases correctas, y en la
comprensión de cómo interactúan las clases. Las siguientes secciones discuten las responsabilidades de cada una de las clases en el patrón
MVVM.

Ver
La vista es responsable de definir la estructura, el diseño y la apariencia de lo que el usuario ve en la pantalla. Idealmente, cada vista se define en
XAML, con un código subyacente limitada que no contiene la lógica de negocio. Sin embargo, en algunos casos, el código subyacente puede
contener lógica de interfaz de usuario que implementa el comportamiento visual que es difícil de expresar en XAML, tales como animaciones.

En una aplicación Xamarin.Forms, una vista es típicamente una Página- derivado o ContentView- clase derivada. Sin embargo, puntos de vista también se pueden
representar por una plantilla de datos, que especifica los elementos de interfaz de usuario para ser utilizados para representar visualmente un objeto cuando se
visualiza. Una plantilla de datos como una vista no tiene ningún código subyacente, y está diseñado para unirse a un tipo de vista del modelo específico.

Propina: Evitar la activación y desactivación elementos de la interfaz en el código subyacente

Asegúrese de que los modelos de vista son responsables de definir los cambios de estado lógicas que afectan a algunos aspectos de la pantalla de la
vista, como si un comando está disponible, o una indicación de que está pendiente de una operación. Por lo tanto, activar y desactivar elementos de la
interfaz mediante la unión a ver las propiedades de modelo, en lugar de habilitar y deshabilitar en código subyacente.

Hay varias opciones para la ejecución de código en el modelo de vista en respuesta a las interacciones en la vista, tales como un clic de botón o
selección de elementos. Si un control compatible con los comandos, el control de las Mando
propiedad puede ser enlazado a datos a una Yo ordeno propiedad en el modelo de vista. Cuando se invoca la orden del control, se ejecutará el
código en el modelo de vista. Además de los comandos, los comportamientos se pueden unir a un objeto en la vista y pueden escuchar, ya sea
para un comando para invocar o evento que se planteó. En respuesta, el comportamiento puede entonces invocar una Yo ordeno en el modelo de
vista o un método en el modelo de vista.

ViewModel
El modelo de vista implementa propiedades y comandos para que la vista puede enlazar datos a, y notifica a la vista de cualquier cambio de
estado a través de eventos de notificación de cambio. Las propiedades y los comandos que el modelo de vista proporciona definir la funcionalidad
para ser ofrecido por la interfaz de usuario, pero la vista determina cómo esa funcionalidad se va a visualizar.

Propina: Mantener la interfaz de usuario sensible, con operaciones asíncronas

Las aplicaciones móviles deben mantener el hilo de interfaz de usuario desbloqueado para mejorar la percepción del usuario de rendimiento. Por lo tanto, en el
modelo de vista, utilizar métodos asincrónicos para las operaciones de E / S y provocar eventos para notificar de forma asíncrona puntos de vista de los cambios de
propiedad.

8 CAPÍTULO 2 | MVVM
El modelo de vista es también responsable de la coordinación de las interacciones de la vista con las clases del modelo que se requieren. Hay
normalmente una relación de uno a muchos entre el modelo y la vista de las clases del modelo. El modelo de vista puede optar por exponer a las clases
del modelo directamente a la vista para que los controles en la vista de los datos se pueden unir directamente a ellos. En este caso, tendrá que ser
diseñado el modelo de clases para apoyar el enlace de datos y cambiar los eventos de notificación.

Cada modelo de vista proporciona datos de un modelo en una forma que la vista puede consumir fácilmente. Para lograr esto, el modelo de vista a
veces realiza la conversión de datos. La colocación de esta conversión de datos en el modelo de vista es una buena idea, ya que proporciona
propiedades que la vista se puede unir a. Por ejemplo, el modelo de vista podría combinar los valores de dos propiedades para que sea más fácil para
la visualización por la vista.

Propina: Centralizar conversiones de datos en una capa de conversión

También es posible utilizar convertidores como una capa de conversión de datos separada que se encuentra entre el modelo de vista y la vista.
Esto puede ser necesario, por ejemplo, cuando los datos requiere un formato especial que el modelo de vista no proporciona.

Para que el modelo de vista para participar en los datos de dos vías de unión con la vista, sus propiedades deben elevar la PropertyChanged evento.
Ver modelos satisfacen este requisito mediante la implementación de la
INotifyPropertyChanged interfaz, y el aumento de la PropertyChanged evento cuando se cambia una propiedad.

Para las colecciones, la vista de usar ObservableCollection <T> está provisto. Esta colección implementa colección notificación
cambiada, aliviando al programador de tener que poner en práctica el
INotifyCollectionChanged interfaz en colecciones.

Modelo
clases del modelo son clases no visuales que encapsulan los datos de la aplicación. Por lo tanto, el modelo puede ser considerado como que
representa modelo de dominio de la aplicación, que generalmente incluye un modelo de datos junto con los negocios y la lógica de validación.
Ejemplos de objetos de modelo incluyen la transferencia de objetos de datos (DTO), Plain Old CLR Objetos (Pocos), y entidad generado y objetos
proxy.

clases del modelo se utilizan normalmente en conjunción con servicios o repositorios que encapsulan el acceso a datos y el almacenamiento en caché.

Conexión de modelos de vista a las vistas


Ver modelos pueden conectarse a puntos de vista por medio de la capacidad de enlace de datos de Xamarin.Forms. Hay muchos enfoques que se
pueden utilizar para construir puntos de vista y modelos de vista y asociarlos en tiempo de ejecución. Estos enfoques se dividen en dos categorías,
conocidas como vista primera composición, y vista de modelo primera composición. La elección entre la vista primera composición y vista del modelo
primera composición es una cuestión de preferencia y complejidad. Sin embargo, todos los enfoques comparten el mismo objetivo, que es para el fin
de tener un modelo de vista asignado a su BindingContext propiedad.

Con vista primera composición de la aplicación se compone de puntos de vista conceptual que se conectan a los modelos de vista de los que
dependen. La principal ventaja de este enfoque es que hace que sea fácil de construir de forma flexible, la unidad de aplicaciones comprobables debido
a que los modelos de vista no tienen dependencia de los propios puntos de vista. También es fácil de entender la estructura de la aplicación siguiendo
su estructura visual, en lugar de tener que realizar un seguimiento de la ejecución de código para entender cómo se crean y se asocian clases.
Además, ver primera construcción se alinea con el sistema de navegación Xamarin.Forms que es responsable de la construcción de páginas cuando se
produce la navegación, lo que hace una vista de modelo primer complejo composición y desalineada con la plataforma.

9 CAPÍTULO 2 | MVVM
Con vista modelo primera composición la aplicación está conceptualmente compone de modelos de vista, con un servicio de ser responsable de la
localización de la vista para un modelo de vista. Ver modelo de primera composición se siente más natural para algunos desarrolladores, ya que la
creación de la vista puede abstraerse de distancia, que les permite centrarse en la estructura no-UI lógica de la aplicación. Además, permite que los
modelos de visualización para ser creados por otros modelos de vista. Sin embargo, este enfoque es a menudo complejo y puede llegar a ser difícil de
entender cómo se crean y se asocian las diversas partes de la aplicación.

Propina: Mantener la visión modelos y puntos de vista independientes

La unión de los puntos de vista de una propiedad en una fuente de datos debe ser la dependencia principal de la vista sobre su correspondiente modelo
de vista. Específicamente, no hacen referencia a tipos de vista, tales como Botón y
Vista de la lista, de modelos de vista. Siguiendo los principios descritos aquí, ver modelos pueden ser probados en el aislamiento, por lo tanto, reduciendo la
probabilidad de defectos de software mediante la limitación de su alcance.

Las siguientes secciones discuten los principales enfoques para la conexión de modelos de vista a las vistas.

La creación de un modelo de vista declarativa

El enfoque más sencillo es para el fin de crear una instancia declarativa su correspondiente modelo de vista en XAML. Cuando se
construye la vista, también se construyó el correspondiente objeto vista del modelo. Este enfoque se demuestra en el ejemplo de
código siguiente:

< Pagina de contenido ... xmlns: = locales "CLR-espacio de nombres: eShop" >
< ContentPage.BindingContext >
< locales: LoginViewModel />
</ ContentPage.BindingContext >
. . .
</ Pagina de contenido >

Cuando el Pagina de contenido se crea, una instancia de la LoginViewModel está construido de forma automática y establece como el punto de vista de BindingContext.

Esta construcción declarativa y asignación del modelo de vista por la vista, tiene la ventaja de que es simple, pero tiene la desventaja de
que requiere un constructor predeterminado (parámetro-menos) en el modelo de vista.

La creación de un modelo de vista mediante programación

Una vista puede tener código en el archivo de código subyacente que da lugar a la modelo de vista que se le asigne a su propiedad
BindingContext. Esto se logra a menudo en el constructor de la vista, como se muestra en el ejemplo de código siguiente:

público LoginView ()
{
InitializeComponent ();
BindingContext = nuevo LoginViewModel (NavigationService);
}

La construcción programática y la asignación del modelo de vista dentro de código subyacente de la vista tiene la ventaja de que es muy
sencillo. Sin embargo, la principal desventaja de este enfoque es que la vista necesita proporcionar el modelo de vista con las dependencias
requeridas. El uso de un contenedor de inyección de dependencias puede ayudar a mantener el acoplamiento débil entre la vista y vista del
modelo. Para más información, ver Inyección de dependencia .

10 CAPÍTULO 2 | MVVM
Creación de una vista definida como una plantilla de datos

Una vista puede definirse como una plantilla de datos y asociada con un tipo de vista del modelo. plantillas de datos pueden ser definidos como
recursos, o pueden ser definidos en línea dentro del control que mostrará el modelo de vista. El contenido del control es la instancia de vista del
modelo, y la plantilla de datos se utiliza para representar visualmente. Esta técnica es un ejemplo de una situación en la que el modelo de vista se
crea una instancia primero, seguido por la creación de la vista.

crear automáticamente un modelo de vista con una vista de modelo localizador

Una vista del modelo de localización es una clase personalizada que gestiona la creación de instancias de modelos de vista y su asociación a puntos
de vista. En los eShopOnContainers aplicación móvil, la ViewModelLocator clase tiene una propiedad adjunta, AutoWireViewModel, que se utiliza para
asociar modelos de vista con vistas. En XAML de la vista, esta propiedad adjunta se establece en cierto para indicar que el modelo de vista se debe
conectar automáticamente a la vista, como se muestra en el ejemplo de código siguiente:

viewModelBase: ViewModelLocator.AutoWireViewModel = "true"

los AutoWireViewModel la propiedad es una propiedad enlazable que se inicializa a falso, y cuando su valor cambia el OnAutoWireViewModelChanged
controlador de eventos se llama. Este método resuelve el modelo de vista para la vista. El ejemplo de código siguiente muestra
cómo se logra esto:

privado hoyo estatico OnAutoWireViewModelChanged ( bindableObject enlazable, objeto valor antiguo, objeto norte
ewValue)
{
var view = enlazable como Elemento ;
Si (Vista == nulo )
{
regreso ;
}

var ViewType = view.GetType ();


var viewName = viewType.FullName.Replace ( ".Puntos de vista." , ".ViewModels". );
var viewAssemblyName = viewType.GetTypeInfo () Assembly.FullName.;
var viewModelName = cuerda .Formato(
CultureInfo .InvariantCulture, "{0} Modelo, {1}" , ViewName, viewAssemblyName);

var viewModelType = Tipo .GetType (viewModelName);


Si (ViewModelType == nulo )
{
regreso ;
}
var ViewModel = _container.Resolve (viewModelType);
view.BindingContext = modelo de vista;
}

los OnAutoWireViewModelChanged método intenta resolver el modelo de vista utilizando un enfoque conventionbased. Este
convenio supone que:

• Ver los modelos están en la misma Asamblea que los tipos de vista.

• Las vistas son en un espacio de nombres de niño .Views.

• Ver los modelos están en un espacio de nombres .ViewModels niño.

• Ver nombres de modelo se corresponden con los nombres de vista y terminan con "modelo de vista".

11 CAPÍTULO 2 | MVVM
Finalmente, el OnAutoWireViewModelChanged método establece el BindingContext del tipo de vista al tipo de vista modelo resuelto. Para obtener más
información sobre cómo resolver el punto de vista del tipo de modelo, véase Resolución .

Este enfoque tiene la ventaja de que una aplicación tiene una única clase que es responsable de la creación de instancias de modelos de vista y
su conexión con vistas.

Propina: Utilice una vista de modelo de localización para la facilidad de sustitución

Un localizador de vista del modelo también se puede utilizar como un punto de sustitución para las implementaciones alternativas de dependencias, como para
los datos de pruebas de unidad o en tiempo de diseño.

Actualización de vistas en respuesta a cambios en el modelo de


vista subyacente o modelo
Todas las vistas modelo y de sus clases que se puede acceder a una vista deberían aplicar el
INotifyPropertyChanged interfaz. La implementación de esta interfaz en un modelo de vista o una clase de modelo permite la clase para proporcionar

notificaciones de cambio a cualquier controles enlazados a datos en la vista cuando cambia el valor de la propiedad subyacentes.

App debe Diseñada para el uso correcto de la notificación de cambio de propiedad, mediante el cumplimiento de los siguientes requisitos:

• Siempre levantando una PropertyChanged evento si cambia el valor de una propiedad pública. No asuma que el aumento de la PropertyChanged
evento puede ser ignorado debido al conocimiento de cómo se produce la unión XAML.

• Siempre levantando una PropertyChanged evento para cualquier propiedades calculadas cuyos valores son utilizados por otras propiedades en
el modelo de vista o modelo.

• Siempre elevar el PropertyChanged evento al final del método que hace un cambio de propiedad, o cuando el objeto es conocido
por ser en un estado seguro. Provocar el evento interrumpe la operación por la invocación de controladores del evento
síncrono. Si esto ocurre en medio de una operación, que podría exponer el objeto a funciones de llamada cuando está en un
estado inseguro, parcialmente actualizada. Además, es posible que los cambios en cascada a ser provocados por

PropertyChanged eventos. cambios en cascada generalmente requieren cambios a ser completa antes del cambio en
cascada es seguro para ejecutar.

• Sin levantar una PropertyChanged caso si la propiedad no cambia. Esto significa que usted debe comparar los valores
antiguos y nuevos antes de levantar la PropertyChanged evento.

• Sin levantar la PropertyChanged evento durante el constructor de un modelo de vista si se están inicializando una propiedad.
controles enlazados a datos en la vista no se han suscrito para recibir notificaciones de cambio en este punto.

• Sin levantar más de una PropertyChanged evento con el mismo argumento de nombre de propiedad dentro de una única invocación
síncrona de un método público de una clase. Por ejemplo, dada una Número de items propiedad cuyo almacén de respaldo es la _ número
de items campo, si un método incrementos _ número de items cincuenta veces durante la ejecución de un bucle, sólo se debe aumentar
la notificación de cambios en la propiedad Número de items propiedad una vez, después de todo el trabajo se haya completado. Para
los métodos asíncronos, elevar el PropertyChanged evento para un nombre de propiedad dada en cada segmento síncrono de una
cadena continuación asíncrona.

Los eShopOnContainers aplicación móvil que utiliza el ExtendedBindableObject clase para proporcionar notificaciones de cambio, que se
muestra en el ejemplo de código siguiente:

12 CAPÍTULO 2 | MVVM
público clase abstracta ExtendedBindableObject : bindableObject
{
public void RaisePropertyChanged < T > ( Expresión < Func < T >> propiedad)
{
var name = GetMemberInfo (propiedad) .Nombre;
OnPropertyChanged (nombre);
}

privado MemberInfo GetMemberInfo ( Expresión expresión)


{
. . .
}
}

Xamarin.Form de bindableObject clase implementa la INotifyPropertyChanged interfaz, y proporciona una OnPropertyChanged método.
los ExtendedBindableObject clase proporciona el
RaisePropertyChanged método a invocar la notificación de cambio de propiedad, y al hacerlo, utiliza la funcionalidad
proporcionada por el bindableObject clase.

Cada clase de vista del modelo en la aplicación móvil eShopOnContainers deriva de la ViewModelBase clase, que a su vez deriva de la ExtendedBindableObject
clase. Por lo tanto, cada clase de vista modelo utiliza el RaisePropertyChanged método en el ExtendedBindableObject clase para proporcionar
notificación de cambio de propiedad. El ejemplo de código siguiente muestra cómo la aplicación móvil eShopOnContainers invoca
notificación de cambio de propiedad mediante el uso de una expresión lambda:

public bool IsLogin


{
obtener

{
regreso _isLogin;
}
conjunto

{
_isLogin = valor ;
RaisePropertyChanged (() => IsLogin);
}
}

Tenga en cuenta que el uso de una expresión lambda de esta manera implica un pequeño costo de rendimiento debido a que la expresión lambda tiene
que ser evaluado para cada llamada. Aunque el costo de rendimiento es pequeña y normalmente no tendría un impacto de una aplicación, los costos se
acumulan cuando hay muchas notificaciones de cambio. Sin embargo, el beneficio de este enfoque es que proporciona en tiempo de compilación
seguridad de tipos y apoyo refactorización al cambiar el nombre de propiedades.

interacción de interfaz de usuario mediante comandos y comportamientos


En aplicaciones móviles, las acciones suelen ser invocados en respuesta a una acción del usuario, como un clic de botón, que puede ser implementado
mediante la creación de un controlador de eventos en el archivo de código subyacente. Sin embargo, en el patrón MVVM, la responsabilidad de la ejecución de
la acción recae sobre el modelo de vista, y el código de la colocación en el código subyacente debe ser evitado.

Comandos proporcionan una manera conveniente de representar acciones que se pueden unir a los controles de la interfaz de usuario. Ellos
encapsulan el código que implementa la acción, y ayudan a mantenerlo desacoplada de su representación visual en la vista. Xamarin.Forms
incluye controles que se pueden conectar de forma declarativa a un comando, y estos controles invocará el comando cuando el usuario
interactúa con el control.

13 CAPÍTULO 2 | MVVM
También comportamientos que los controles sean conectados de forma declarativa a un comando. Sin embargo, los comportamientos se pueden utilizar para
invocar una acción que se asocia con una serie de eventos generados por un control. Por lo tanto, los comportamientos frente a muchos de los mismos
escenarios como los controles de mando habilitado, al tiempo que proporciona un mayor grado de flexibilidad y control. Además, los comportamientos también se
pueden usar para asociar objetos de comando o métodos con controles que no fueron diseñados específicamente para interactuar con los comandos.

La implementación de los comandos

Ver modelos típicamente exponen propiedades de comando, para la unión de la vista, que son instancias de objetos que implementan
la Yo ordeno interfaz. Una serie de controles Xamarin.Forms proporcionar una
Mando propiedad, que pueden ser los datos ligados a una Yo ordeno objeto proporcionado por el modelo de vista. los
Yo ordeno interfaz define una Ejecutar método, que encapsula la operación en sí, una
CanExecute método, que indica si el comando se puede invocar, y una
CanExecuteChanged evento que ocurre cuando se producen cambios que afectan a si el comando debe ejecutarse. los Mando y Comando
<T> clases, proporcionados por Xamarin.Forms, poner en práctica el Yo ordeno
interfaz, donde T es el tipo de los argumentos para Ejecutar y CanExecute.

Dentro de un modelo de vista, no debe haber un objeto de tipo Mando o Comando <T> para cada propiedad pública en la vista del
modelo de tipo Yo ordeno. los Mando o Comando <T> constructor requiere una
Acción objeto de devolución de llamada que se llama cuando el ICommand.Execute método se invoca. los
CanExecute método es un parámetro constructor opcional, y es una Func que devuelve una bool.

El código siguiente muestra cómo una Mando ejemplo, que representa un comando de registro, se construye mediante la
especificación de un delegado a la Registro método vista de modelo:

público Yo ordeno RegisterCommand => nuevo Mando (Registro);

El comando está expuesta a la vista a través de una propiedad que devuelve una referencia a una Yo ordeno.
Cuando el Ejecutar método se llama en el Mando objeto, simplemente reenvía la llamada al método en el modelo de vista a través
del delegado que se especificó en el Mando constructor.

Un método asíncrono puede ser invocado por un comando mediante el asíncrono y esperar palabras clave cuando se especifica el comando de Ejecutar
delegar. Esto indica que la devolución de llamada es una Tarea y debe ser esperado. Por ejemplo, el código siguiente muestra cómo una Mando ejemplo,
que representa un comando de inicio de sesión, se construye mediante la especificación de un delegado a la SignInAsync método vista de
modelo:

público Yo ordeno SignInCommand => nuevo Mando ( asíncrono () => esperar SignInAsync ());

Los parámetros pueden ser pasados ​a la Ejecutar y CanExecute acciones mediante el uso de la Comando <T> clase de ejecutar el
comando. Por ejemplo, el código siguiente muestra cómo una Comando <T> ejemplo se utiliza para indicar que la NavigateAsync método
requerirá un argumento de tipo cuerda:

público Yo ordeno NavigateCommand => nuevo Mando < cuerda > (NavigateAsync);

Tanto en el Mando y Comando <T> clases, el delegado de la CanExecute método en cada constructor es opcional. Si no se
especifica un delegado, el Mando volverá cierto para CanExecute.
Sin embargo, el modelo de vista puede indicar un cambio en el comando de CanExecute estado llamando a la
ChangeCanExecute método en el Mando objeto. Esto hace que el CanExecuteChanged evento que se levantó. Los controles en la interfaz de
usuario que están obligados a la orden luego actualizar su estado habilitado para reflejar la disponibilidad del comando enlazado a datos.

14 CAPÍTULO 2 | MVVM
La invocación de comandos desde un punto de vista

El ejemplo de código siguiente muestra cómo una Cuadrícula en el LoginView se une a la RegisterCommand en el LoginViewModel clase
utilizando una TapGestureRecognizer ejemplo:

< Rejilla rejilla. columna = "1" HorizontalOptions = "Centrar" >


< Etiqueta text = "REGISTRO" TextColor = "Gris" />
< Grid.GestureRecognizers >
< TapGestureRecognizer comando = "{Binding} RegisterCommand" NumberOfTapsRequired = "1" />
</ Grid.GestureRecognizers >
</ Cuadrícula >

Un parámetro de comando también puede opcionalmente define utilizando la CommandParameter propiedad. El tipo del
argumento esperado se especifica en el Ejecutar y CanExecute métodos diana. los
TapGestureRecognizer invocará automáticamente el comando de destino cuando el usuario interactúa con el control adjunto. El
parámetro de comando, si se proporciona, se pasa como argumento de la orden de Ejecutar delegar.

La implementación de comportamientos

Comportamientos permiten funcionalidad que se añade a los controles de interfaz de usuario sin tener que subclase ellos. En cambio, la funcionalidad
se implementa en una clase de comportamiento y unido al control como si fuera parte del control de sí mismo. Comportamientos le permiten
implementar código que normalmente tendría que escribir como código subyacente, ya que interactúa directamente con la API del control, de tal
manera que se puede conectar de forma concisa con el control, y se envasa para su reutilización en más de un punto de vista o aplicación. En el
contexto de MVVM, comportamientos son un enfoque útil para la conexión de los controles a los comandos.

Un comportamiento que se une a un control a través de propiedades adjuntas se conoce como una comportamiento adjunto.
El comportamiento puede entonces utilizar la API expuesta del elemento al que está unido para agregar funcionalidad a que el control, u otros
controles, en el árbol visual de la vista. Los eShopOnContainers aplicación móvil contiene la LineColorBehavior clase, que es un comportamiento
adjunto. Para obtener más información acerca de este comportamiento, consulte Viendo los errores de validación .

Un comportamiento Xamarin.Forms es una clase que deriva de la Comportamiento o Comportamiento <T> clase, donde T es el tipo de control a la
que debe aplicarse el comportamiento. Estas clases proporcionan OnAttachedTo y
OnDetachingFrom métodos, que tienen que ser cambiados para proporcionar lógica que será ejecutado cuando el comportamiento está
unido a y separado de controles.

En los eShopOnContainers aplicación móvil, la BindableBehavior <T> clase se deriva de la


Comportamiento <T> clase. El propósito de BindableBehavior <T> clase es proporcionar una clase base para Xamarin.Forms comportamientos
que requieren la BindingContext del comportamiento que se establece en el control adjunto.

los BindableBehavior <T> clase proporciona una Overridable OnAttachedTo método que establece el
BindingContext del comportamiento, y un reemplazable OnDetachingFrom método que limpia la
BindingContext. Además, la clase almacena una referencia al control adjunta en el
AssociatedObject propiedad.

Los eShopOnContainers aplicación móvil incluye una EventToCommandBehavior clase, que ejecuta un comando en respuesta
a un evento que ocurre. Esta clase se deriva de la BindableBehavior <Ver>
la clase de modo que el comportamiento puede unirse y ejecutar una Yo ordeno especificado por una Mando propiedad cuando se consume el
comportamiento. El ejemplo de código siguiente muestra el EventToCommandBehavior
clase:

15 CAPÍTULO 2 | MVVM
clase pública EventToCommandBehavior : BindableBehavior < Ver >
{
. . .
protegido override void OnAttachedTo ( Ver visualElement)
{
base .OnAttachedTo (visualElement);

var . Eventos = AssociatedObject.GetType () GetRuntimeEvents () toArray ().;


Si (Events.Any ())
{
_eventInfo = events.FirstOrDefault (e => e.Name == eventName);
Si (_eventInfo == nulo )
arrojar nueva ArgumentException ( cuerda .Formato(
"EventToCommand: No se puede encontrar ningún evento denominado '{0}' en el tipo de adjunto" ,
Nombre del evento));

AddEventHandler (_eventInfo, AssociatedObject, OnFired);


}
}

protegido override void OnDetachingFrom ( Ver ver)


{
Si (_handler! = nulo )
_eventInfo.RemoveEventHandler (AssociatedObject, _handler);

base .OnDetachingFrom (vista);


}

private void AddEventHandler (


EventInfo EventInfo, objeto ít, Acción < objeto , EventArgs > Acción)
{
. . .
}

private void OnFired ( objeto remitente, EventArgs eventArgs)


{
. . .
}
}

los OnAttachedTo y OnDetachingFrom métodos se utilizan para registrar y cancelar el registro de un controlador de eventos para el evento definido
en el Nombre del evento propiedad. Entonces, cuando se activa el evento, el OnFired
método es invocado, que ejecuta el comando.

La ventaja de utilizar la EventToCommandBehavior para ejecutar un comando cuando un evento se desencadena, es que los comandos se pueden asociar
con los controles que no fueron diseñados para interactuar con los comandos. Además, esta se mueve código de gestión de eventos para ver los
modelos, en los que se puede probar la unidad.

La invocación de comportamientos desde una vista

los EventToCommandBehavior es particularmente útil para la fijación de un comando a un control que no admite comandos. Por
ejemplo, el ProfileView utiliza el EventToCommandBehavior para ejecutar la OrderDetailCommand cuando el ItemTapped desencadena el
evento en el Vista de la lista que enumera las órdenes del usuario, como se muestra en el siguiente código:

< Vista de la lista >

< ListView.Behaviors >


< comportamientos: EventToCommandBehavior
eventName = "ItemTapped"
comando = "{Binding} OrderDetailCommand"
EventArgsConverter = "{} StaticResource ItemTappedEventArgsConverter" />

dieciséis CAPÍTULO 2 | MVVM


</ ListView.Behaviors >
. . .
</ Vista de la lista >

En tiempo de ejecución, la EventToCommandBehavior responderá a la interacción con el Vista de la lista. Cuando se selecciona un elemento en
el Vista de la lista, el ItemTapped evento se disparará, que ejecutará el
OrderDetailCommand en el ProfileViewModel. Por defecto, los argumentos del evento para el evento se pasan al comando. Estos
datos se convierten como se le hace pasar entre la fuente y de destino por el convertidor especificado en el EventArgsConverter propiedad,
que devuelve el ít. del Vista de la lista
Del I temTappedEventArgs. Por lo tanto, cuando el OrderDetailCommand se ejecuta, el seleccionado
Orden se pasa como un parámetro para el domicilio Acción.

Para obtener más información sobre comportamientos, consulte comportamientos en el Centro de desarrolladores de Xamarin.

Resumen
El (MVVM) patrón Model-View-ViewModel ayuda a separar limpiamente la lógica de negocio y presentación de una solicitud desde su interfaz de usuario (UI). El
mantenimiento de una separación limpia entre la lógica de la aplicación y la interfaz de usuario ayuda a hacer frente a numerosos problemas de desarrollo y
puede hacer una aplicación más fácil de probar, mantener y evolucionar. También puede mejorar en gran medida las oportunidades de reutilización de código y
permite a los desarrolladores y diseñadores de interfaz de usuario para mayor facilidad colaboran en el desarrollo de sus respectivas partes de una aplicación.

Utilizando el patrón MVVM, la interfaz de usuario de la aplicación y la lógica de presentación y negocio subyacente se separan en tres clases
distintas: la vista, que encapsula la interfaz de usuario y la lógica de interfaz de usuario; el modelo de vista, que encapsula la lógica de presentación
y estado; y el modelo, que encapsula la lógica de negocio y datos de la aplicación.

17 CAPÍTULO 2 | MVVM
CAPÍTULO 3

Inyección de
dependencia
Típicamente, un constructor de la clase se invoca cuando instanciar un objeto, y los valores que las necesidades de objeto se pasan como
argumentos para el constructor. Este es un ejemplo de la inyección de dependencia y, específicamente, se conoce como la inyección de
constructor. Las dependencias las necesidades de objeto se inyectan en el constructor.

Mediante la especificación de dependencias como tipos de interfaz, la inyección de dependencia permite la disociación de los tipos de hormigón a
partir del código que depende de estos tipos. Por lo general, utiliza un recipiente que contiene una lista de registros y asignaciones entre las
interfaces y tipos abstractos, y los tipos de hormigón que implementan o se extienden estos tipos.

También hay otros tipos de inyección de dependencias, tales como inyección de setter propiedad, y inyección llamada a un método, pero se ven con menos
frecuencia. Por lo tanto, este capítulo se centrará exclusivamente en la realización de la inyección de constructor con un contenedor de inyección de
dependencias.

Introducción a la inyección de dependencias


Dependencia de inyección es una versión especializada de la Inversión de Control patrón (COI), donde la preocupación de ser invertida es
el proceso de obtención de la dependencia requerida. Con la inyección de dependencias, otra clase es responsable de la inyección de
dependencias en un objeto en tiempo de ejecución. El ejemplo de código siguiente muestra cómo el ProfileViewModel clase está estructurada
cuando se utiliza la inyección de dependencia:

clase pública ProfileViewModel : ViewModelBase


{
privado IOrderService _orderService;

público ProfileViewModel ( IOrderService OrderService)


{
_orderService = OrderService;
}
. . .
}

los ProfileViewModel constructor recibe una IOrderService ejemplo, como un argumento, inyectado por otra clase. La única
dependencia en el ProfileViewModel clase es del tipo de interfaz. Por lo tanto, la ProfileViewModel clase no tiene ningún conocimiento
de la clase que es responsable de crear instancias de la IOrderService objeto. La clase que es responsable de instanciar la

18 CAPÍTULO 3 | Inyección de dependencia


IOrderService objeto, e insertándolo en el ProfileViewModel clase, se conoce como el contenedor de inyección de dependencia.

Dependencia contenedores de inyección de reducir el acoplamiento entre los objetos, proporcionando una instalación para crear instancias de
instancias de la clase y gestionar su tiempo de vida basado en la configuración del recipiente. Durante la creación de objetos, el contenedor
inyecta cualquier dependencia que el objeto requiere en ella. Si aún no se han creado esas dependencias, el contenedor crea y resuelve sus
dependencias en primer lugar.

Nota: La inyección de dependencia también se puede implementar de forma manual utilizando fábricas. Sin embargo, usando un recipiente proporciona
capacidades adicionales, tales como la gestión de toda la vida, y el registro a través de la exploración de montaje.

Hay varias ventajas de utilizar un recipiente de inyección de dependencia:

• Un contenedor elimina la necesidad de una clase para ubicar sus dependencias y gestionar su vida.

• Un contenedor permite el mapeo de dependencias implementadas sin afectar a la clase.

• Un recipiente facilita la capacidad de prueba permitiendo dependencias a ser burlado.

• Un recipiente aumenta la mantenibilidad, permitiendo nuevas clases que se añaden fácilmente a la aplicación.

En el contexto de una aplicación que utiliza Xamarin.Forms MVVM, un contenedor de inyección de dependencias típicamente será utilizado para el
registro y la resolución de vista de los modelos, y para el registro de servicios e inyectarlas en modelos de vista.

Hay muchos contenedores de inyección de dependencias disponibles, con la aplicación móvil eShopOnContainers utilizando Autofac para gestionar la creación
de instancias de vista del modelo y de servicio de las Clases en la aplicación. Autofac facilita la creación de aplicaciones débilmente acoplados, y proporciona
todas las características que se encuentran comúnmente en recipientes de inyección de dependencia, incluidos los métodos para registrar asignaciones de tipo
y del objeto de los casos, resolver los objetos, gestionar tiempos de vida del objeto, e inyectar objetos dependientes en constructores de objetos que los que
resuelve. Para obtener más información acerca de Autofac, véase autofac en readthedocs.io.

En Autofac, la IContainer interfaz proporciona el contenedor de inyección de dependencia. La Figura 3-1 muestra las dependencias
cuando se utiliza este contenedor, que crea la instancia de una IOrderService objeto y lo inyecta en el ProfileViewModel clase.

Figura 3-1: Dependencias cuando se utiliza la inyección de dependencia

19 CAPÍTULO 3 | Inyección de dependencia


En tiempo de ejecución, el recipiente debe saber qué aplicación de la IOrderService interfaz debe crear una instancia, antes de que pueda crear
una instancia de una ProfileViewModel objeto. Esto involucra:

• El contenedor de decidir cómo crear una instancia de un objeto que implementa la IOrderService
interfaz. Esto se conoce como registro.

• El contenedor instanciar el objeto que implementa la IOrderService interfaz, y el


ProfileViewModel objeto. Esto se conoce como resolución.

Finalmente, la aplicación va a terminar con el ProfileViewModel objeto y que estará disponible para la recolección de basura. En este
punto, el recolector de basura debe disponer de la IOrderService ejemplo, si otras clases no comparten la misma instancia.

Propina: Escribir código independiente del contenedor

Siempre trate de escribir código de contenedor agnóstica para desacoplar la aplicación desde el contenedor de la dependencia específica que se utiliza.

Registro
Antes de dependencias se pueden inyectar en un objeto, los tipos de las dependencias primero deben estar registrados en el
recipiente. Registro de un tipo normalmente consiste en pasar el contenedor de una interfaz y un tipo de hormigón que implementa la
interfaz.

Hay dos maneras de registrar tipos y objetos en el recipiente a través de código:

• El registro de un tipo o mapeo con el recipiente. Cuando sea necesario, el contenedor va a construir una instancia del tipo
especificado.

• Registrar un objeto existente en el contenedor como un conjunto unitario. Cuando sea necesario, el contenedor devolverá una
referencia al objeto existente.

Propina: contenedores de inyección de dependencia no siempre son adecuados

La inyección de dependencia introduce complejidad y requisitos que podrían no ser apropiado o útil para pequeñas aplicaciones
adicionales. Si una clase no tiene ninguna dependencia, o no es una dependencia de otros tipos, que podría no tener sentido para ponerlo
en el recipiente. Además, si una clase tiene un único conjunto de dependencias que son esenciales para el tipo y nunca va a cambiar,
puede que no tenga sentido para ponerlo en el recipiente.

El registro de los tipos que requieren la inyección de dependencia se debe realizar en un solo método en una aplicación, y este método debe
ser invocado temprano en el ciclo de vida de la aplicación para asegurarse de que la aplicación es consciente de las dependencias entre sus
clases. En la aplicación móvil eShopOnContainers esto se lleva a cabo por el ViewModelLocator clase, que se basa la IContainer objeto y es la
única clase en la aplicación que contiene una referencia a ese objeto. El ejemplo de código siguiente muestra cómo la aplicación móvil
eShopOnContainers declara el IContainer objeto en el ViewModelLocator clase:

privado estático IContainer _envase;

Tipos y casos están registrados en la RegisterDependencies método en el ViewModelLocator


clase. Esto se consigue mediante la creación de una primera ContainerBuilder ejemplo, que se demuestra en el ejemplo de código siguiente:

var constructor = nuevo ContainerBuilder ();

20 CAPÍTULO 3 | Inyección de dependencia


Tipos y casos son entonces registrados en el ContainerBuilder objeto, y el ejemplo de código siguiente demuestra la forma
más común de registro de tipo:

builder.RegisterType < RequestProvider > (). Como < IRequestProvider > ();

los RegisterType método que se muestra aquí los mapas de un tipo de interfaz a un tipo concreto. Se dice al contenedor para crear instancias de
una RequestProvider objeto cuando se crea una instancia de un objeto que requiere una inyección de una IRequestProvider a través de un
constructor.

tipos de hormigón también pueden ser registrados directamente sin una asignación de un tipo de interfaz, como se muestra en el ejemplo de código
siguiente:

builder.RegisterType < ProfileViewModel > ();

Cuando el ProfileViewModel tipo se ha resuelto, el recipiente inyectará sus dependencias requeridas.

Autofac también permite el registro ejemplo, donde el contenedor es responsable de mantener una referencia a una instancia de
singleton de un tipo. Por ejemplo, el ejemplo de código siguiente muestra cómo la aplicación móvil eShopOnContainers registra el tipo
de concreto a utilizar cuando una ProfileViewModel
instancia requiere una IOrderService ejemplo:

builder.RegisterType < OrderService > (). Como < IOrderService .> () SingleInstance ();

los RegisterType método que se muestra aquí los mapas de un tipo de interfaz a un tipo concreto. los
Única instancia método configura el registro para que cada objeto dependiente recibe la misma instancia compartida. Por lo
tanto, sólo un único OrderService instancia existirá en el recipiente, que es compartida por los objetos que requieren una
inyección de una IOrderService a través de un constructor.

Instancia de inscripción también se puede realizar con el registerInstance método, que se demuestra en el ejemplo
de código siguiente:

builder.RegisterInstance ( nuevo OrderMockService ()). Como < IOrderService > ();

los registerInstance método que se muestra aquí crea una nueva OrderMockService instancia y la registra con el recipiente. Por lo tanto,
sólo un único OrderMockService ejemplo existe en el recipiente, que es compartida por los objetos que requieren una inyección de una IOrderService
a través de un constructor.

Después de tipo e instancia de registro, el IContainer objeto debe ser construido, que se demuestra en el ejemplo de código
siguiente:

_container = builder.Build ();

invocando la Construir método en el ContainerBuilder instancia construye un nuevo contenedor de inyección de dependencias que
contiene los registros que se han hecho.

Propina: Considere una IContainer como ser inmutable

Mientras que proporciona una Autofac Actualizar Método para actualizar registros en un contenedor existente, llamar a este método debe evitarse
siempre que sea posible. Existen riesgos para la modificación de un contenedor después de haber sido construido, sobre todo si el contenedor ha
sido utilizado. Para más información, ver Considere un recipiente como inmutable en readthedocs.io.

21 CAPÍTULO 3 | Inyección de dependencia


Resolución
Después de que se ha registrado un tipo, que puede ser resuelto o se inyecta como una dependencia. Cuando se resuelve un tipo y el
contenedor tiene que crear una nueva instancia, se inyecta ninguna dependencia en la instancia.

En general, cuando se resuelve un tipo, una de las tres cosas sucede:

1. Si el tipo no se ha registrado, el recipiente se produce una excepción.

2. Si el tipo ha sido registrado como un singleton, el contenedor devuelve la instancia singleton. Si esta es la primera vez que
el tipo que se pide, el contenedor crea que, si es necesario, y mantiene una referencia a él.

3. Si el tipo no se ha registrado como un conjunto unitario, el contenedor devuelve una nueva instancia, y no mantiene una
referencia a él.

El ejemplo de código siguiente muestra cómo el RequestProvider tipo que se ha registrado previamente con Autofac se puede
resolver:

var requestProvider = _container.Resolve < IRequestProvider > ();

En este ejemplo, se le pide Autofac para resolver el tipo concreto para la IRequestProvider escriba, junto con cualquier dependencias. Típicamente, la Resolver
método se llama cuando se requiere una instancia de un tipo específico. Para obtener información acerca de cómo controlar el tiempo de vida de los
objetos resuelve, consulte La gestión de la vida útil de los objetos resueltos .

El ejemplo de código siguiente muestra cómo la aplicación móvil eShopOnContainers instancia tipos de vista del modelo y sus
dependencias:

var ViewModel = _container.Resolve (viewModelType);

En este ejemplo, se le pide Autofac para resolver el tipo de vista de modelo para un modelo de vista solicitada, y el contenedor también
resolverá las dependencias. Cuando la resolución de la ProfileViewModel el tipo, la dependencia de resolver es una IOrderService objeto. Por lo
tanto, Autofac primero construye una
OrderService objeto y luego pasa al constructor de la ProfileViewModel clase. Para obtener más información acerca de cómo los
eShopOnContainers aplicación móvil construye modelos de vista y los asocia a las vistas, consulte crear automáticamente un modelo de
vista con una vista de modelo localizador .

Nota: El registro y la resolución de tipos con un contenedor tiene un costo de rendimiento debido al uso del contenedor de reflexión para la
creación de cada tipo, sobre todo si las dependencias están siendo reconstruidos para cada página de navegación en la aplicación. Si hay
muchos o profundas dependencias, el coste de la creación puede aumentar significativamente.

La gestión de la vida útil de los objetos resueltos


Después de registrar un tipo, el comportamiento por defecto para Autofac es crear una nueva instancia del tipo registrada cada vez que se ha
resuelto el tipo, o cuando el mecanismo de dependencia inyecta casos en otras clases. En este escenario, el recipiente no tiene una referencia
al objeto de resolver. Sin embargo, cuando el registro de una instancia, el comportamiento por defecto para Autofac es la gestión de la vida útil
del objeto como un singleton. Por lo tanto, la instancia se mantiene en su alcance, mientras que el recipiente está en el alcance, y está
dispuesto cuando el contenedor sale del ámbito y es basura recogida, o cuando el código dispone explícitamente el recipiente.

22 CAPÍTULO 3 | Inyección de dependencia


Un alcance ejemplo Autofac se puede utilizar para especificar el comportamiento singleton para un objeto que Autofac crea a partir de un tipo
registrado. alcances de instancia autofac gestionar los tiempos de vida de objetos instanciados por el contenedor. El alcance instancia
predeterminada para la RegisterType método es el
InstancePerDependency alcance. sin embargo, el Única instancia alcance se puede utilizar con el
RegisterType método, de modo que el contenedor crea o devuelve una instancia singleton de un tipo al llamar a la Resolver método.
El ejemplo de código siguiente muestra cómo Autofac es instruido para crear una instancia singleton de la NavigationService clase:

builder.RegisterType < NavigationService > (). Como < INavigationService .> () SingleInstance ();

La primera vez que la INavigationService interfaz se resuelve, el contenedor crea una nueva
NavigationService oponerse y mantiene una referencia a él. En cualquier resoluciones posteriores de la
INavigationService interfaz, el contenedor devuelve una referencia a la NavigationService objeto que se creó anteriormente.

Nota: los Única instancia alcance dispone creado objetos cuando está dispuesto el recipiente.

Autofac incluye alcances instancia adicional. Para más información, ver instancia Alcance en readthedocs.io.

Resumen
La inyección de dependencia permite el desacoplamiento de tipos concretos del código que depende de este tipo. Se suele utilizar un
recipiente que contiene una lista de registros y asignaciones entre las interfaces y tipos abstractos, y los tipos de hormigón que
implementan o se extienden estos tipos.

Autofac facilita la creación de aplicaciones débilmente acoplados, y proporciona todas las características que se encuentran comúnmente en recipientes de
inyección de dependencia, incluidos los métodos para registrar asignaciones de tipo y del objeto de los casos, resolver los objetos, gestionar tiempos de vida del
objeto, e inyectar objetos dependientes en constructores de objetos se resuelve.

23 CAPÍTULO 3 | Inyección de dependencia


CAPÍTULO 4

La comunicación entre
débilmente acoplados

componentes
El patrón de publicación-suscripción es un patrón de mensajería en el que los editores envían mensajes sin tener conocimiento de ningún tipo de
receptor, conocidos como suscriptores. Del mismo modo, los suscriptores escuchar los mensajes específicos, sin tener ningún conocimiento de los
editores.

Eventos en .NET implementar el patrón de publicación-suscripción, y son el método más sencillo y directo para una capa de
comunicación entre los componentes si no se requiere la articulación flexible, como un control y la página que lo contiene. Sin embargo,
los tiempos de vida del editor y de abonado están acoplados por referencias a objetos entre sí, y el tipo de abonado deben tener una
referencia al tipo editor. Esto puede crear problemas de gestión de memoria, especialmente cuando hay objetos de corta vida que se
suscriben a un evento de un objeto estático o de larga duración. Si no se elimina el controlador de eventos, el abonado se mantiene
viva por la referencia a ella en el editor, y esto va a prevenir o retrasar la recolección de basura del suscriptor.

Introducción a MessagingCenter
los Xamarin.Forms MessagingCenter clase implementa el patrón de publicación-suscripción, que permite la comunicación basada en mensajes
entre los componentes que son un inconveniente para enlazar por objeto y tipo referencias. Este mecanismo permite a los editores y
suscriptores para comunicarse sin tener una referencia a cada otro, ayudando a reducir las dependencias entre los componentes, mientras que
también permite componentes que se desarrollaron y probaron de forma independiente.

los MessagingCenter clase proporciona la funcionalidad de multidifusión de publicación-suscripción. Esto significa que no puede haber
varios editores que publican un solo mensaje, y no puede haber varios suscriptores de escucha para el mismo mensaje. Figura 4-1
ilustra esta relación:

24 CAPÍTULO 4 | La comunicación entre los componentes débilmente acoplados


Figura 4-1: Multicast publicación-suscripción funcionalidad

Editores envían mensajes utilizando el MessagingCenter.Send método, mientras que los suscriptores escuchar los mensajes
utilizando el MessagingCenter.Subscribe método. Además, los suscriptores también pueden darse de baja de subscripciones de
datos, si es necesario, con el MessagingCenter.Unsubscribe
método.

Internamente, el MessagingCenter clase utiliza referencias débiles. Esto significa que no va a mantener los objetos vivos, y
permitirá que sean basura recogida. Por lo tanto, sólo debe ser necesario darse de baja de un mensaje cuando una clase ya
no desea recibir el mensaje.

Los eShopOnContainers aplicación móvil que utiliza el MessagingCenter clase para comunicar entre los componentes débilmente
acoplados. La aplicación define tres mensajes:

• los Agregar producto mensaje es publicado por la CatalogViewModel clase cuando se añade un elemento a la cesta de la
compra. A cambio, el BasketViewModel clase suscribe el mensaje y se incrementa el número de elementos en la cesta
en la respuesta. además, el BasketViewModel clase también cancele la suscripción de este mensaje.

• los Filtrar mensaje es publicado por la CatalogViewModel clase cuando el usuario aplica un filtro de marca o tipo de los
elementos que se muestran en el catálogo. A cambio, el CatalogView
clase suscribe el mensaje y actualiza la interfaz de usuario de manera que sólo se muestran los elementos que coinciden con los criterios del filtro.

• los ChangeTab mensaje es publicado por la MainViewModel clase cuando el


CheckoutViewModel navega a la MainViewModel después de la exitosa creación y presentación de un nuevo orden. A cambio, el Vista
principal clase suscribe el mensaje y actualiza la interfaz de usuario para que el Mi perfil pestaña está activo, para mostrar
las órdenes del usuario.

Nota: Mientras que la MessagingCenter clase permite la comunicación entre las clases débilmente acoplados, que no ofrece la única solución
arquitectónica a este problema. Por ejemplo, la comunicación entre un modelo de vista y una vista también se puede lograr por el motor de
unión y a través de las notificaciones de cambio de propiedad. Además, la comunicación entre dos modelos de vista también se puede
lograr mediante el paso de datos durante la navegación.

En los eShopOnContainers aplicación móvil, MessagingCenter se utiliza para actualizar en la interfaz de usuario en respuesta a una acción que
ocurre en otra clase. Por lo tanto, los mensajes se publican en el hilo de interfaz de usuario, con los abonados que recibe el mensaje en el mismo
hilo.

25 CAPÍTULO 4 | La comunicación entre los componentes Loosley acoplado


Propina: Marshal al hilo de interfaz de usuario al realizar actualizaciones de interfaz de usuario

Si se requiere un mensaje que se envía desde un subproceso de fondo para actualizar la interfaz de usuario, procesar el mensaje en el hilo de
interfaz de usuario en el suscriptor invocando el Device.BeginInvokeOnMainThread
método.

Para obtener más información acerca de MessagingCenter, ver MessagingCenter en el Centro de desarrolladores de Xamarin.

La definición de un mensaje
MessagingCenter los mensajes son cadenas que se utilizan para identificar los mensajes. El ejemplo de código siguiente muestra los
mensajes definidos dentro de la aplicación móvil eShopOnContainers:

clase pública MessengerKeys


{
// Añadir el producto a la cesta
const string pública addProduct = "Agregar producto" ;

// Filtro
const string pública filter = "Filtrar" ;

// Cambio seleccionado Tab mediante programación


const string pública ChangeTab = "ChangeTab" ;
}

En este ejemplo, los mensajes se definen utilizando constantes. La ventaja de este enfoque es que proporciona en tiempo de compilación
seguridad de tipos y apoyo refactorización.

La publicación de un mensaje
Editores notificar a los suscriptores de un mensaje con una de las MessagingCenter.Send sobrecargas. El ejemplo de código siguiente
demuestra la publicación de la Agregar producto mensaje:

MessagingCenter .Enviar( esta , MessengerKeys .AddProduct, CatalogItem);

En este ejemplo, la Enviar método especifica tres argumentos:

• El primer argumento especifica la clase remitente. La clase remitente debe ser especificado por cualquier abonados que
deseen recibir el mensaje.

• El segundo argumento especifica el mensaje.

• El tercer argumento especifica los datos de carga útil para ser enviados al abonado. En este caso los datos de carga útil es una CatalogItem
ejemplo.

los Enviar Método publicará el mensaje y sus datos de carga útil, utilizando un enfoque dispara y olvida. Por lo tanto, el mensaje
se envía incluso si no hay abonados registrados para recibir el mensaje. En esta situación, se ignora el mensaje enviado.

Nota: los MessagingCenter.Send método se puede utilizar parámetros genéricos para controlar cómo se entregan los mensajes. Por lo tanto, varios
mensajes que comparten una identidad de mensaje, pero envían diferentes tipos de datos de carga útil pueden ser recibidos por diferentes
abonados.

26 CAPÍTULO 4 | La comunicación entre los componentes Loosley acoplado


La suscripción a un mensaje
Los suscriptores pueden registrarse para recibir un mensaje usando una de las MessagingCenter.Subscribe
sobrecargas. El siguiente ejemplo de código muestra cómo la aplicación móvil eShopOnContainers suscribe, y los
procesos, la Agregar producto mensaje:

MessagingCenter .Subscribe < CatalogViewModel , CatalogItem > (


esta , MessageKeys .Agregar producto, asíncrono (Remitente, arg) =>
{
BadgeCount ++;

esperar AddCatalogItemAsync (arg);


});

En este ejemplo, la Suscribir método se suscribe a la Agregar producto mensaje, y ejecuta un delegado de devolución de llamada en respuesta a la recepción del
mensaje. Este delegado de devolución de llamada, especificado como una expresión lambda, ejecuta el código que actualiza la interfaz de usuario.

Propina: Considere el uso de datos de carga útil inmutables

No intente modificar los datos de carga útil desde el interior de un delegado de devolución de llamada debido a que varios hilos puede obtener acceso a
los datos recibidos de forma simultánea. En este escenario, los datos de carga útil deben ser inmutables para evitar errores de concurrencia.

Un abonado puede que no tenga que manejar cada instancia de un mensaje publicado, y esto puede ser controlado por los
argumentos de tipo genérico que se especifican en el Suscribir método. En este ejemplo, el abonado sólo recibirá Agregar
producto los mensajes que se envían desde el
CatalogViewModel clase, cuyos datos de carga útil es una CatalogItem ejemplo.

Darse de baja de un mensaje


Los suscriptores pueden darse de baja de los mensajes que ya no desean recibir. Esto se consigue con una de las MessagingCenter.Unsubscribe
sobrecargas, como se demuestra en el ejemplo de código siguiente:

MessagingCenter .Unsubscribe < CatalogViewModel , CatalogItem > ( esta , MessengerKeys .Agregar producto);

En este ejemplo, la Darse de baja sintaxis del método refleja los argumentos de tipo especificados cuando se suscribe para recibir
el Agregar producto mensaje.

Resumen
los Xamarin.Forms MessagingCenter clase implementa el patrón de publicación-suscripción, que permite la comunicación basada en mensajes
entre los componentes que son un inconveniente para enlazar por objeto y tipo referencias. Este mecanismo permite a los editores y
suscriptores para comunicarse sin tener una referencia a cada otro, ayudando a reducir las dependencias entre los componentes, mientras que
también permite componentes que se desarrollaron y probaron de forma independiente.

27 CAPÍTULO 4 | La comunicación entre los componentes Loosley acoplado


CAPÍTULO 5

Navegación
Xamarin.Forms incluye soporte para la página de navegación, que típicamente resulta de la interacción del usuario con la interfaz de usuario o de la
aplicación en sí como resultado de los cambios de estado de lógica impulsada internos. Sin embargo, la navegación puede ser difícil de aplicar en las
aplicaciones que utilizan el patrón Model-View-ViewModel (MVVM), ya que se deben cumplir los siguientes desafíos:

• Cómo identificar el objeto de ser navegado a, utilizando un enfoque que no introduzca estrecho acoplamiento y las
dependencias entre las vistas.

• ¿Cómo coordinar el proceso por el cual el fin de ser navegado a se instancia e inicializado. Al usar MVVM, la vista y vista
de modelo deben ser instanciado y asociados entre sí a través de contexto de unión de la vista. Cuando una aplicación
está utilizando un contenedor de inyección de dependencias, la instanciación de vistas y modelos de vista podría requerir
un mecanismo de construcción específica.

• Ya sea para realizar la navegación vista primero, o vista de navegación modelo primero. Mediante la navegación por primera vista, la
página para navegar hasta hace referencia al nombre del tipo de vista. Durante la navegación, la vista especificada se instancia, junto
con su correspondiente modelo de vista y otros servicios dependientes. Un enfoque alternativo es utilizar vista de navegación-modelo
primero, donde la página para navegar a se refiere al nombre del tipo de vista del modelo.

• ¿Cómo separar limpiamente el comportamiento de navegación de la aplicación a través de los puntos de vista y modelos de vista. El patrón MVVM

proporciona una separación entre la interfaz de usuario de la aplicación y su lógica de presentación y de negocios. Sin embargo, el comportamiento de

navegación de una aplicación a menudo tendrá una duración de las partes de interfaz de usuario y presentaciones de la aplicación. El usuario a menudo

iniciar la navegación desde un punto de vista, y la vista será reemplazado como resultado de la navegación. Sin embargo, a menudo, también podría

necesitar ser iniciado o coordinado desde dentro del modelo de vista de navegación.

• Cómo pasar parámetros durante la navegación con fines de inicialización. Por ejemplo, si el usuario navega a fin de actualizar
los datos del pedido, los datos de la orden tendrán que ser pasado a la vista para que pueda mostrar los datos correctos.

• Cómo coordinar la navegación, para asegurarse de que ciertas reglas de negocio son obedecidas. Por ejemplo, los usuarios puede
pedir que vuelva antes de salir de una vista para que puedan corregir los datos no válidos o preguntará si desea enviar o descartar
los cambios en los datos que se hicieron dentro de la vista.

Este capítulo aborda estos desafíos mediante la presentación de una NavigationService la clase que se utiliza para llevar a cabo la vista-modelo de
navegación primera página.

Nota: los NavigationService utilizado por la aplicación sólo está diseñado para realizar la navegación jerárquica entre Pagina de contenido instancias.
Usar el servicio para navegar entre otros tipos de páginas que podría dar lugar a un comportamiento inesperado.

28 CAPÍTULO 5 | Navegación
La navegación entre las páginas
la lógica de navegación puede residir en una vista de código subyacente, o en un modelo de vista de datos enlazado. Mientras que la colocación lógica de
navegación en una vista podría ser el enfoque más simple, no es fácilmente comprobable a través de pruebas de unidad. La colocación de lógica de navegación
en vista de las clases del modelo significa que la lógica puede ser ejercido a través de pruebas de unidad. Además, el modelo de vista puede entonces aplicar la
lógica para controlar la navegación para asegurar que ciertas reglas de negocio se cumplan. Por ejemplo, una aplicación puede no permitir al usuario navegar lejos
de una página sin antes asegurarse de que la información ingresada es válida.

UN NavigationService clase es típicamente invoca desde modelos de vista, a fin de promover la capacidad de prueba. Sin embargo, la navegación
a las vistas de modelos de vista requeriría los modelos de vista para hacer referencia a puntos de vista, y en particular puntos de vista que el
modelo de vista activo no está asociado con, lo que no es recomendable. Por lo tanto, la NavigationService presenta aquí especifica el tipo de vista
del modelo como el objetivo a navegar.

Los eShopOnContainers aplicación móvil que utiliza el NavigationService clase para proporcionar vista de navegación modelo primero. Esta
clase implementa la INavigationService interfaz, que se muestra en el ejemplo de código siguiente:

público interfaz INavigationService


{
ViewModelBase PreviousPageViewModel { obtener ; }
Tarea InitializeAsync ();
Tarea NavigateToAsync < TViewModel > () dónde TViewModel : ViewModelBase ;
Tarea NavigateToAsync < TViewModel > ( objeto parámetro) dónde TViewModel : ViewModelBase ;
Tarea RemoveLastFromBackStackAsync ();
Tarea RemoveBackStackAsync ();
}

Esta interfaz especifica que una clase de implementación debe proporcionar los métodos siguientes:

Método Propósito

InitializeAsync Realiza la navegación a uno de dos páginas cuando se inicia la aplicación.

NavigateToAsync <T> Realiza la navegación jerárquica a una página.


NavigateToAsync <T> (parámetro) Realiza la navegación jerárquica a una página especificada, pasando

un parámetro.
RemoveLastFromBackStackAsync Elimina la página anterior de la pila de navegación.
RemoveBackStackAsync Elimina todas las páginas anteriores de la pila de desplazamiento.

además, el INavigationService interfaz especifica que una clase de aplicación debe proporcionar una
PreviousPageViewModel propiedad. Esta propiedad devuelve el tipo de vista del modelo asociado a la página anterior en la pila
de navegación.

Nota: Un INavigationService Interfaz lo general también especificar una GoBackAsync método, que se utiliza para devolver mediante
programación a la página anterior de la pila de desplazamiento. Sin embargo, este método no se encuentra en la aplicación móvil
eShopOnContainers porque no es necesario.

La creación de la instancia NavigationService


los NavigationService clase, que implementa el INavigationService interfaz, se ha registrado como un singleton con el contenedor de
inyección de dependencias Autofac, como se demuestra en el ejemplo de código siguiente:

29 CAPÍTULO 5 | Navegación
builder.RegisterType < NavigationService > (). Como < INavigationService .> () SingleInstance ();

los INavigationService interfaz se resuelve en el ViewModelBase constructor de la clase, como se demuestra en el


ejemplo de código siguiente:

NavigationService = ViewModelLocator.Resolve <INavigationService> ();

Esto devuelve una referencia a la NavigationService objeto que se almacena en el contenedor de inyección de dependencias Autofac,
que se crea por la InitNavigation método en el Aplicación clase. Para más información, ver Navegando cuando se inicia la aplicación .

los ViewModelBase tiendas de clase las NavigationService ejemplo, en una NavigationService propiedad, del tipo de INavigationService. Por lo
tanto, todas las clases de vista de modelo, que se derivan de la
ViewModelBase clase, puede utilizar el NavigationService propiedad para acceder a los métodos especificados por el INavigationService
interfaz. Esto evita la sobrecarga de la inyección de la NavigationService
objetar desde el contenedor de inyección de dependencias Autofac en cada clase vista del modelo.

Manejar las solicitudes de navegación

Xamarin.Forms proporciona la NavigationPage clase, que implementa una experiencia de navegación jerárquica en la que el usuario es capaz de
navegar a través de páginas, hacia delante y hacia atrás, según se desee. Para obtener más información acerca de la navegación jerárquica,
véase Navegación jerárquica en el Centro de desarrolladores de Xamarin.

En lugar de utilizar el NavigationPage clase directamente, el eShopOnContainers aplicación envuelve el


NavigationPage clase en el CustomNavigationView clase, como se muestra en el ejemplo de código siguiente:

público clase parcial CustomNavigationView : NavigationPage


{
público CustomNavigationView (): base ()
{
InitializeComponent ();
}

público CustomNavigationView ( Página root): base (raíz)


{
InitializeComponent ();
}
}

El propósito de esta envoltura es para la facilidad de peinado NavigationPage instancia dentro del archivo XAML para la clase.

La navegación se realiza dentro de las clases de vista del modelo mediante la invocación de una de las NavigateToAsync
métodos, especificando el tipo de vista de modelo para la página que está siendo navegado, como se demuestra en el ejemplo de código siguiente:

esperar NavigationService.NavigateToAsync <MainViewModel> ();

El ejemplo de código siguiente muestra el NavigateToAsync métodos proporcionados por la


NavigationService clase:

30 CAPÍTULO 5 | Navegación
público Tarea NavigateToAsync < TViewModel > () dónde TViewModel : ViewModelBase
{
regreso InternalNavigateToAsync ( tipo de ( TViewModel ), nulo );
}

público Tarea NavigateToAsync < TViewModel > ( objeto parámetro) dónde TViewModel : ViewModelBase
{
regreso InternalNavigateToAsync ( tipo de ( TViewModel ), Parámetro);
}

Cada método permite a cualquier clase de vista modelo que se deriva de la ViewModelBase clase para realizar la navegación
jerárquica invocando el InternalNavigateToAsync método. Además, el segundo
NavigateToAsync método permite que los datos de navegación para ser especificadas como un argumento que se pasa a la modelo de vista
siendo navegado a, donde se utiliza típicamente para realizar la inicialización. Para más información, ver Paso de parámetros durante la
navegación .

los InternalNavigateToAsync método ejecuta la solicitud de navegación, y se muestra en el ejemplo de código siguiente:

asincrónico privado Tarea InternalNavigateToAsync ( Tipo viewModelType, objeto parámetro)


{
Página page = CreatePage (viewModelType, parámetro);

Si (página es LoginView )
{
Solicitud .Current.MainPage = nuevo CustomNavigationView (página);
}
más
{
var navigationPage = Solicitud .Current.MainPage como CustomNavigationView ;
Si (NavigationPage! = nulo )
{
esperar navigationPage.PushAsync (página);
}
más
{
Solicitud .Current.MainPage = nuevo CustomNavigationView (página);
}
}

esperar (page.BindingContext como ViewModelBase ) .InitializeAsync (parámetro);


}

privado Tipo GetPageTypeForViewModel ( Tipo viewModelType)


{
var viewName = viewModelType.FullName.Replace ( "Modelo" , cuerda .Vacío);
var viewModelAssemblyName = viewModelType.GetTypeInfo () Assembly.FullName.;
var viewAssemblyName = cuerda .Formato(
CultureInfo .InvariantCulture, "{0}, {1}" , ViewName, viewModelAssemblyName);
var ViewType = Tipo .GetType (viewAssemblyName);
regreso ViewType;
}

privado Página Crear página( Tipo viewModelType, objeto parámetro)


{
Tipo quiénes somos? = GetPageTypeForViewModel (viewModelType);
Si (Quiénes somos? == nulo )
{
arrojar nueva Excepción ( $ "No se puede localizar el tipo de página para { viewModelType }" );
}

31 CAPÍTULO 5 | Navegación
Página page = Activador .CreateInstance (quiénes somos?) como Página ;
regreso página;
}

los InternalNavigateToAsync método realiza la navegación a un modelo de vista llamando primero a la


Crear página método. Este método localiza el punto de vista que se corresponde con el tipo de vista modelo especificado, y crea y devuelve una
instancia de este tipo de vista. Localización de la vista que se corresponde con el tipo de vista modelo utiliza un enfoque basado en la
convención, que asume que:

• Vistas están en el mismo conjunto como tipos de vista del modelo.

• Las vistas son en un espacio de nombres de niño .Views.

• Ver los modelos están en un espacio de nombres .ViewModels niño.

• Ver los nombres corresponden a ver los nombres de modelo, con "Modelo" eliminado.

Cuando se instancia una vista, que está asociado con su correspondiente modelo de vista. Para obtener más información acerca de cómo ocurre esto, vea crear
automáticamente un modelo de vista con una vista de modelo localizador .

Si la vista que se está creando es una LoginView, está envuelto dentro de una nueva instancia de la
CustomNavigationView clase y asignado a la Application.Current.MainPage propiedad. De lo contrario, el CustomNavigationView instancia
se recupera, y siempre que no se nulo, el
PushAsync método se invoca para empujar la vista que se está creando en la pila de navegación. Sin embargo, si el recuperada CustomNavigationView
instancia es nulo, estando la vista creada está envuelto dentro de una nueva instancia de la CustomNavigationView clase y asignado a
la
Application.Current.MainPage propiedad. Este mecanismo garantiza que durante la navegación, las páginas se añaden correctamente a la
pila de desplazamiento, tanto cuando está vacío, y cuando contiene datos.

Propina: Considere páginas de almacenamiento en caché

Resultados página de almacenamiento en caché en el consumo de memoria de puntos de vista que no se muestran actualmente. Sin embargo, sin
caché de páginas que quiere decir que el análisis de XAML y construcción de la página y su modelo de vista ocurrirá cada vez que una nueva
página se navega a, que puede tener un impacto en el rendimiento de una página compleja. Para una página bien diseñada que no utiliza un
número excesivo de controles, el rendimiento debería ser suficiente. Sin embargo, la caché de páginas podría ayudar si se encuentran tiempos de
carga lenta página.

Después de que se crea y se navega a la vista, la InitializeAsync se ejecuta el método de modelo vista asociada de la vista. Para
más información, ver Paso de parámetros durante la navegación .

Navegando cuando se inicia la aplicación


Cuando se inicia la aplicación, la InitNavigation método en el Aplicación clase se invoca. El ejemplo de código siguiente muestra este
método:

privado Tarea InitNavigation ()


{
var NavigationService = ViewModelLocator .Resolve < INavigationService > ();
regreso navigationService.InitializeAsync ();
}

El método crea un nuevo NavigationService objeto en el contenedor de inyección de dependencias Autofac, y devuelve una referencia a
la misma, antes de invocar su InitializeAsync método.

32 CAPÍTULO 5 | Navegación
Nota: Cuando el INavigationService interfaz se resuelve por el ViewModelBase clase, el contenedor devuelve una
referencia a la NavigationService objeto creado cuando el
InitNavigation método se invoca.

El ejemplo de código siguiente muestra el NavigationService InitializeAsync método:

público Tarea InitializeAsync ()


{
Si ( cuerda .IsNullOrEmpty ( ajustes .AuthAccessToken))
regreso NavigateToAsync < LoginViewModel > ();
más
regreso NavigateToAsync < MainViewModel > ();
}

los Vista principal se navega a si la aplicación tiene un token de acceso en caché, que se utiliza para la autenticación. De lo contrario, el LoginView
se navega a.

Para obtener más información sobre el contenedor de inyección de dependencias Autofac, véase Introducción a la inyección de dependencias .

Paso de parámetros durante la navegación


Uno de los NavigateToAsync métodos, especificados por el INavigationService interfaz, permite que los datos de navegación para ser
especificadas como un argumento que se pasa a la modelo de vista siendo navegado a, donde se utiliza típicamente para realizar la
inicialización.

Por ejemplo, el ProfileViewModel clase contiene una OrderDetailCommand que es ejecutado cuando el usuario selecciona un
pedido en el ProfileView página. A su vez, esto ejecuta el OrderDetailAsync
método, que se muestra en el ejemplo de código siguiente:

asincrónico privado Tarea OrderDetailAsync ( Orden orden)


{
esperar NavigationService.NavigateToAsync < OrderDetailViewModel > (Orden);
}

Este método invoca a la navegación OrderDetailViewModel, el paso de una Orden instancia que representa el orden que el usuario
selecciona en el ProfileView página. Cuando el NavigationService
clase crea la OrderDetailView, el OrderDetailViewModel se crea una instancia de clase y asignado a la vista de BindingContext. Después
de navegar a la OrderDetailView, el
InternalNavigateToAsync método ejecuta la InitializeAsync método de modelo vista asociada de la vista.

los InitializeAsync método se define en el ViewModelBase clase como un método que puede ser anulado. Este método especifica una objeto
argumento que representa los datos que se pasa a un modelo de vista durante una operación de navegación. Por lo tanto, ver las
clases del modelo que desean recibir datos de una operación de navegación proporcionan su propia implementación de la InitializeAsync
método para realizar la inicialización necesaria. El ejemplo de código siguiente muestra el InitializeAsync método de la OrderDetailViewModel
clase:

público asíncrono de anulación Tarea InitializeAsync ( objeto navigationData)


{
Si (navigationData es Orden )
{
. . .
order = esperar _ordersService.GetOrderAsync (
Convertir .ToInt32 (order.OrderNumber), authToken);

33 CAPÍTULO 5 | Navegación
. . .
}
}

Este método recupera la Orden instancia que se introduce en el modelo de vista durante la operación de navegación, y lo utiliza para
recuperar los detalles de la orden completo de la OrderService ejemplo.

La invocación de comportamientos de navegación utilizando

La navegación se desencadena generalmente desde una vista por una interacción del usuario. Por ejemplo, el LoginView
realiza la navegación tras la autenticación exitosa. El ejemplo de código siguiente muestra cómo la navegación es invocado por un
comportamiento:

< WebView ... >


< WebView.Behaviors >
< comportamientos: EventToCommandBehavior
eventName = "Navegando"
EventArgsConverter = "{} StaticResource WebNavigatingEventArgsConverter"
comando = "{Binding} NavigateCommand" />
</ WebView.Behaviors >
</ WebView >

En tiempo de ejecución, la EventToCommandBehavior responderá a la interacción con el WebView. Cuando el


WebView navega a una página web, el Navegando evento se disparará, que ejecutará el
NavigateCommand en el LoginViewModel. Por defecto, los argumentos del evento para el evento se pasan al comando. Estos datos se
convierten como se le hace pasar entre la fuente y de destino por el convertidor especificado en el EventArgsConverter propiedad,
que devuelve el url desde el
WebNavigatingEventArgs. Por lo tanto, cuando el NavigationCommand se ejecuta, el url La página web se pasa como un parámetro
para el domicilio Acción.

A su vez, la NavigationCommand ejecuta el NavigateAsync método, que se muestra en el ejemplo de código siguiente:

asincrónico privado Tarea NavigateAsync ( cuerda url)


{
. . .
esperar NavigationService.NavigateToAsync <MainViewModel> ();
esperar NavigationService.RemoveLastFromBackStackAsync ();
. . .
}

Este método invoca a la navegación MainViewModel, y después de la navegación, se elimina la


LoginView la página de la pila de navegación.

Confirmar o cancelar la navegación


Una aplicación que tenga que interactuar con el usuario durante una operación de navegación, por lo que el usuario puede confirmar o cancelar la
navegación. Esto podría ser necesario, por ejemplo, cuando el usuario intenta navegar antes de haber completado totalmente una página de entrada de
datos. En esta situación, una aplicación debe proporcionar una notificación que permite al usuario navegar fuera de la página, o para cancelar la
operación de navegación antes de que ocurra. Esto se puede lograr en una clase de vista del modelo mediante el uso de la respuesta de una notificación
para controlar si o no la navegación se invoca.

34 CAPÍTULO 5 | Navegación
Resumen
Xamarin.Forms incluye soporte para la página de navegación, que típicamente resulta de la interacción del usuario con la interfaz de usuario, o de la aplicación
en sí, como resultado de los cambios de estado de lógica impulsada internos. Sin embargo, la navegación puede ser difícil de aplicar en las aplicaciones que
utilizan el patrón MVVM.

En este capítulo se presenta una NavigationService clase, que se utiliza para realizar vista de navegación-modelo primero de modelos de vista. La
colocación de lógica de navegación en vista de las clases del modelo significa que la lógica puede ser ejercido a través de pruebas automáticas. Además,
el modelo de vista puede entonces aplicar la lógica para controlar la navegación para asegurar que ciertas reglas de negocio se cumplan.

35 CAPÍTULO 5 | Navegación
CAPÍTULO 6

Validación
Cualquier aplicación que acepta la entrada de los usuarios deben asegurarse de que la entrada es válida. Una aplicación podría, por ejemplo, la
verificación de entrada que contiene sólo caracteres en un rango particular, es de una cierta longitud, o coincide con un formato particular. Sin
validación, un usuario puede suministrar datos que provoca la aplicación falle. Validación hace cumplir las reglas de negocio, y evita que un atacante
inyectar datos maliciosos.

En el contexto del patrón Model-ViewModel-Model (MVVM), a menudo se requiere un modelo de vista o modelo para realizar la validación de datos
y señal de cualquier error de validación a la vista de manera que el usuario puede corregirlos. La aplicación móvil eShopOnContainers realiza la
validación del lado del cliente sincrónica de vista del modelo y notifica al usuario de cualquier error de validación resaltando el control que contiene
los datos no válidos, y mediante la visualización de mensajes de error que informan al usuario de por qué los datos no es válido. La Figura 6-1
muestra las clases involucradas en la realización de la validación en los eShopOnContainers aplicación móvil.

Figura 6-1: clases de validación en la aplicación móvil eShopOnContainers

Ver propiedades modelo que requieren la validación son de tipo ValidatableObject <T>, y cada
ValidatableObject <T> instancia ha reglas de validación añadido a su validaciones propiedad. La validación se invoca desde el
modelo de vista llamando al Validar método de la ValidatableObject <T>

36 CAPÍTULO 6 | Validación
ejemplo, que recupera las reglas de validación y las ejecuta en contra de la ValidatableObject <T>
Valor propiedad. Cualquier error de validación se colocan en el errores propiedad de la
ValidatableObject <T> ejemplo, y el Es válida propiedad de la ValidatableObject <T> instancia se actualiza para indicar si la validación
tuvo éxito o no.

notificación de cambio de propiedades es provista por el ExtendedBindableObject clase, y así una Entrada
el control se puede unir a la Es válida propiedad de ValidatableObject <T> instancia en la clase de vista del modelo de ser notificado de si o no la
información ingresada es válida.

Especificación de las reglas de validación


Las reglas de validación se especifican mediante la creación de una clase que deriva de la IValidationRule <T> interfaz, que se muestra en el
ejemplo de código siguiente:

público interfaz IValidationRule < T >


{
cuerda ValidationMessage { obtener ; conjunto ; }
bool Comprobar( T valor);
}

Esta interfaz especifica que una clase de regla de validación debe proporcionar una Comprobar booleano método que se utiliza para realizar la
validación se requiere, y una ValidationMessage propiedad cuyo valor es el mensaje de error de validación que se mostrará si falla la validación.

El ejemplo de código siguiente muestra el IsNotNullOrEmptyRule <T> regla de validación, que se utiliza para realizar la validación del nombre de
usuario y la contraseña introducida por el usuario en el LoginView al utilizar los servicios de simulacros en la aplicación móvil
eShopOnContainers:

clase pública IsNotNullOrEmptyRule < T >: IValidationRule < T >


{
public string ValidationMessage { obtener ; conjunto ; }

public bool Comprobar( T valor)


{
Si (Valor == nulo )
{
falso retorno ;
}

var str = valor como cuerda ;


regreso ! cuerda .IsNullOrWhiteSpace (str);
}
}

los Comprobar método devuelve una booleano que indica si el valor argumento es nulo, vacía, o consiste solamente en los
espacios en blanco.

Aunque no es utilizado por los eShopOnContainers aplicación móvil, el siguiente ejemplo de código se muestra una regla de validación para validar
direcciones de correo electrónico:

clase pública EmailRule < T >: IValidationRule < T >


{
public string ValidationMessage { obtener ; conjunto ; }

public bool Comprobar( T valor)


{
Si (Valor == nulo )

37 CAPÍTULO 6 | Validación
{
falso retorno ;
}

var str = valor como cuerda ;


regex Regex = nuevo expresiones regulares ( @ "^ ([\ W \ \ -.] +) @ ([\ W \ -] +) (. (\ (\ W) {2,3}) +) $" );
coincidir = regex.Match (str);

regreso match.Success;
}
}

los Comprobar método devuelve una booleano que indica si o no la valor argumento es una dirección de correo electrónico válida. Esto se logra
mediante la búsqueda del valor argumento para la primera ocurrencia del patrón de expresión regular especificada en el expresiones regulares constructor.
Si el patrón de expresión regular se ha encontrado en la cadena de entrada puede determinarse comprobando el valor de la Partido objetos

Éxito propiedad.

Nota: validación de la propiedad a veces puede implicar propiedades dependientes. Un ejemplo de propiedades dependientes es cuando el
conjunto de valores válidos para la propiedad A depende del valor particular que ha sido establecido en la propiedad B. Para comprobar que
el valor de la propiedad A es uno de los valores permitidos implicaría recuperar el valor de la propiedad B . Además, cuando cambia el valor
de la propiedad B, tendría que ser revalidado la propiedad a.

Adición de reglas de validación a una propiedad


En la aplicación móvil eShopOnContainers, ver Características del modelo que requieren validación se declaran a ser de tipo ValidatableObject
<T>, dónde T es el tipo de los datos a ser validado. El ejemplo de código siguiente muestra un ejemplo de dos de tales propiedades:

público ValidatableObject < cuerda > Nombre de usuario


{
obtener

{
regreso _username;
}
conjunto

{
_username = valor ;
RaisePropertyChanged (() => nombre de usuario);
}
}

público ValidatableObject < cuerda > Contraseña


{
obtener

{
regreso _contraseña;
}
conjunto

{
_contraseña = valor ;
RaisePropertyChanged (() => contraseña);
}
}

Para la validación se produzca, las reglas de validación deben ser añadidos a la validaciones colección de cada uno
ValidatableObject <T> ejemplo, como se demuestra en el ejemplo de código siguiente:

38 CAPÍTULO 6 | Validación
private void AddValidations ()
{
_userName.Validations.Add ( nuevo IsNotNullOrEmptyRule < cuerda >
{
ValidationMessage = "Se requiere un nombre de usuario."
});
_password.Validations.Add ( nuevo IsNotNullOrEmptyRule < cuerda >
{
ValidationMessage = "Se requiere una contraseña."
});
}

Este método agrega el IsNotNullOrEmptyRule <T> regla de validación a la validaciones colección de cada uno ValidatableObject <T> ejemplo,
la especificación de valores para la validación de reglas de ValidationMessage
propiedad, que especifica el mensaje de error de validación que se mostrará si falla la validación.

validación de activación
El enfoque de validación utilizado en la aplicación móvil eShopOnContainers puede activar manualmente la validación de una propiedad, y
automáticamente desencadenar validación cuando una propiedad cambia.

Desencadenamiento de validación manualmente

La validación puede ser activado manualmente para una propiedad de vista del modelo. Por ejemplo, esto ocurre en los eShopOnContainers
aplicación móvil cuando el usuario toca la Iniciar sesión botón de la LoginView, al utilizar los servicios de simulacros. El delegado comando llama
al MockSignInAsync método en el LoginViewModel,
que invoca la validación mediante la ejecución de la Validar método, que se muestra en el ejemplo de código siguiente:

private bool Validar()


{
bool isValidUser = ValidateUserName ();
bool isValidPassword = ValidatePassword ();
regreso isValidUser && isValidPassword;
}

private bool ValidateUserName ()


{
regreso _userName.Validate ();
}

private bool ValidatePassword ()


{
regreso _password.Validate ();
}

los Validar método realiza la validación del nombre de usuario y la contraseña introducida por el usuario en el
LoginView, invocando el Validar método en cada ValidatableObject <T> ejemplo. El ejemplo de código siguiente muestra el Validar
método de la ValidatableObject <T> clase:

public bool Validar()


{
Errors.Clear ();

IEnumerable < cuerda > Errores = _validations


. Donde (v =>! V.Check (Valor))
. Seleccionar (v => v.ValidationMessage);

39 CAPÍTULO 6 | Validación
Errores = errors.ToList ();
! = IsValid Errors.Any ();

devolver este .Es válida;


}

Este método despeja el errores colección, y a continuación, recupera las reglas de validación que se han añadido al objeto de validaciones colección.
los Comprobar se ejecute el método para cada regla de validación recuperado, y el ValidationMessage Se agrega valor de la propiedad para
cualquier regla de validación que no puede validar los datos a la errores colección de la ValidatableObject <T> ejemplo. Finalmente, el Es
válida propiedad se establece, y su valor se devuelve al método de llamada, que indica si la validación tuvo éxito o no.

Activación de validación cuando propiedades cambian

La validación también se activa automáticamente cada vez que un salto cambios de propiedad. Por ejemplo, cuando una de dos vías de unión en el LoginView
establece el Nombre de usuario o Contraseña propiedad, la validación se desencadena. El siguiente ejemplo de código se muestra cómo se produce
esto:

< Entrada text = "{Binding UserName.Value, Modo = TwoWay}" >


< Entry.Behaviors >
< comportamientos: EventToCommandBehavior
eventName = "TextChanged"
comando = "{Binding} ValidateUserNameCommand" />
</ Entry.Behaviors >
. . .
</ Entrada >

los Entrada de control se une a la UserName.Value propiedad de la ValidatableObject <T> ejemplo, y el control de comportamientos colección
tiene una EventToCommandBehavior ejemplo añadido a la misma. Este comportamiento se ejecuta el ValidateUserNameCommand en
respuesta a la TextChanged evento de disparo en el Entrada, que se eleva cuando el texto en el Entrada cambios. A su vez, la

ValidateUserNameCommand delegado ejecuta el ValidateUserName método, que ejecuta el


Validar método en el ValidatableObject <T> ejemplo. Por lo tanto, cada vez que el usuario entra en un personaje de la Entrada control para
el nombre de usuario, se realiza la validación de los datos introducidos.

Para obtener más información sobre comportamientos, consulte La implementación de comportamientos .

Viendo los errores de validación


La aplicación móvil eShopOnContainers notifica al usuario de cualquier error de validación resaltando el control que contiene los datos no
válidos con una línea roja, y mostrando un mensaje de error que informa al usuario de por qué los datos no es válida por debajo del control
que contiene los datos no válidos. Cuando se corrige los datos no válidos, la línea cambia a negro y se elimina el mensaje de error. La Figura
6-2 muestra la
LoginView en la aplicación móvil eShopOnContainers cuando los errores de validación están presentes.

40 CAPÍTULO 6 | Validación
Figura 6-2: Viendo los errores de validación durante el inicio de sesión

Destacando un control que contiene datos no válidos


los LineColorBehavior comportamiento adjunto se utiliza para resaltar Entrada controles que se han producido errores de validación. El
ejemplo de código siguiente muestra cómo el LineColorBehavior comportamiento adjunto está unido a un Entrada controlar:

< Entrada text = "{Binding UserName.Value, Modo = TwoWay}" >


< Entry.Style >
< OnPlatform x: TypeArguments = "Estilo"
iOS = "{} StaticResource EntryStyle"
Android = "{} StaticResource EntryStyle"
WinPhone = "{} StaticResource UwpEntryStyle" />
</ Entry.Style >
. . .
</ Entrada >

los Entrada Control consume un estilo explícito, que se muestra en el siguiente ejemplo de código:

< Estilo x: Key = "EntryStyle"


TargetType = "{X: Entrada Tipo}" >
. . .
< Setter propiedad = "Comportamientos: LineColorBehavior.ApplyLineColor"
valor = "Cierto" />
< Setter propiedad = "Comportamientos: LineColorBehavior.LineColor"
valor = "{} StaticResource BlackColor" />
. . .
</ Estilo >

Este estilo establece el ApplyLineColor y Color de linea propiedades adjuntas de la LineColorBehavior


comportamiento adjunto en el Entrada controlar. Para obtener más información sobre estilos, consulte estilos en el Centro de desarrolladores de Xamarin.

Cuando el valor de la ApplyLineColor propiedad adjunta se establece, o cambios, el LineColorBehavior


comportamiento adjunto ejecuta el OnApplyLineColorChanged método, que se muestra en el ejemplo de código siguiente:

41 CAPÍTULO 6 | Validación
público clase estática LineColorBehavior
{
. . .
static void privada OnApplyLineColorChanged (
bindableObject enlazable, objeto valor antiguo, objeto nuevo valor)
{
var view = enlazable como Ver ;
Si (Vista == nulo )
{
regreso ;
}

bool hasLine = ( bool )nuevo valor;


Si (HasLine)
{
view.Effects.Add ( nuevo EntryLineColorEffect ());
}
más
{
var entryLineColorEffectToRemove =
view.Effects.FirstOrDefault (e => e es EntryLineColorEffect );
Si (EntryLineColorEffectToRemove! = nulo )
{
view.Effects.Remove (entryLineColorEffectToRemove);
}
}
}
}

Los parámetros para este método proporcionan la instancia del control que el comportamiento está unido a, y los viejos y los
nuevos valores de la ApplyLineColor propiedad adjunta. los EntryLineColorEffect
clase se agrega al control de efectos Si la colección ApplyLineColor es propiedad adjunta cierto,
de lo contrario es retirado de la del control de efectos colección. Para obtener más información sobre comportamientos, consulte La implementación de
comportamientos .

los EntryLineColorEffect subclases de la RoutingEffect clase, y se muestra en el ejemplo de código siguiente:

clase pública EntryLineColorEffect: RoutingEffect


{
público EntryLineColorEffect (): base ( "EShopOnContainers.EntryLineColorEffect" )
{
}
}

los RoutingEffect clase representa un efecto independiente de la plataforma que se envuelve un efecto interior que es específico de la plataforma. Esto
simplifica el proceso de eliminación del efecto, ya que no hay tiempo de compilación acceso a la información de tipo para un efecto específico de la
plataforma. los EntryLineColorEffect llama al constructor de la clase base, que pasa en un parámetro que consiste en una concatenación del nombre del
grupo de resolución, y el ID exclusivo que se especifica en cada clase de efecto específico de la plataforma.

El ejemplo de código siguiente muestra la implementación eShopOnContainers.EntryLineColorEffect para iOS:

[montaje: ResolutionGroupName ( "eShopOnContainers" )]


[montaje: ExportEffect ( tipo de ( EntryLineColorEffect ), "EntryLineColorEffect" )]
espacio de nombres eShopOnContainers.iOS.Effects
{
clase pública EntryLineColorEffect : PlatformEffect

42 CAPÍTULO 6 | Validación
{
UITextField controlar;

protected void override OnAttached ()


{
tratar

{
Control = Control como UITextField ;
UpdateLineColor ();
}
captura ( Excepción ex)
{
Consola .Línea de escritura( "No se puede establecer la propiedad en el control adjunta de error:". , Ex.Message);
}
}

protected void override OnDetached ()


{
control = nulo ;
}

protected void override OnElementPropertyChanged ( PropertyChangedEventArgs args)


{
base .OnElementPropertyChanged (args);

Si (Args.PropertyName == LineColorBehavior .LineColorProperty.PropertyName ||


args.PropertyName == "Altura" )
{
Inicializar();
UpdateLineColor ();
}
}

private void Inicializar()


{
var entrada = Elemento como Entrada ;
Si (Entrada! = nulo )
{
Control.Bounds = nuevo CGRect ( 0 , 0 , Entry.Width, entry.Height);
}
}

private void UpdateLineColor ()


{
BorderLineLayer lineLayer = control.Layer.Sublayers.OfType < BorderLineLayer > ()
. FirstOrDefault ();

Si (LineLayer == nulo )
{
lineLayer = nuevo BorderLineLayer ();
lineLayer.MasksToBounds = cierto ;
lineLayer.BorderWidth = 1.0f ;
control.Layer.AddSublayer (lineLayer);
control.BorderStyle = UITextBorderStyle .Ninguna;
}

lineLayer.Frame = nuevo CGRect ( 0f , Control.Frame.Height- 1f , Control.Bounds.Width, 1f );


lineLayer.BorderColor = LineColorBehavior .GetLineColor (Element) .ToCGColor ();
control.TintColor = control.TextColor;
}

clase privada BorderLineLayer : CALayer


{
}

43 CAPÍTULO 6 | Validación
}
}

los OnAttached método recupera el control nativo para los Xamarin.Forms Entrada control, y actualiza el color de la línea
llamando al UpdateLineColor método. los OnElementPropertyChanged
anulación responde a los cambios de propiedades que pueden vincularse a la Entrada el control mediante la actualización de la línea de color si el adjunto Color de linea cambios
de propiedad, o la Altura propiedad de la Entrada cambios. Para obtener más información acerca de los efectos, consulte efectos en el Centro de desarrolladores de
Xamarin.

Cuando se introducen los datos válidos en el Entrada el control, se aplicará una línea de negro a la parte inferior del mando, para indicar que no hay
ningún error de validación. Figura 6-3 muestra un ejemplo de esto.

Figura 6-3: línea de color negro indica que no hay error de validación

los Entrada de control también tiene una DataTrigger añadido a su disparadores colección. El ejemplo de código siguiente muestra el DataTrigger:

< Entrada text = "{Binding UserName.Value, Modo = TwoWay}" >


. . .
< Entry.Triggers >
< DataTrigger
TargetType = "Entrada"
= vinculante "{Binding} UserName.IsValid"
valor = "Falso" >
< Setter propiedad = "Comportamientos: LineColorBehavior.LineColor"
valor = "{} StaticResource ErrorColor" />
</ DataTrigger >
</ Entry.Triggers >
</ Entrada >

Esta DataTrigger supervisa la UserName.IsValid propiedad, y si es de valor se convierte falso, ejecuta el Setter, que cambia la Color
de linea propiedad adjunta de la LineColorBehavior
atado comportamiento a rojo. Figura 6-4 muestra un ejemplo de esto.

Figura 6-4: La línea roja indica error de validación

La línea en la Entrada el control permanecerá en rojo mientras que los datos introducida no es válida, de lo contrario va a cambiar a negro para indicar que la
información ingresada es válida.

Para obtener más información acerca de disparadores, véase disparadores en el Centro de desarrolladores de Xamarin.

Mostrar mensajes de error


Los mensajes de error interfaz de usuario muestra la validación en Etiqueta controles por debajo de cada control cuyos datos fallaron validación. El ejemplo
de código siguiente muestra el Etiqueta que muestra un mensaje de error de validación si el usuario no ha introducido un nombre de usuario válido:

44 CAPÍTULO 6 | Validación
< Etiqueta text = "{Binding UserName.Errors, convertidor = {} StaticResource FirstValidationErrorConverter"
style = "{} StaticResource ValidationErrorLabelStyle" />

Cada Etiqueta se une a la propiedad errores de la vista de objetos modelo que está siendo validada. los errores
propiedades es provista por el ValidatableObject <T> clase, y es de tipo List <string>. Porque el
errores propiedad puede contener varios errores de validación, las FirstValidationErrorConverter
instancia se utiliza para recuperar el primer error de la colección para su visualización.

Resumen
La aplicación móvil eShopOnContainers realiza la validación del lado del cliente sincrónica de vista del modelo y notifica al usuario de
cualquier error de validación resaltando el control que contiene los datos no válidos, y mediante la visualización de mensajes de error que
informan al usuario por qué los datos no es válido.

Ver propiedades modelo que requieren la validación son de tipo ValidatableObject <T>, y cada
ValidatableObject <T> instancia ha reglas de validación añadido a su validaciones propiedad. La validación se invoca desde el
modelo de vista llamando al Validar método de la ValidatableObject <T>
ejemplo, que recupera las reglas de validación y las ejecuta en contra de la ValidatableObject <T>
Valor propiedad. Cualquier error de validación se colocan en el errores propiedad de la
ValidatableObject <T> ejemplo, y el Es válida propiedad de la ValidatableObject <T> instancia se actualiza para indicar si la validación
tuvo éxito o no.

45 CAPÍTULO 6 | Validación
CAPÍTULO 7

gestión de la
configuración
Ajustes permiten la separación de los datos que configuran el comportamiento de una aplicación desde el código, lo que permite que el comportamiento puede
cambiar sin volver a generar la aplicación. Existen dos tipos de ajustes: Ajustes de aplicaciones y configuraciones de usuario.

configuración de la aplicación son datos que una aplicación crea y gestiona. Puede incluir datos tales como los puntos finales fijos web de servicios, claves de la API, y

el estado de ejecución. configuración de la aplicación están vinculados a la existencia de la aplicación y sólo son significativos para esa aplicación.

Ajustes de usuario son los ajustes personalizables de una aplicación que afectan el comportamiento de la aplicación y no requieren frecuentes
re-ajuste. Por ejemplo, una aplicación podría permitir al usuario especificar dónde recuperar datos de, y cómo mostrarlo en la pantalla.

Xamarin.Forms incluye un diccionario persistente que se puede utilizar para almacenar datos de configuración. Este diccionario se puede acceder
mediante el Application.Current.Properties propiedad, y cualquier dato que se coloca en ella se guarda cuando la aplicación entra en un estado de sueño, y se
restablece cuando la aplicación se reanuda o se pone en marcha de nuevo. además, el Solicitud clase también tiene una SavePropertiesAsync método que
permite una aplicación para haber salvado sus ajustes cuando sea necesario. Para obtener más información acerca de este diccionario, véase

propiedades del diccionario en el Centro de desarrolladores de Xamarin.

Una desventaja de almacenamiento de datos utilizando las Xamarin.Forms diccionario persistente es que no es fácil de datos con destino a. Por lo tanto,
la aplicación móvil eShopOnContainers utiliza la biblioteca Xam.Plugins.Settings, disponible en NuGet . Esta biblioteca proporciona un enfoque
multiplataforma consistente, con seguridad de tipos de persistencia y recuperación de configuración de la aplicación y de usuario, durante el uso de la
gestión de la configuración nativa proporcionada por cada plataforma. Además, es fácil de utilizar el enlace para ver los ajustes datos expuestos por la
biblioteca de datos.

Nota: Mientras que la biblioteca Xam.Plugin.Settings puede almacenar tanto la configuración del usuario y la aplicación, no hace ninguna distinción entre
los dos.

Crear una clase de ajustes


Cuando se utiliza la biblioteca Xam.Plugins.Settings, una sola estático clase debe ser creado que contendrá los ajustes de la aplicación y de
usuario requeridos por la aplicación. El ejemplo de código siguiente muestra el ajustes clase en la aplicación móvil eShopOnContainers:

46 CAPÍTULO 7 | gestión de la configuración


público clase estática ajustes
{
estática privada ISettings Ajustes de Aplicacion
{
obtener

{
regreso CrossSettings .Corriente;
}
}
. . .
}

Los ajustes se pueden leer y escribir a través de la ISettings API, que es proporcionada por la biblioteca Xam.Plugins.Settings. Esta
biblioteca ofrece un producto único que se puede utilizar para acceder a la API,
CrossSettings.Current, y la clase de configuración de una aplicación debe exponer este producto único a través de una ISettings

propiedad.

Nota: Uso de directivas para la Plugin.Settings y Plugin.Settings.Abstractions


espacios de nombres, debe añadirse a una clase que requiere el acceso a los tipos de bibliotecas Xam.Plugins.Settings.

Al agregar un valor
Cada ajuste consiste en una clave, un valor por defecto, y una propiedad. El ejemplo de código siguiente muestra los tres puntos para un
ajuste de usuario que representa la URL base para los servicios en línea que la aplicación móvil se conecta a eShopOnContainers:

público clase estática ajustes


{
. . .
const string privada IdUrlBase = "Url_base" ;
cadena de sólo lectura estática privada UrlBaseDefault = Configuración global .Instance.BaseEndpoint;
. . .

public static string URLBase


{
obtener

{
regreso AppSettings.GetValueOrDefault < cuerda > (IdUrlBase, UrlBaseDefault);
}
conjunto

{
AppSettings.AddOrUpdateValue < cuerda > (IdUrlBase, valor );
}
}
}

La clave es siempre una const string que define el nombre de la clave, con el valor predeterminado para la configuración de ser una sólo lectura estática valor
del tipo requerido. Proporcionar un valor predeterminado garantiza que un valor válido está disponible si se recupera un entorno sin definir.

los URLBase estático propiedad utiliza dos métodos de la ISettings API para leer o escribir el valor de ajuste. los ISettings.GetValueOrDefault método se
utiliza para recuperar el valor de una opción de almacenamiento específico de la plataforma. Si no se define ningún valor para el ajuste, su valor
por defecto se recupera en su lugar. Del mismo modo, la ISettings.AddOrUpdateValue método se utiliza para guardar el valor de un parámetro para el
almacenamiento platformspecific.

47 CAPÍTULO 7 | gestión de la configuración


Más bien que definir un valor predeterminado dentro de la ajustes clase, el UrlBaseDefault cadena obtiene su valor de la Configuración
global clase. El ejemplo de código siguiente muestra el BaseEndpoint propiedad y UpdateEndpoint método en esta clase:

clase pública Configuración global


{
. . .
public string BaseEndpoint
{
obtener { regreso _baseEndpoint; }
conjunto

{
_baseEndpoint = valor ;
UpdateEndpoint (_baseEndpoint);
}
}
. . .

private void UpdateEndpoint ( cuerda baseEndpoint)


{
RegisterWebsite = cuerda .Formato( "{0}: 5105 / Cuenta / Registro" , BaseEndpoint);
CatalogEndpoint = cuerda .Formato( "{0}: 5101" , BaseEndpoint);
OrdersEndpoint = cuerda .Formato( "{0}: 5102" , BaseEndpoint);
BasketEndpoint = cuerda .Formato( "{0}: 5103" , BaseEndpoint);
IdentityEndpoint = cuerda .Formato( "{0}: 5105 / connect / autorizar" , BaseEndpoint);
UserInfoEndpoint = cuerda .Formato( "{0}: 5105 / connect / userinfo" , BaseEndpoint);
TokenEndpoint = cuerda .Formato( "{0}: 5105 / connect / ficha" , BaseEndpoint);
LogoutEndpoint = cuerda .Formato( "{0}: 5105 / connect / EndSession" , BaseEndpoint);
IdentityCallback = cuerda .Formato( "{0}: 5105 / xamarincallback" , BaseEndpoint);
LogoutCallback = cuerda .Formato( "{0}: 5105 / Cuenta / redireccionante" , BaseEndpoint);
}
}

Cada vez que el BaseEndpoint propiedad se establece, la UpdateEndpoint método se llama. Este método actualiza una serie de propiedades,
todos los cuales se basan en el URLBase la configuración del usuario que se proporciona por el ajustes clase que representan diferentes puntos
finales que la aplicación móvil eShopOnContainers conecta.

El enlace de datos de configuración de usuario


En los eShopOnContainers aplicación móvil, la SettingsView expone dos configuraciones del usuario. Estos ajustes permiten la configuración
de si la aplicación debe recuperar datos de microservicios que se implementan como contenedores de Docker, o si la aplicación debe
recuperar datos de los servicios de simulacros que no requieren una conexión a Internet. Al elegir para recuperar datos de microservicios en
contenedores, una URL del punto base para los microservicios debe ser especificado. La Figura 7-1 muestra la SettingsView cuando el usuario
ha elegido para recuperar datos de microservicios en contenedores.

48 CAPÍTULO 7 | gestión de la configuración


Figura 7-1: Ajustes de usuario expuestas por los eShopOnContainers de aplicaciones móviles

El enlace de datos se puede utilizar para recuperar y establecer los ajustes expuestos por el ajustes clase. Esto se consigue mediante controles de la vista de
unión para ver las propiedades de modelo que, a su vez propiedades de acceso en el
ajustes clase, y levantando una propiedad cambiaron notificación si el valor de configuración ha cambiado. Para obtener información acerca de
cómo los eShopOnContainers aplicación móvil construye modelos de vista y los asocia a las vistas, consulte crear automáticamente un modelo de
vista con una vista de modelo localizador .

El ejemplo de código siguiente muestra el Entrada el control de la SettingsView que permite que el usuario introduzca una URL del punto base
para los microservicios en contenedores:

< Entrada text = "{Binding punto final, Modo = TwoWay}" />

Esta Entrada de control se une a la punto final propiedad de la SettingsViewModel clase, utilizando una de dos vías de unión. El ejemplo de
código siguiente muestra el punto final propiedad:

público cuerda punto final


{
obtener { regreso _endpoint; }
conjunto

{
_endpoint = valor ;

Si (! cuerda .IsNullOrEmpty (_endpoint))


{
UpdateEndpoint (_endpoint);
}

RaisePropertyChanged (() => Punto de llegada);


}
}

Cuando la propiedad de punto final se establece la UpdateEndpoint método se llama, siempre que el valor suministrado es válido, y la
propiedad cambió de notificación se eleva. El ejemplo de código siguiente muestra el
UpdateEndpoint método:

private void UpdateEndpoint ( cuerda punto final)


{
ajustes .UrlBase = punto final;
}

49 CAPÍTULO 7 | gestión de la configuración


Este método actualiza el URLBase propiedad en el ajustes clase con el valor de URL de punto final de base introducida por el usuario, lo que hace
que se persistió al almacenamiento específico de la plataforma.

Cuando el SettingsView se navega a la InitializeAsync método en el SettingsViewModel


se ejecuta la clase. El ejemplo de código siguiente muestra este método:

público anular Tarea InitializeAsync ( objeto navigationData)


{
. . .
punto final = ajustes .UrlBase;
. . .
}

El método establece el punto final propiedad en el valor de la URLBase propiedad en el ajustes clase. Acceso a la URLBase propiedad provoca la
biblioteca Xam.Plugins.Settings para recuperar el valor de los ajustes de almacenamiento específico de la plataforma. Para obtener
información acerca de cómo el InitializeAsync método se invoca, ver Paso de parámetros durante la navegación .

Este mecanismo garantiza que cuando un usuario navega a la SettingsView, configuración de usuario se recuperan de almacenamiento específico de la
plataforma y se presentan a través del enlace de datos. Entonces, si el usuario cambia los valores de los ajustes, los datos garantiza que se
conservan inmediatamente posterior al almacenamiento específico de la plataforma de unión.

Resumen
Ajustes permiten la separación de los datos que configuran el comportamiento de una aplicación desde el código, lo que permite que el comportamiento puede
cambiar sin volver a generar la aplicación. configuración de la aplicación son datos que una aplicación crea y gestiona, y la configuración del usuario son los
ajustes personalizables de una aplicación que afectan el comportamiento de la aplicación y no requieren frecuentes re-ajuste.

La biblioteca Xam.Plugins.Settings ofrece un enfoque multiplataforma consistente, con seguridad de tipos de persistencia y recuperación de
configuración de la aplicación y de usuario, y el enlace de datos se puede utilizar para ver los ajustes creados con la biblioteca.

50 CAPÍTULO 7 | gestión de la configuración


CAPÍTULO 8

microservicios en
contenedores
Desarrollo de aplicaciones cliente-servidor se ha traducido en un enfoque en la creación de aplicaciones por niveles que utilizan tecnologías específicas
en cada nivel. Tales aplicaciones se refieren a menudo como monolítico aplicaciones, y se envasan en hardware en la pre-escalado de los picos de
carga. Los principales inconvenientes de este enfoque de desarrollo son el estrecho acoplamiento entre los componentes dentro de cada nivel, que los
componentes individuales no se pueden escalar fácilmente, y el costo de las pruebas. Una simple actualización puede tener efectos imprevistos en el
resto de la grada, y por lo que un cambio en un componente de aplicación requiere toda su nivel volverse a probar y redistribuido.

Particularmente se refiere, en la edad de la nube, es que los componentes individuales no se pueden escalar fácilmente. Una aplicación monolítica
contiene funcionalidad de dominio específico, y típicamente se divide por capas funcionales, tales como extremo delantero, la lógica de negocio, y
almacenamiento de datos. Una aplicación monolítica se escala mediante la clonación de toda la aplicación en varias máquinas, como se ilustra en la
Figura 8-1.

Figura 8-1: enfoque de escalamiento aplicación monolítica

51 CAPÍTULO 8 | microservicios en contenedores


microservicios
Microservicios ofrecen un enfoque diferente para el desarrollo y despliegue de aplicaciones, un enfoque que se adapta a la agilidad, la escala y los
requisitos de fiabilidad de aplicaciones en la nube modernas. Una aplicación microservicios se descompone en componentes independientes que
trabajan juntos para ofrecer funcionalidad global de la aplicación. El término microService hace hincapié en que las aplicaciones deben estar
compuestos de los servicios lo suficientemente pequeños como para reflejar las preocupaciones independientes, de manera que cada microService
implementa una sola función. Además, cada microService ha contratos bien definidos de modo que otros microservicios pueden comunicar y
compartir datos con ella. Ejemplos típicos de microservicios incluyen carritos de la compra, procesamiento de inventario, subsistemas de compra y
procesamiento de pagos.

Microservicios pueden escalar-cabo de forma independiente, en comparación con aplicaciones monolíticas gigantes que escalan juntos. Esto
significa que un área funcional específica, que requiere más potencia de procesamiento o ancho de banda de red para apoyar la demanda, se puede
escalar en lugar de escalar innecesariamente-out otras áreas de la aplicación. Figura 8-2 ilustra este enfoque, donde microservicios se despliegan y
se escalan de forma independiente, la creación de instancias de servicios a través de máquinas.

Figura 8-2: Microservicios enfoque de escalamiento de aplicaciones

Microservice escala de salida puede ser casi instantánea, lo que permite una aplicación para adaptarse a los cambios de carga. Por ejemplo,
una sola microService en la funcionalidad de cara al web de una aplicación podría ser la única microService en la aplicación que necesita
escalar para manejar el tráfico de entrada adicional.

El modelo clásico para la escalabilidad de la aplicación es tener un nivel sin estado de carga equilibrada con un almacén de datos externo compartido para
almacenar datos persistentes. microservicios con estado gestionar sus propios datos persistentes, por lo general almacenar localmente en los servidores en los
que se colocan, para evitar la sobrecarga de acceso a la red y la complejidad de las operaciones de servicio cruz. Esto permite el procesamiento más rápido
posible de datos y puede eliminar la necesidad de sistemas de almacenamiento en caché. Además, microservicios con estado escalables generalmente
partición de datos entre sus casos, a fin de gestionar tamaño de los datos y la transferencia de rendimiento más allá de la cual un solo servidor puede soportar.

Microservicios también admiten actualizaciones independientes. Este acoplamiento débil entre microservicios proporciona una evolución rápida y fiable de la
aplicación. Su naturaleza independiente distribuido soporta actualizaciones de laminación, donde sólo un subconjunto de instancias de una sola microService
actualizará en un momento dado. Por lo tanto, si se detecta un problema, una actualización con errores se puede rodar hacia atrás, antes de todas las
instancias de actualización con el código defectuoso o configuración. Del mismo modo, microservicios suelen utilizar versiones del esquema, por lo que

52 CAPÍTULO 8 | microservicios en contenedores


clientes ver una versión consistente cuando se aplican cambios, independientemente de qué instancia microService que se está
comunicando con.

Por lo tanto, las aplicaciones MICROSERVICE tienen muchas ventajas sobre las aplicaciones monolíticas:

• Cada microService es relativamente pequeña, fácil de manejar y evolucionar.

• Cada microService puede ser desarrollado y desplegado de forma independiente de otros servicios.

• Cada microService se puede escalar de salida de forma independiente. Por ejemplo, podría tener que ser reducido de salida más que un servicio
de pedidos en un servicio de catálogo o de compras de servicios canasta. Por lo tanto, la infraestructura resultante va a consumir de manera más
eficiente los recursos cuando se escala a cabo.

• Cada microService aísla ningún problema. Por ejemplo, si hay un problema en un servicio que sólo los impactos de ese servicio.
Los otros servicios pueden seguir para manejar las peticiones.

• Cada microService puede utilizar las últimas tecnologías. Debido microservicios son autónomos y corren lado a lado, las últimas
tecnologías y los marcos se pueden utilizar, en lugar de estar obligados a utilizar un marco más antigua que pueda ser utilizado
por una aplicación monolítica.

Sin embargo, una solución basada microService también tiene desventajas potenciales:

• La elección de cómo particionar una aplicación en microservicios puede ser un reto, ya que cada microService tiene que ser
completamente autónomo, de extremo a extremo, incluyendo la responsabilidad de sus fuentes de datos.

• Los desarrolladores deben implementar la comunicación entre los distintos servicios, lo que añade complejidad y la latencia de la aplicación.

• transacciones atómicas entre múltiples microservicios por lo general no son posibles. Por lo tanto, los requisitos de
negocio deben abrazar consistencia eventual entre microservicios.

• En la producción, hay una complejidad operativa en la implementación y gestión de un sistema comprometido de muchos
servicios independientes.

• La comunicación directa entre el cliente y microService puede hacer que sea difícil de refactorizar los contratos de microservicios. Por
ejemplo, con el tiempo cómo el sistema se divide en servicios podrían tener que cambiar. Un único servicio puede dividirse en dos o más
servicios, y dos servicios podría fusionarse. Cuando los clientes se comunican directamente con microservicios, este trabajo refactorización
puede romper la compatibilidad con las aplicaciones cliente.

El uso de contenedores
El uso de contenedores es un enfoque para el desarrollo de software en la que una aplicación y su conjunto versionado de dependencias, además de su
configuración de entorno abstraído como archivos de manifiesto de implementación, se empaquetan juntos imagen un recipiente como, probados como una
unidad, y se despliegan a un sistema operativo anfitrión.

Un contenedor es un hecho aislado, recurso controlado y el entorno operativo portátil, donde una aplicación se puede ejecutar
sin tocar los recursos de otros contenedores, o el anfitrión. Por lo tanto, un recipiente parece y actúa como un equipo físico o
recién instalado una máquina virtual.

Hay muchas similitudes entre contenedores y máquinas virtuales, como se ilustra en la Figura 8-3.

53 CAPÍTULO 8 | microservicios en contenedores


Figura 8-3: Comparación de las máquinas virtuales y contenedores

Un contenedor corre un sistema operativo, tiene un sistema de archivos, y se puede acceder a través de una red como si se tratara de una máquina
física o virtual. Sin embargo, la tecnología y los conceptos utilizados por los envases son muy diferentes de las máquinas virtuales. Las máquinas
virtuales incluyen las aplicaciones, las dependencias necesarias, y un sistema operativo completo de invitados. Contenedores incluyen la aplicación y
sus dependencias, pero comparten el sistema operativo con otros recipientes, los procesos en ejecución como aislados en el sistema operativo
anfitrión (aparte de contenedores Hyper-V que se ejecutan dentro de una máquina virtual especial por contenedor). Por lo tanto, los recipientes
comparten recursos y por lo general requieren menos recursos que las máquinas virtuales.

La ventaja de un desarrollo orientado al recipiente y enfoque de implementación es que elimina la mayor parte de los problemas que surgen de las
configuraciones de entorno inconsistentes y los problemas que vienen con ellos. Además, los contenedores permiten la funcionalidad de escalado
aplicación rápida por de instancias nuevos contenedores como sea necesario.

Los conceptos clave en la creación y trabajo con los envases son:

• Contenedor de host: La máquina física o virtual configurado para alojar contenedores. El anfitrión recipiente se ejecutará uno o
más recipientes.

• Recipiente de archivo: Una imagen consiste en una unión de sistemas de archivos en capas apiladas una encima de la otra, y es la
base de un contenedor. Una imagen no tiene estado y nunca cambia a medida que se implementa en diferentes entornos.

• Contenedor: Un contenedor es una instancia de tiempo de ejecución de una imagen.

• Contenedor de imagen del sistema operativo: Los contenedores se despliegan a partir de imágenes. La imagen del sistema operativo contenedor es la
primera capa en potencialmente muchas capas de imagen que conforman un recipiente. Un sistema operativo contenedor es inmutable, y no puede ser
modificado.

• Recipiente Repositorio: Cada vez que se crea una imagen de contenedor, la imagen y sus dependencias se almacenan en un repositorio local. Estas
imágenes se pueden reutilizar muchas veces en el host contenedor. Las imágenes de contenedores también se pueden almacenar en un registro
público o privado, tales como acoplable Hub , De manera que puedan ser utilizados a través de diferentes anfitriones de contenedores.

54 CAPÍTULO 8 | microservicios en contenedores


Las empresas están adoptando cada vez más contenedores al implementar aplicaciones basadas MICROSERVICE y acoplable se ha convertido en
la aplicación contenedor estándar que ha sido adoptado por la mayoría de las plataformas de software y proveedores de la nube.

Referencia Los eShopOnContainers aplicación utiliza acoplable para albergar cuatro microservicios de back-end en contenedores, como se
ilustra en la Figura 8-4.

Figura 8-4: aplicación de referencia eShopOnContainers microservicios de back-end

La arquitectura de los servicios de back-end en la aplicación de referencia se descompone en múltiples sub-sistemas autónomos en forma
de colaborar microservicios y contenedores. Cada microService proporciona una única área de funcionalidad: un servicio de identidad, un
servicio de catálogo, un servicio de pedidos, y un servicio de canasta.

Cada microService tiene su propia base de datos, que le permite ser totalmente desacoplada de los demás microservicios. Cuando sea
necesario, la coherencia entre las bases de datos de diferentes microservicios se consigue utilizando eventos de nivel de aplicación.
Para más información, ver La comunicación entre microservicios .

Para obtener más información acerca de la aplicación de referencia, véase .NET microservicios: Arquitectura de Aplicaciones .NET en
contenedores .

La comunicación entre el cliente y el microservicios


La aplicación móvil eShopOnContainers comunica con los microservicios de back-end en contenedores utilizando directa
cliente-a-microService la comunicación, que se muestra en la Figura 8-5.

55 CAPÍTULO 8 | microservicios en contenedores


Figura 8-5: Directa cliente-a-microService comunicación

Con la comunicación directa entre el cliente y microService, la aplicación móvil hace peticiones a cada microService directamente a través de su
criterio de valoración pública, con un puerto diferente, por microService. En la producción, el punto final sería típicamente asignar a equilibrador de
carga de la microService, que distribuye solicitudes a través de las instancias disponibles.

Propina: Considere el uso de comunicación de puerta de enlace API

La comunicación directa entre el cliente y microService puede tener inconvenientes cuando se construye una aplicación basada microService
grande y compleja, pero es más que adecuado para una pequeña aplicación. En el diseño de una gran aplicación basada microService con
decenas de microservicios, considere el uso de la comunicación de puerta de enlace de la API. Para más información, ver .NET microservicios:
Arquitectura de Aplicaciones .NET en contenedores .

La comunicación entre microservicios


Una aplicación microservicios base es un sistema distribuido, potencialmente se ejecuta en múltiples máquinas. Cada instancia de servicio es
típicamente un proceso. Por lo tanto, los servicios deben interactuar utilizando un protocolo de comunicación entre procesos, como HTTP, TCP,
Protocolo avanzada mensaje Queuing (AMQP), o protocolos binarios, dependiendo de la naturaleza de cada servicio.

Los dos enfoques comunes para la comunicación de microService a microService son la comunicación HTTP REST basada al
consultar los datos, y la mensajería asincrónica ligeros cambios en la comunicación a través de múltiples microservicios.

la comunicación orientada a eventos basados ​mensajería asíncrona es crítico cuando se propaga a través de múltiples cambios microservicios.
Con este enfoque, un microService publica un evento notable cuando algo sucede, por ejemplo, cuando se actualiza una entidad comercial.
Otros microservicios suscribirse a estos eventos. Entonces, cuando un microService recibe un evento, que actualiza sus propias entidades de
negocio, lo que podría a su vez conducen a más eventos de ser publicados. Esta funcionalidad de publicación-suscripción se logra
generalmente con un bus de eventos.

Un bus de eventos permite la comunicación entre microservicios publicación-suscripción, sin necesidad de los componentes a ser explícitamente
consciente de uno al otro, como se muestra en la Figura 8-6.

56 CAPÍTULO 8 | microservicios en contenedores


Figura 8-6: Publicación-suscripción con un bus de eventos

Desde una perspectiva de la aplicación, el bus de eventos es simplemente un canal de publicación-suscripción expuesta a través de una interfaz. Sin
embargo, la forma en que se implementa el bus de eventos puede variar. Por ejemplo, una implementación bus de eventos podría utilizar RabbitMQ,
Azure Service Bus, u otros servicios tales como autobuses y NServiceBus MassTransit. Figura 8-7 muestra cómo se utiliza un bus de evento en las
eShopOnContainers referencia de aplicación.

Figura 8-7: la comunicación orientada a eventos asíncronos en la aplicación de referencia

El bus de eventos eShopOnContainers, implementado usando RabbitMQ, ofrece asíncrono uno a muchos funcionalidad de
publicación-suscripción. Esto significa que después de la publicación de un evento, puede haber varios suscriptores de escucha para el
mismo evento. Figura 8-9 ilustra esta relación.

57 CAPÍTULO 8 | microservicios en contenedores


Figura 8-9: Uno-a-muchos de comunicación

Este uno-a-muchos método de comunicación utiliza eventos para implementar las transacciones comerciales que abarcan múltiples
servicios, asegurando la coherencia de eventos entre los servicios. Una transacción eventual-coherente consiste en una serie de pasos
distribuidos. Por lo tanto, cuando el microService perfil de usuario recibe UpdateUser comando, actualiza los datos del usuario en su base de
datos y publica el
UserUpdated evento para el bus de eventos. Tanto el microService cesta y el ordenamiento microService han suscrito para recibir
este evento, y en respuesta al día su información del comprador en sus respectivas bases de datos.

Nota: El bus de eventos eShopOnContainers, implementado utilizando RabbitMQ, está destinado a ser utilizado sólo como una prueba de concepto.
Para los sistemas de producción, se deben considerar las implementaciones de bus evento alternativo.

Para obtener información acerca de la aplicación bus de eventos, consulte .NET microservicios: Arquitectura de Aplicaciones .NET en
contenedores .

Resumen
Microservicios ofrecen un enfoque para el desarrollo y despliegue de aplicaciones que es adecuado para la agilidad, la escala y los requisitos de
fiabilidad de aplicaciones en la nube modernas. Una de las principales ventajas de microservicios es que pueden ser escalados-cabo de forma
independiente, lo que significa que un área funcional específico se puede escalar que requiere más potencia de procesamiento o del ancho de
banda de red para apoyar la demanda, sin áreas innecesariamente escalado de la aplicación que no están experimentando aumento de la
demanda.

Un contenedor es un hecho aislado, recurso controlado y el entorno operativo portátil, donde una aplicación se puede ejecutar sin tocar los
recursos de otros contenedores, o el anfitrión. Las empresas están adoptando cada vez más contenedores al implementar aplicaciones
basadas MICROSERVICE y acoplable se ha convertido en la aplicación contenedor estándar que ha sido adoptado por la mayoría de las
plataformas de software y proveedores de la nube.

58 CAPÍTULO 8 | microservicios en contenedores


CAPÍTULO 9

Autenticacion y
autorizacion
La autenticación es el proceso de obtención de las credenciales de identificación tales como el nombre y la contraseña de un usuario, y la
validación de esas credenciales contra una autoridad. Si las credenciales son válidas, la entidad que presentó las credenciales se considera
una identidad autenticada. Una vez que la identidad ha sido autenticado, un proceso de autorización determina si esa identidad tiene
acceso a un recurso dado.

Hay muchos enfoques para la integración de la autenticación y autorización en una aplicación Xamarin.Forms que se comunica con una
aplicación Web ASP.NET MVC, incluyendo el uso de ASP.NET Core Identidad, proveedores de autenticación externos tales como
Microsoft, Google, Facebook, o Twitter, y la autenticación middleware. La aplicación móvil eShopOnContainers realiza la autenticación y
la autorización con una identidad microService contenedores que utiliza IdentityServer 4. La aplicación móvil solicita tokens de
seguridad de IdentityServer, ya sea para la autenticación de un usuario o de acceso a un recurso. Para IdentityServer para emitir
señales en nombre de un usuario, el usuario debe iniciar sesión en IdentityServer. Sin embargo, IdentityServer no proporciona una
interfaz de usuario o base de datos para la autenticación. Por lo tanto, en los eShopOnContainers referencia de aplicación, ASP.

Autenticación
Se requiere autenticación cuando una aplicación necesita conocer la identidad del usuario actual. mecanismo principal de ASP.NET básico para la identificación de
los usuarios es el sistema de suscripción de ASP.NET Core Identidad, que almacena la información del usuario en un almacén de datos configurado por el
desarrollador. Normalmente, este almacén de datos será una tienda de ADO.NET Entity Framework, aunque las tiendas personalizados o paquetes de terceros se
pueden utilizar para almacenar la información de identidad en el almacenamiento de Azure, DocumentDB, o en otros lugares.

Para los escenarios de autenticación que hacen uso de un almacén de datos de usuario local, y que persisten información de identidad entre las peticiones a
través de cookies (como es típico en las aplicaciones web ASP.NET MVC), ASP.NET Núcleo de identidad es una solución adecuada. Sin embargo, las
cookies no son siempre un medio natural de la persistencia y la transmisión de datos. Por ejemplo, una aplicación Web ASP.NET Core que expone los puntos
finales REST que se accede desde una aplicación móvil necesitará típicamente utilizar portador testigo de autenticación, ya que las galletas no se puede
utilizar en este escenario. Sin embargo, fichas al portador pueden ser fácilmente recuperados y se incluyen en la cabecera de autorización de solicitudes web
realizadas desde la aplicación móvil.

59 CAPÍTULO 9 | Autenticacion y autorizacion


La emisión de fichas al portador utilizando IdentityServer 4

IdentityServer 4 es un código abierto OpenID Connect y marco de OAuth 2.0 para ASP.NET Core, que se puede utilizar para muchos
escenarios de autenticación y autorización incluyendo la emisión de tokens de seguridad para los usuarios locales ASP.NET Core identidad.

Nota: OpenID Connect y OAuth 2.0 son muy similares, si bien tienen diferentes responsabilidades.

OpenID Connect es una capa de autenticación en la parte superior del protocolo OAuth 2.0. OAuth 2 es un protocolo que permite a las
aplicaciones solicitar tokens de acceso a un servicio de token de seguridad y los utilizan para comunicarse con las API. Esta delegación
reduce la complejidad, tanto en aplicaciones cliente y APIs ya que la autenticación y autorización pueden ser centralizados.

La combinación de OpenID Connect y OAuth 2.0 se combinan los dos problemas de seguridad fundamentales de la
autenticación y la API de acceso, y IdentityServer 4 es una implementación de estos protocolos.

En las aplicaciones que utilizan la comunicación directa entre el cliente y microService, tales como los eShopOnContainers referencia de la aplicación, una
microService autenticación dedicado que actúa como un servicio de token de seguridad (STS) se puede utilizar para autenticar a los usuarios, como se
muestra en la Figura 9-1. Para obtener más información acerca de la comunicación directa clientto-microService, véase La comunicación entre el cliente y el
microservicios .

Figura 9-1: Autenticación por un microService autenticación dedicado

La aplicación móvil eShopOnContainers comunica con el microService identidad, que utiliza IdentityServer 4 para realizar la
autenticación y control de acceso para las API. Por lo tanto, la aplicación móvil solicita tokens de IdentityServer, ya sea para la
autenticación de un usuario o de acceso a un recurso:

• IdentityServer usuarios con la autenticación se logra mediante la aplicación móvil de solicitar una identidad
token, que representa el resultado de un proceso de autenticación. Como mínimo, contiene un identificador para el
usuario, y la información sobre cómo y cuando el usuario autenticado. También puede contener datos de identidad
adicionales.

• El acceso a un recurso con IdentityServer se logra mediante la aplicación móvil de solicitar una acceso
token, que permite el acceso a un recurso de API. Los clientes solicitan testigos de acceso y las remitirá a la API. Los tokens de
acceso contienen información sobre el cliente y el usuario (si está presente). API luego usar esa información para autorizar el
acceso a sus datos.

Nota: Un cliente debe estar registrado en el IdentityServer antes de que pueda solicitar fichas.

Añadiendo IdentityServer a una aplicación web


Para que una aplicación web ASP.NET Core utilizar IdentityServer 4, hay que añadir a la solución de Visual Studio de la
aplicación web. Para más información, ver Configuración y visión general en la documentación IdentityServer.

60 CAPÍTULO 9 | Autenticacion y autorizacion


Una vez IdentityServer está incluido en solución de Visual Studio de la aplicación web, hay que añadir a la tubería de proceso de
peticiones HTTP de la aplicación web, de modo que pueda servir peticiones a OpenID Connect y OAuth 2.0 puntos finales. Esto se
consigue en el configurar en el método de la aplicación web
Puesta en marcha clase, como se demuestra en el ejemplo de código siguiente:

public void configurar (


IApplicationBuilder aplicación, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
. . .
app.UseIdentity ();
. . .
}

El orden es importante en el procesamiento de canalización de solicitudes HTTP de la aplicación web. Por lo tanto, IdentityServer hay que añadir a la tubería antes
de que el marco de interfaz de usuario que implementa la pantalla de inicio de sesión.

Configuración IdentityServer
IdentityServer deben estar configurados en el ConfigureServices en el método de la aplicación web
Puesta en marcha clase llamando al services.AddIdentityServer método, como se demuestra en el siguiente ejemplo de
código de la aplicación de referencia eShopOnContainers:

public void ConfigureServices (servicios) IServiceCollection


{
. . .
services.AddIdentityServer (x => x.IssuerUri = "nulo" )
. AddSigningCredential (Certificate.Get ())
. AddAspNetIdentity <ApplicationUser> ()
. AddConfigurationStore (constructor =>
builder.UseSqlServer (connectionString, opciones =>
options.MigrationsAssembly (migrationsAssembly)))
. AddOperationalStore (constructor =>
builder.UseSqlServer (connectionString, opciones =>
options.MigrationsAssembly (migrationsAssembly)))
. Services.AddTransient <IProfileService, ProfileService> ();
}

Después de llamar al services.AddIdentityServer método, las API de fluidez adicionales están llamados a configurar lo siguiente:

• Credenciales utilizadas para la firma.

• Principios activos y de los recursos de identidad que los usuarios pueden solicitar el acceso a.

• Los clientes que se conectan a solicitar fichas.

• ASP.NET identidad central.

Propina: Dinámicamente cargar la configuración IdentityServer 4

IdentityServer APIs de 4 permiten configurar IdentityServer desde una lista en memoria de objetos de configuración. En la aplicación de
referencia eShopOnContainers, estas colecciones en memoria están codificados en la aplicación. Sin embargo, en escenarios de producción
que se pueden cargar dinámicamente a partir de un archivo de configuración o de una base de datos.

Para obtener información sobre la configuración de IdentityServer utilizar ASP.NET Core Identidad, consulte Utilizando ASP.NET Core Identidad en la
documentación IdentityServer.

61 CAPÍTULO 9 | Autenticacion y autorizacion


Configuración de los recursos de la API

Al configurar los recursos de la API, las AddInMemoryApiResources método espera una


IEnumerable <ApiResource> colección. El ejemplo de código siguiente muestra el GetApis método que ofrece esta colección en la
aplicación de referencia eShopOnContainers:

público estático IEnumerable <ApiResource> GetApis ()


{
return new Lista <ApiResource>
{
nuevo ApiResource ( "pedidos" , "Órdenes de servicio" ),
nuevo ApiResource ( "cesta" , "Canasta de servicios" )
};
}

Este método especifica que deberían proteger el IdentityServer pedidos y cesta API. Por lo tanto, IdentityServer logró serán
requeridos tokens de acceso al hacer llamadas a estas API. Para obtener más información acerca de la ApiResource tipo, véase recursos
API en la documentación IdentityServer 4.

Configuración de recursos de identidad

Al configurar los recursos de identidad, las AddInMemoryIdentityResources método espera una


IEnumerable <IdentityResource> colección. recursos de la identidad son datos tales como nombre de usuario, nombre o dirección de correo electrónico.
Cada recurso de identidad tiene un nombre único, y los tipos de reclamaciones arbitrarias se puede asignar a la misma, la cual será incluido en el
testigo de identidad para el usuario. El ejemplo de código siguiente muestra el getResources método que ofrece esta colección en la aplicación de
referencia eShopOnContainers:

público estático IEnumerable <IdentityResource> getResources ()


{
return new Lista <IdentityResource>
{
nuevo IdentityResources.OpenId (),
nuevo IdentityResources.Profile ()
};
}

La especificación OpenID Conectar especifica alguna recursos de identidad estándar . El requisito mínimo es que se proporciona
soporte para la emisión de un identificador único para los usuarios. Esto se consigue mediante la exposición de la IdentityResources.OpenId
recursos identidad.

Nota: los IdentityResources clase soporta todos los ámbitos definidos en la especificación OpenID Connect (openid, correo
electrónico, perfil, teléfono y dirección).

IdentityServer también es compatible con la definición de los recursos identidad personalizada. Para más información, ver La definición de los recursos de identidad

personalizados en la documentación IdentityServer. Para obtener más información acerca de la

IdentityResource tipo, véase recursos de identidad en la documentación IdentityServer 4.

Configuración de clientes

Los clientes son aplicaciones que pueden solicitar las fichas de IdentityServer. Por lo general, los siguientes parámetros se deben definir para
cada cliente, como mínimo:

• Un identificador de cliente único.

• Las interacciones permitidas con el servicio de token (conocido como el tipo de concesión).

• La ubicación en la identidad y acceso fichas se envían a (conocido como una redirección URI).

62 CAPÍTULO 9 | Autenticacion y autorizacion


• Una lista de los recursos que el cliente tiene permitido el acceso a (conocido como ámbitos).

Cuando la configuración de clientes, las AddInMemoryClients método espera una IEnumerable <cliente>
colección. El ejemplo de código siguiente muestra la configuración para los eShopOnContainers aplicación móvil en el GetClients método
que ofrece esta colección en la aplicación de referencia eShopOnContainers:

público estático IEnumerable <cliente> GetClients (Dictionary < cuerda , cuerda > ClientsUrl)
{
return new Lista <cliente>
{
. . .
nuevo Cliente
{
ClientId = "Xamarin" ,
ClientName = "Cliente eShop Xamarin OpenID" ,
AllowedGrantTypes = GrantTypes.Hybrid,
clientSecrets =
{
nuevo Secreto( "secreto" .Sha256 ())
},
RedirectUris = {clientsUrl [ "Xamarin" ]},
RequireConsent = falso ,
RequirePkce = cierto ,
PostLogoutRedirectUris = { ps clientsUrl [ "Xamarin" ] } / Cuenta / redireccionante" },
AllowedCorsOrigins = { "Http: // eshopxamarin" },
AllowedScopes = nuevo lista < cuerda >
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.OfflineAccess,
"pedidos" ,
"cesta"
},
AllowOfflineAccess = cierto ,
AllowAccessTokensViaBrowser = cierto
},
. . .
};
}

Esta configuración especifica de datos para las siguientes propiedades:

• Identificación del cliente: Un identificador único para el cliente.

• Nombre del cliente: El nombre de visualización del cliente, que se utiliza para el registro y la pantalla de consentimiento.

• AllowedGrantTypes: Especifica cómo un cliente quiere interactuar con IdentityServer. Para más información, ver Configuración
del flujo de autenticación .

• clientSecrets: Especifica las credenciales secretas de cliente que se utilizan cuando se solicite tokens desde el punto final
token.

• RedirectUris: Especifica los URI permitidos a los que regresan a fichas o los códigos de autorización.

• RequireConsent: Especifica si se requiere una pantalla de consentimiento.

• RequirePkce: Especifica si los clientes que utilizan un código de autorización deben enviar una clave de prueba.

• PostLogoutRedirectUris: Especifica los URI permitidos a redireccionar después de cierre de sesión.

63 CAPÍTULO 9 | Autenticacion y autorizacion


• AllowedCorsOrigins: Especifica el origen del cliente para que IdentityServer puede permitir llamadas crossorigin desde el
origen.

• AllowedScopes: Especifica los recursos que el cliente tiene acceso. De manera predeterminada, un cliente no tiene acceso a ningún

recurso.

• AllowOfflineAccess: Especifica si el cliente puede solicitar tokens de actualización.

Configuración del flujo de autenticación

El flujo de autenticación entre un cliente y IdentityServer se puede configurar mediante la especificación de los tipos de subvenciones en el Client.AllowedGrantTypes
propiedad. Las especificaciones 2.0 OpenID Connect y OAuth definen un número de flujos de autenticación, incluyendo:

• Implícito. Este flujo está optimizado para aplicaciones basadas en el navegador y se debe utilizar ya sea para los usuarios de autenticación de
sólo, o de autenticación y token de acceso peticiones. Todas las fichas se transmiten a través del navegador, y no se les permite, por tanto,
características avanzadas como tokens de actualización.

• Código de Autorización. Este flujo proporciona la capacidad de recuperar fichas en un canal de retorno, en comparación con el canal
delantero del navegador, mientras que también soporta la autenticación del cliente.

• Híbrido. Este flujo es una combinación de los tipos de subvenciones implícitas código y autorización. El testigo de identidad se
transmite a través del canal navegador y contiene la respuesta del protocolo firmado junto con otros artefactos, tales como el
código de autorización. Después de la validación exitosa de la respuesta, el canal de retorno se debe utilizar para recuperar el
acceso y refrescar token.

Propina: Utilizar el flujo de autenticación híbrida

El flujo de autenticación híbrida mitiga una serie de ataques que se aplican al canal de navegador, y es el flujo recomendado
para aplicaciones nativas que quieren recuperar tokens de acceso (y posiblemente fichas de refresco).

Para obtener más información acerca de los flujos de autenticación, consulte Tipos de subvenciones en la documentación IdentityServer 4.

Realización de autenticación
Para IdentityServer para emitir señales en nombre de un usuario, el usuario debe iniciar sesión en IdentityServer. Sin embargo, IdentityServer no
proporciona una interfaz de usuario o base de datos para la autenticación. Por lo tanto, en la aplicación de referencia eShopOnContainers, ASP.NET
Core identidad se utiliza para este propósito.

La aplicación móvil eShopOnContainers autentica con IdentityServer con el híbrido flujo de autenticación, que se ilustra en la
Figura 9-2.

Figura 9-2: visión general de alto nivel del proceso de inicio de sesión

64 CAPÍTULO 9 | Autenticacion y autorizacion


Una solicitud de inicio de sesión se hace a < punto final de base>: 5105 / connect / autorizar. Después de una autenticación exitosa, IdentityServer
devuelve una respuesta de autenticación que contiene un código de autorización y un testigo de identidad. El código de autorización se envía
entonces a < base
punto final>: 5105 / connect / ficha, que responde con tokens de acceso, de identidad y de refresco.

El eShopOnContainers móvil app señales de salida de IdentityServer mediante el envío de una solicitud de
http: // <base de punto final>: 5105 / connect / EndSession, con parámetros adicionales. Después se produce señal de salida, IdentityServer responde
enviando un mensaje de cierre de sesión de redirección URI de vuelta a la aplicación móvil. Figura 9-3 ilustra este proceso.

Figura 9-3: visión general de alto nivel del proceso de cierre de sesión

En la aplicación móvil eShopOnContainers, la comunicación con IdentityServer se realiza por el


IdentityService clase, que implementa el IIdentityService interfaz. Esta interfaz especifica que la clase que implementa debe
proporcionar CreateAuthorizationRequest, CreateLogoutRequest,
y GetTokenAsync métodos.

Iniciando sesión

Cuando el usuario toca la INICIAR SESIÓN botón de la LoginView, el SignInCommand en el LoginViewModel


clase se ejecuta, que a su vez ejecuta el SignInAsync método. El ejemplo de código siguiente muestra este método:

asincrónico privado Tarea SignInAsync ()


{
. . .
LoginUrl = _identityService.CreateAuthorizationRequest ();
IsLogin = cierto ;
. . .
}

Este método invoca el CreateAuthorizationRequest método en el IdentityService clase, que se muestra en el ejemplo de
código siguiente:

público cuerda CreateAuthorizationRequest ()


{
// crear URI hasta el punto final de autorización
var AuthorizeRequest = nuevo AuthorizeRequest ( Configuración global .Instance.IdentityEndpoint);

// diccionario con valores para autorizar la solicitud


var DIC = nuevo Diccionario < cuerda , cuerda > ();
dic.Add ( "Identificación del cliente" , Configuración global .Instance.ClientId);
dic.Add ( "Client_secret" , Configuración global .Instance.ClientSecret);
dic.Add ( "Response_type" , "Código id_token" );
dic.Add ( "alcance" , "Ubicaciones openid órdenes perfil cesta comercialización offline_access" );
dic.Add ( "Redirect_uri" , Configuración global .Instance.IdentityCallback);
dic.Add ( "mientras tanto" , Guid .NewGuid (). ToString ( "NORTE" ));
dic.Add ( "Code_challenge" , CreateCodeChallenge ());
dic.Add ( "Code_challenge_method" , "S256" );

sesenta y cinco CAPÍTULO 9 | Autenticacion y autorizacion


// Añadir token CSRF para proteger contra ataques de cross-site solicitud falsificación.
var currentCSRFToken = Guid .NewGuid (). ToString ( "NORTE" );
dic.Add ( "estado" , CurrentCSRFToken);

var authorizeUri = authorizeRequest.Create (DIC);


regreso authorizeUri;
}

Este método crea el URI de IdentityServer punto final de autorización , Con los parámetros requeridos. El punto final de autorización es en / conectar /
autorizar en el puerto 5105 de la base de punto final expuesto como un ajuste de usuario. Para obtener más información acerca de la configuración del
usuario, consulte gestión de la configuración .

Nota: La superficie de ataque de la aplicación móvil eShopOnContainers se reduce mediante la aplicación de la clave de prueba para el Código
de Cambio (PKCe) extensión a OAuth. PKCe protege el código de autorización que se utilice si es interceptada. Esto se logra por el cliente
generando un verificador secreta, un hash de los cuales se pasa en la solicitud de autorización, y de la que se presenta sin troceo al canjear el
código de autorización. Para obtener más información acerca de PKCe, véase Clave de prueba para Exchange Código por parte de los clientes
públicos OAuth en el sitio web de Internet Engineering Task Force.

El URI devuelto se almacena en el loginUrl propiedad de la LoginViewModel clase. Cuando el IsLogin


propiedad se convierte cierto, el WebView en el LoginView se hace visible. los WebView los datos se une a su
Fuente alojamiento hasta el loginUrl propiedad de la LoginViewModel clase, y así hace una petición de inicio de sesión a IdentityServer
cuando el loginUrl propiedad se establece en la autorización del punto final IdentityServer. Cuando IdentityServer recibe esta solicitud y el
usuario no está autenticado, el WebView será redirigido a la página de acceso configurado, que se muestra en la Figura 9-4.

Figura 9-4: página de la conexión mostrada por el WebView

Una vez que se ha completado la conexión, el WebView será redirigido a un URI de retorno. Esta WebView navegación hará que el NavigateAsync
método en el LoginViewModel clase para ser ejecutado, que se muestra en el ejemplo de código siguiente:

66 CAPÍTULO 9 | Autenticacion y autorizacion


asincrónico privado Tarea NavigateAsync ( cuerda url)
{
. . .
var authResponse = nuevo AuthorizeResponse (Url);
Si (! cuerda .IsNullOrWhiteSpace (authResponse.Code))
{
var UserToken = esperar _identityService.GetTokenAsync (authResponse.Code);
cuerda accessToken = userToken.AccessToken;

Si (! cuerda .IsNullOrWhiteSpace (accessToken))


{
ajustes .AuthAccessToken = accessToken;
ajustes .AuthIdToken = authResponse.IdentityToken;

esperar NavigationService.NavigateToAsync < MainViewModel > ();


esperar NavigationService.RemoveLastFromBackStackAsync ();
}
}
. . .
}

Este método analiza la respuesta de autenticación que está contenida en el retorno URI, y siempre que un código de autorización válida está
presente, se hace una petición a la IdentityServer punto final simbólico , Pasando el código de autorización, el verificador PKCe secreto, y otros
parámetros necesarios. El punto final de token es en / conectar / ficha en el puerto 5105 de la base de punto final expuesto como un ajuste de usuario.
Para obtener más información acerca de la configuración del usuario, consulte gestión de la configuración .

Propina: Validar los URI de retorno

Aunque la aplicación móvil eShopOnContainers no valida el retorno URI, la mejor práctica es validar que el retorno URI se
refiere a un lugar conocido con el fin de prevenir abierta-redirigir los ataques.

Si el punto final testigo recibe un código de autorización y PKCe verificador secreto válida, responde con un token de acceso, testigo de
identidad, y refrescar token. El token de acceso (que permite el acceso a los recursos API) y testigo de identidad se almacenan entonces como
configuración de la aplicación, y se realiza la navegación de páginas. Por lo tanto, el efecto global en la aplicación móvil eShopOnContainers es
la siguiente: siempre que los usuarios pueden autenticarse con éxito con IdentityServer, que se puede navegar a la Vista principal

página, que es una TabbedPage que muestra el CatalogView como su ficha seleccionada.

Para obtener información sobre la navegación de páginas, consulte Navegación . Para obtener información acerca de cómo WebView

navegación provoca un método vista del modelo para ser ejecutado, ver La invocación de comportamientos de navegación utilizando . Para obtener información sobre la
configuración de la aplicación, consulte gestión de la configuración .

Nota: Los eShopOnContainers también permite un inicio de sesión simulada cuando la aplicación está configurada para utilizar los servicios de simulacros
en el SettingsView. En este modo, la aplicación no se comunica con IdentityServer, permitiendo así que el usuario para inscribirse en el uso de las
credenciales.

Cerrando sesión

Cuando el usuario toca la CERRAR SESIÓN en el botón ProfileView, el LogoutCommand en el


ProfileViewModel clase se ejecuta, que a su vez ejecuta el LogoutAsync método. Este método realiza la navegación de páginas a la LoginView
página, pasando una LogoutParameter ejemplo establecido en cierto
como un parámetro. Para obtener más información sobre cómo pasar parámetros durante la navegación de páginas, consulte Paso de parámetros durante la
navegación .

67 CAPÍTULO 9 | Autenticacion y autorizacion


Cuando se crea y se navega a la vista InitializeAsync se ejecuta el método de modelo vista asociada de la vista, que ejecuta
entonces la Cerrar sesión método de la LoginViewModel clase, que se muestra en el ejemplo de código siguiente:

private void Cerrar sesión()


{
var authIdToken = ajustes .AuthIdToken;
var LogoutRequest = _identityService.CreateLogoutRequest (authIdToken);

Si (! cuerda .IsNullOrEmpty (LogoutRequest))


{
// Cerrar sesión

LoginUrl = LogoutRequest;
}
. . .
}

Este método invoca el CreateLogoutRequest método en el IdentityService clase, pasando el testigo de identidad recuperada de configuración de la
aplicación como un parámetro. Para obtener más información acerca de configuración de la aplicación, consulte gestión de la configuración . El
ejemplo de código siguiente muestra el
CreateLogoutRequest método:

público cuerda CreateLogoutRequest ( cuerda simbólico)


{
. . .
cadena de retorno .Formato( " {0} ? Id_token_hint = {1} Y = post_logout_redirect_uri {2} " ,
Configuración global .Instance.LogoutEndpoint,
simbólico,
Configuración global .Instance.LogoutCallback);
}

Este método crea la URI de IdentityServer sesión de punto final final , Con los parámetros requeridos. El punto final de la sesión final es en / conectar /
EndSession en el puerto 5105 de la base de punto final expuesto como un ajuste de usuario. Para obtener más información acerca de la configuración del

usuario, consulte gestión de la configuración .

El URI devuelto se almacena en el loginUrl propiedad de la LoginViewModel clase. Mientras que la IsLogin
propiedad es cierto, el WebView en el LoginView es visible. los WebView los datos se une a su Fuente alojamiento hasta el loginUrl propiedad de la LoginViewModel
clase, y así hace una solicitud de registro de salida a IdentityServer cuando el loginUrl propiedad se establece en la sesión final del punto final
IdentityServer. Cuando IdentityServer recibe esta solicitud, siempre que el usuario se accede a la cuenta, cierre de sesión se produce. La
autenticación se realiza un seguimiento con una galleta gestionado por el middleware de autenticación de cookies desde ASP.NET Core. Por lo tanto,
la firma de IdentityServer elimina la cookie de autenticación y envía un mensaje de cierre de sesión de redirección URI de vuelta al cliente.

En la aplicación móvil, la WebView será redirigido al puesto de cierre de sesión URI de redireccionamiento. Esta WebView
navegación hará que el NavigateAsync método en el LoginViewModel clase para ser ejecutado, que se muestra en el ejemplo de
código siguiente:

asincrónico privado Tarea NavigateAsync ( cuerda url)


{
. . .
ajustes .AuthAccessToken = cuerda .Vacío;
ajustes .AuthIdToken = cuerda .Vacío;
IsLogin = falso ;
LoginUrl = _identityService.CreateAuthorizationRequest ();
. . .
}

68 CAPÍTULO 9 | Autenticacion y autorizacion


Este método borra tanto el testigo de identidad y el token de acceso de configuración de la aplicación, y establece el IsLogin propiedad a falso,
que hace que el WebView sobre el LoginView página a hacerse invisible. Finalmente, el loginUrl propiedad se establece en el URI del
IdentityServer de punto final de autorización , Con los parámetros requeridos, en preparación para la próxima vez que el usuario inicia una
señal de entrada.

Para obtener información sobre la navegación de páginas, consulte Navegación . Para obtener información acerca de cómo WebView

navegación provoca un método vista del modelo para ser ejecutado, ver La invocación de comportamientos de navegación utilizando . Para obtener información sobre la

configuración de la aplicación, consulte gestión de la configuración .

Nota: Los eShopOnContainers también permite a un simulacro de cierre de sesión cuando la aplicación está configurada para utilizar los servicios de
simulacros en el SettingsView. En este modo, la aplicación no se comunica con IdentityServer, y en su lugar borra cualquier fichas almacenadas de
configuración de la aplicación.

Autorización
Después de la autenticación, API web ASP.NET Core a menudo necesitan para autorizar el acceso, lo que permite un servicio para hacer APIs disponibles para
algunos usuarios autenticados, pero no a todos.

La restricción del acceso a una ruta ASP.NET MVC Core se puede lograr mediante la aplicación de una Autorizar atribuir a un controlador o acción,
que limita el acceso al controlador o acción a los usuarios autenticados, como se muestra en el ejemplo de código siguiente:

[Autorizar]
clase pública BasketController : Controlador
{
. . .
}

Si un usuario no autorizado intenta acceder a un controlador o acción que está marcado con el Autorizar
atributo, el marco MVC devuelve un código de estado 401 (no autorizado) HTTP.

Nota: Los parámetros pueden ser especificados en el Autorizar atribuir a restringir una API para usuarios específicos. Para más
información, ver Autorización en el Centro de Documentación de Microsoft.

69 CAPÍTULO 9 | Autenticacion y autorizacion


IdentityServer se pueden integrar en el flujo de trabajo de autorización para que el acceso que proporciona fichas de autorización de control.
Este enfoque se muestra en la Figura 9-5.

Figura 9-5: La autorización de token de acceso

La aplicación móvil eShopOnContainers se comunica con el microService identidad y solicita un token de acceso como parte del proceso de
autenticación. El token de acceso se envía entonces a las API expuestas por los microservicios cesta de pedidos y como parte de las solicitudes
de acceso. Los tokens de acceso contienen información sobre el cliente y el usuario. API luego usar esa información para autorizar el acceso a
sus datos. Para obtener información acerca de cómo configurar IdentityServer para proteger las API, consulte Configuración de los recursos de la
API .

Configuración IdentityServer para realizar la autorización


Para realizar la autorización con IdentityServer, su middleware autorización debe ser añadido a canalización de solicitudes HTTP
de la aplicación web. El middleware se añade en el ConfigureAuth en el método de la aplicación web Puesta en marcha clase, que se
invoca desde el configurar método, y se demuestra en el siguiente ejemplo de código de referencia de aplicación los
eShopOnContainers:

protegido virtual void ConfigureAuth (IApplicationBuilder app)


{
var identityUrl = Configuration.GetValue < cuerda > ( "IdentityUrl" );
app.UseIdentityServerAuthentication ( nuevo IdentityServerAuthenticationOptions
{
Autoridad = identityUrl.ToString (),
nombreDeÁmbito = "cesta" ,
RequireHttpsMetadata = falso
});
}

Este método asegura que la API sólo se puede acceder con un token de acceso válido. El middleware valida el token entrante para
asegurar que se envía desde un emisor de confianza, y valida que el token es válido para ser utilizado con la API que lo recibe. Por lo
tanto, la navegación al controlador de pedido o cesta devolverá un código de estado HTTP 401 (no autorizado), lo que indica que se
requiere un token de acceso.

70 CAPÍTULO 9 | Autenticacion y autorizacion


Nota: middleware autorización de IdentityServer se debe agregar a canalización de solicitudes HTTP de la aplicación web
antes de añadir con MVC app.UseMvc () o app.UseMvcWithDefaultRoute ().

Hacer peticiones de acceso a las API

Al hacer peticiones a los microservicios de pedidos y la cesta, el token de acceso, obtenidos a partir IdentityServer durante el
proceso de autenticación, debe incluirse en la solicitud, como se muestra en el siguiente ejemplo de código:

var authToken = ajustes .AuthAccessToken;


order = esperar _ordersService.GetOrderAsync ( Convertir .ToInt32 (order.OrderNumber), authToken);

El token de acceso se almacena como un valor de la aplicación, y se recupera de almacenamiento específico de la plataforma y se incluye
en la llamada a la GetOrderAsync método en el OrderService clase.

Del mismo modo, el token de acceso se debe incluir al enviar datos a una API protegida IdentityServer, como se muestra en el siguiente
ejemplo de código:

var authToken = ajustes .AuthAccessToken;


esperar _basketService.UpdateBasketAsync ( nuevo CustomerBasket
{
ID_COMPRADOR = userInfo.UserId,
Artículos = BasketItems.ToList ()
}, AuthToken);

El token de acceso se recupera de almacenamiento específico de la plataforma y se incluye en la llamada a la


UpdateBasketAsync método en el BasketService clase.

los RequestProvider clase, en la aplicación móvil eShopOnContainers, utiliza el HttpClient clase para hacer peticiones a las APIs
REST expuestos por los eShopOnContainers referencia de la aplicación. Al hacer peticiones a los pedidos y de la cesta API, que
requieren autorización, un token de acceso válido debe ser incluida en la solicitud. Esto se consigue añadiendo el token de
acceso a las cabeceras del
HttpClient ejemplo, como se demuestra en el ejemplo de código siguiente:

httpClient.DefaultRequestHeaders.Authorization = nuevo AuthenticationHeaderValue ( "Portador" , Token);

los DefaultRequestHeaders propiedad de la HttpClient clase expone las cabeceras que se envían con cada solicitud, y se añade el
token de acceso a la Autorización Header prefijado con la cadena Portador. Cuando la solicitud se envía a una API REST, el valor
de la Autorización cabecera se extrae y se valida para asegurar que es enviado desde un emisor de confianza, y se utiliza para
determinar si el usuario tiene permiso para invocar el API que lo recibe.

Para obtener más información acerca de cómo la aplicación móvil eShopOnContainers hace peticiones web, consulte
El acceso a datos remotos .

Resumen
Hay muchos enfoques para la integración de la autenticación y autorización en una aplicación Xamarin.Forms que se comunica con una
aplicación Web ASP.NET MVC. La aplicación móvil eShopOnContainers realiza la autenticación y la autorización con una identidad
microService contenedores que utiliza IdentityServer 4. IdentityServer es un código abierto OpenID Connect y marco de OAuth 2.0 para
ASP.NET Core que se integra con ASP.NET Núcleo de identidad para realizar autenticación de señales portador.

71 CAPÍTULO 9 | Autenticacion y autorizacion


La aplicación móvil solicita tokens de seguridad de IdentityServer, ya sea para la autenticación de un usuario o de acceso a un recurso.
Al acceder a un recurso, un token de acceso se debe incluir en la solicitud de API que requieren autorización. middleware de
IdentityServer valida tokens de acceso de entrada para asegurarse de que son enviados desde un emisor de confianza, y que son
válidos para ser utilizado con la API que los recibe.

72 CAPÍTULO 9 | Autenticacion y autorizacion


CAPÍTULO 10

El acceso a datos remotos

Muchas de las soluciones basadas en la web modernos hacen uso de los servicios web, alojados en servidores web, para proporcionar la funcionalidad de
las aplicaciones de cliente remoto. Las operaciones que expone un servicio web constituye una API web.

aplicaciones cliente debe ser capaz de utilizar la API de Web sin saber que las revelaciones de la API se implementan los datos u operaciones. Esto
requiere que la API se rige por las normas comunes que permiten a una aplicación de cliente y el servicio web para ponerse de acuerdo sobre qué
formatos de datos que desea utilizar, y la estructura de los datos que se intercambian entre aplicaciones de cliente y el servicio web.

Introducción a la Transferencia de estado representacional


Transferencia de estado representacional (REST) ​es un estilo de arquitectura para la construcción de sistemas distribuidos basados ​en hipermedia. Una
de las principales ventajas del modelo REST es que está basado en estándares abiertos y no se une a la aplicación del modelo o las aplicaciones
cliente que tienen acceso a cualquier aplicación específica. Por lo tanto, un servicio web REST podría ser implementado usando Microsoft ASP.NET
MVC Core y aplicaciones de cliente podría estar desarrollando el uso de cualquier idioma y conjunto de herramientas que puede generar peticiones
HTTP y analizar las respuestas HTTP.

El modelo REST utiliza un esquema de navegación para representar objetos y servicios a través de una red, que se refiere a los recursos. Los
sistemas que implementan RESTO suelen utilizar el protocolo HTTP para transmitir las solicitudes de acceso a estos recursos. En tales
sistemas, una aplicación de cliente envía una solicitud en forma de un URI que identifica un recurso, y un método de HTTP (tales como GET,
POST, PUT o DELETE) que indica la operación a realizar en ese recurso. El cuerpo de la petición HTTP contiene todos los datos necesarios
para realizar la operación.

Nota: REST define un modelo de petición sin estado. Por lo tanto, las solicitudes HTTP deben ser independientes y pueden ocurrir en cualquier orden.

La respuesta de una solicitud REST hace uso de códigos de estado HTTP estándar. Por ejemplo, una petición que devuelve datos válidos debe
incluir el código de respuesta HTTP 200 (OK), mientras que una solicitud que no termina de encontrar o eliminar un recurso especificado debe
devolver una respuesta que incluye el código de estado HTTP 404 (no encontrado).

Una web API REST expone un conjunto de recursos conectados, y proporciona las operaciones básicas que permiten a una aplicación para
manipular esos recursos y navegar fácilmente entre ellos. Por esta razón, los URIs que constituyen una API REST Web típica están
orientados a los datos que se expone, y el uso de las facilidades proporcionadas por HTTP para operar en estos datos.

73 CAPÍTULO 10 | El acceso a datos remotos


Los datos incluidos por una aplicación cliente en una petición HTTP, y los mensajes de respuesta correspondiente del servidor web, podrían presentarse
en una variedad de formatos, conocidos como tipos de medios. Cuando una aplicación de cliente envía una solicitud que devuelve datos en el cuerpo de
un mensaje, se puede especificar el tipo de papel que puede manejar en el Aceptar encabezado de la solicitud. Si el servidor web es compatible con este
tipo de medio, puede responder con una respuesta que incluya la Tipo de contenido cabecera que especifica el formato de los datos en el cuerpo del
mensaje. Es entonces la responsabilidad de la aplicación cliente para analizar el mensaje de respuesta e interpretar los resultados en el cuerpo del
mensaje de manera apropiada.

Para obtener más información acerca de los silencios, véase diseño de la API y implementación de la API en Microsoft Docs.

El consumo de API REST


La aplicación móvil eShopOnContainers utiliza el patrón Modelo-Vista-ViewModel (MVVM), y los elementos del modelo del patrón representan
las entidades de dominio utilizados en la aplicación. El controlador y clases de repositorio en la aplicación de referencia eShopOnContainers
aceptan y devuelven muchos de estos modelo objetos. Por lo tanto, se utilizan como objetos de transferencia de datos (DTO) que sostienen
todos los datos que se pasa entre la aplicación móvil y los microservicios en contenedores. La principal ventaja de utilizar dtos para pasar datos
y recibir datos de un servicio web es que mediante la transmisión de más datos en una sola llamada remota, la aplicación puede reducir el
número de llamadas remotas que necesitan ser hechas.

Hacer peticiones web


Los eShopOnContainers aplicación móvil que utiliza el HttpClient clase para hacer peticiones a través de HTTP, con JSON se utiliza como tipo de
soporte. Esta clase proporciona funcionalidad para el envío de peticiones HTTP de forma asíncrona y recibir respuestas HTTP desde un URI de
recurso identificado. los HttpResponseMessage
clase representa un mensaje de respuesta HTTP recibida de una API REST después de una petición HTTP se ha hecho. Contiene información
acerca de la respuesta, incluido el código de estado, las cabeceras, y cualquier cuerpo. los HttpContent clase representa las cabeceras HTTP del
cuerpo y de contenido, tales como Tipo de contenido y
Content-Encoding. El contenido puede ser leído utilizando cualquiera de las Leído como métodos, tales como

ReadAsStringAsync y ReadAsByteArrayAsync, dependiendo del formato de los datos.

Hacer una petición GET

los CatalogService clase se utiliza para gestionar el proceso de recuperación de datos del catálogo microService.
En el RegisterDependencies método en el ViewModelLocator clase, el
CatalogService clase está registrada como una asignación de tipo de contra el ICatalogService escriba con el contenedor de inyección de
dependencias Autofac. Entonces, cuando una instancia de la CatalogViewModel Se crea la clase, su constructor acepta una ICatalogService tipo,
que Autofac resuelve, volviendo una instancia de la CatalogService clase. Para obtener más información acerca de la inyección de
dependencias, consulte
Introducción a la inyección de dependencias .

La Figura 10-1 muestra la interacción de clases que leer los datos del catálogo del catálogo microService para la visualización por el CatalogView.

74 CAPÍTULO 10 | El acceso a datos remotos


Figura 10-1: La recuperación de datos desde el catálogo microService

Cuando el CatalogView se navega a la OnInitialize método en el CatalogViewModel clase se llama. Este método recupera datos del
catálogo del catálogo microService, como se demuestra en el ejemplo de código siguiente:

público asíncrono de anulación Tarea InitializeAsync ( objeto navigationData)


{
. . .
productos = esperar _productsService.GetCatalogAsync ();
. . .
}

Este método llama al GetCatalogAsync método de la CatalogService instancia que se inyecta en el CatalogViewModel por Autofac.
El ejemplo de código siguiente muestra el GetCatalogAsync
método:

asíncrono pública Tarea < ObservableCollection < CatalogItem >> GetCatalogAsync ()


{
UriBuilder constructor = nuevo UriBuilder ( Configuración global .Instance.CatalogEndpoint);
builder.Path = "api / v1 / Catálogo / artículos" ;
cuerda uri = builder.ToString ();

CatalogRoot catálogo = esperar _requestProvider.GetAsync < CatalogRoot > (URI);


. . .

75 CAPÍTULO 10 | El acceso a datos remotos


regreso ? Catálogo .Data.ToObservableCollection ();
}

Este método se basa la URI que identifica el recurso de la solicitud será enviada a, y utiliza el
RequestProvider clase para invocar el método HTTP GET sobre el recurso, antes de devolver los resultados a la CatalogViewModel. los RequestProvider
clase contiene funcionalidad que presenta una solicitud en forma de un URI que identifica un recurso, un método de HTTP que
indica la operación a realizar en ese recurso, y un cuerpo que contiene todos los datos necesarios para realizar la operación. Para
obtener información acerca de cómo el RequestProvider clase se inyecta en el CatalogService clase, consulte

Introducción a la inyección de dependencias .

El ejemplo de código siguiente muestra el GetAsync método en el RequestProvider clase:

asíncrono pública Tarea < TResult > GetAsync < TResult > ( cuerda uri, cuerda token = "" )
{
HttpClient HttpClient = CreateHttpClient (token);
HttpResponseMessage respuesta = esperar httpClient.GetAsync (URI);

esperar HandleResponse (respuesta);


cuerda = serializados esperar response.Content.ReadAsStringAsync ();

TResult resultado = esperar Tarea .Llevar (() =>


JsonConvert .DeserializeObject < TResult > (Serializados, _serializerSettings));

regreso resultado;
}

Este método llama al CreateHttpClient método, que devuelve una instancia de la HttpClient clase con las cabeceras apropiadas
establecidas. A continuación, envía una solicitud GET asíncrona al recurso identificado por el URI, con la respuesta que se
almacena en el HttpResponseMessage ejemplo. los
handleResponse a continuación, se invoca el método, que lanza una excepción si la respuesta no incluye un código de estado HTTP éxito. A
continuación, la respuesta se lee como una cadena, convertida de JSON a una
CatalogRoot oponerse, y devuelto a la CatalogService.

los CreateHttpClient método se muestra en el ejemplo de código siguiente:

privado HttpClient CreateHttpClient ( cuerda token = "" )


{
var HttpClient = nuevo HttpClient ();
httpClient.DefaultRequestHeaders.Accept.Add (
nuevo MediaTypeWithQualityHeaderValue ( "Application / json" ));

Si (! cuerda .IsNullOrEmpty (token))


{
httpClient.DefaultRequestHeaders.Authorization =
nuevo AuthenticationHeaderValue ( "Portador" , Token);
}
regreso HttpClient;
}

Este método crea una nueva instancia de la HttpClient clase, y los conjuntos Aceptar cabecera de cualesquiera solicitudes hechas por el HttpClient
a instancia application / json, lo que indica que se espera que el contenido de cualquier respuesta a ser formateado usando JSON.
Entonces, si un testigo de acceso se pasa como argumento a la CreateHttpClient método, se añade a la Autorización cabecera de
cualesquiera solicitudes hechas por el HttpClient ejemplo, un prefijo con la cadena Portador. Para obtener más información sobre la
autorización, consulte Autorización .

76 CAPÍTULO 10 | El acceso a datos remotos


Cuando el GetAsync método en el RequestProvider llamadas de clase HttpClient.GetAsync, el Artículos
método en el CatalogController clase en el Catalog.API proyecto se invoca, que se muestra en el ejemplo de código siguiente:

[HttpGet]
[Ruta( "[acción]" )]
asíncrono pública Tarea <> IActionResult artículos (
[FromQuery] En t pageSize = 10, [FromQuery] En t pageIndex = 0 )
{
var TotalItems = esperar _catalogContext.Catalog ite Sra
. LongCountAsync ();

var itemsOnPage = esperar _catalogContext.CatalogItems


. Ord Erby (c => c.Name)
. Saltar (pageSize * pageIndex)
. Tome (pageSize)
. ToListAsync ();

itemsOnPage = ComposePicUri (itemsOnPage);


v modelo ar = nuevo PaginatedItemsViewModel <CatalogItem> (
pageIndex, pageSize, TotalItems, itemsOnPage);

r eturn Ok (modelo);
}

Este método recupera los datos del catálogo de la base de datos SQL usando ADO.NET Entity Framework, y lo devuelve como un mensaje de
respuesta que incluye un código de estado HTTP éxito, y una colección de formato JSON
CatalogItem instancias.

Hacer una solicitud POST

los BasketService clase se utiliza para gestionar la recuperación de datos y el proceso de actualización con el microService cesta.
En el RegisterDependencies método en el ViewModelLocator clase, el
BasketService clase está registrada como una asignación de tipo de contra el IBasketService escriba con el contenedor de inyección de
dependencias Autofac. Entonces, cuando una instancia de la BasketViewModel Se crea la clase, su constructor acepta una IBasketService tipo,
que Autofac resuelve, volviendo una instancia de la BasketService clase. Para obtener más información acerca de la inyección de
dependencias, consulte
Introducción a la inyección de dependencias .

Figura 10-2 muestra la interacción de las clases que envían los datos de la cesta mostrados por el BasketView,
microService a la cesta.

77 CAPÍTULO 10 | El acceso a datos remotos


Figura 10-2: El envío de datos a la cesta microService

Cuando se añade un elemento a la cesta de la compra, el ReCalculateTotalAsync método en el


BasketViewModel clase se llama. Este método actualiza el valor total de los artículos de la canasta, y envía los datos a la cesta
microService cesta, como se demuestra en el siguiente ejemplo de código:

asincrónico privado Tarea ReCalculateTotalAsync ()


{
. . .
esperar _basketService.UpdateBasketAsync ( nuevo CustomerBasket
{
ID_COMPRADOR = userInfo.UserId,
Artículos = BasketItems.ToList ()
}, AuthToken);
}

Este método llama al UpdateBasketAsync método de la BasketService instancia que se inyecta en el BasketViewModel por Autofac.
El método siguiente muestra el UpdateBasketAsync
método:

asíncrono pública Tarea < CustomerBasket > UpdateBasketAsync ( CustomerBasket customerBasket, cuerda simbólico)
{
UriBuilder constructor = nuevo UriBuilder ( Configuración global .Instance.BasketEndpoint);
cuerda uri = builder.ToString ();

78 CAPÍTULO 10 | El acceso a datos remotos


var resultado = esperar _requestProvider.PostAsync (uri, customerBasket, token);
regreso resultado;
}

Este método se basa la URI que identifica el recurso de la solicitud será enviada a, y utiliza el
RequestProvider clase para invocar el método POST de HTTP en el recurso, antes de devolver los resultados a la BasketViewModel. Tenga en
cuenta que un token de acceso, obtenidos a partir IdentityServer durante el proceso de autenticación, se requiere para autorizar las
solicitudes a la microService cesta. Para obtener más información sobre la autorización, consulte Autorización .

El ejemplo de código siguiente muestra una de las PostAsync métodos en el RequestProvider clase:

asíncrono pública Tarea < TResult > PostAsync < TResult > (
cuerda uri, TResult datos, cuerda token = "" , cuerda encabezado = "" )
{
HttpClient HttpClient = CreateHttpClient (token);
. . .
var content = nuevo StringContent ( JsonConvert .SerializeObject (datos));
content.Headers.ContentType = nuevo MediaTypeHeaderValue ( "Application / json" );
HttpResponseMessage respuesta = esperar httpClient.PostAsync (uri, contenido);

esperar HandleResponse (respuesta);


cuerda = serializados esperar response.Content.ReadAsStringAsync ();

TResult resultado = esperar Tarea .Llevar (() =>


JsonConvert .DeserializeObject < TResult > (Serializados, _serializerSettings));

regreso resultado;
}

Este método llama al CreateHttpClient método, que devuelve una instancia de la HttpClient clase con las cabeceras apropiadas
establecidas. A continuación, envía una solicitud POST asíncrona al recurso identificado por el URI, con los datos de la cesta en
serie que se envían en formato JSON, y la respuesta se almacena en el HttpResponseMessage ejemplo. los handleResponse a
continuación, se invoca el método, que lanza una excepción si la respuesta no incluye un código de estado HTTP éxito. Entonces, la
respuesta se lee como una cadena, convertida de JSON a una CustomerBasket oponerse, y devuelto a la

BasketService. Para obtener más información acerca del método CreateHttpClient, véase Hacer una petición GET .

Cuando el PostAsync método en el RequestProvider llamadas de clase HttpClient.PostAsync, el Enviar


método en el BasketController clase en el Basket.API proyecto se invoca, que se muestra en el ejemplo de código siguiente:

[HttpPost]
asíncrono pública Tarea <IActionResult> Post ([FromBody] CustomerBasket valor )
{
var = cesta esperar _repository.U pda teBasketAsync ( Virginia Lue);
r eturn Ok (cesta);
}

Este método utiliza una instancia de la RedisBasketRepository clase de persistir los datos de la cesta a la caché Redis, y lo devuelve
como un mensaje de respuesta que incluye un código de estado HTTP éxito, y un formato JSON CustomerBasket ejemplo.

Hacer una solicitud DELETE

La figura 10-3 muestra las interacciones de las clases que eliminan los datos de la cesta de la microService cesta, para la CheckoutView.

79 CAPÍTULO 10 | El acceso a datos remotos


Figura 10-3: Eliminación de los datos de la cesta microService

Cuando se invoca el proceso de pago, la CheckoutAsync método en el CheckoutViewModel clase se llama. Este método crea un
nuevo orden, antes de borrar la cesta de la compra, como se demuestra en el siguiente ejemplo de código:

asincrónico privado Tarea CheckoutAsync ()


{
. . .
esperar _basketService.ClearBasketAsync (_shippingAddress.Id.ToString (), authToken);
. . .
}

Este método llama al ClearBasketAsync método de la BasketService instancia que se inyecta en el CheckoutViewModel por
Autofac. El método siguiente muestra el ClearBasketAsync
método:

asíncrono pública Tarea ClearBasketAsync ( cuerda guidUser, cuerda simbólico)


{
UriBuilder constructor = nuevo UriBuilder ( Configuración global .Instance.BasketEndpoint);
builder.Path = guidUser;
cuerda uri = builder.ToString ();
esperar _requestProvider.DeleteAsync (uri, token);
}

Este método se basa la URI que identifica el recurso que la solicitud será enviada a, y utiliza el
RequestProvider clase para invocar el método HTTP DELETE en el recurso. Tenga en cuenta que un token de acceso, obtenidos a partir
IdentityServer durante el proceso de autenticación, se requiere para autorizar las solicitudes a la microService cesta. Para obtener más
información sobre la autorización, consulte Autorización .

El ejemplo de código siguiente muestra el DeleteAsync método en el RequestProvider clase:

80 CAPÍTULO 10 | El acceso a datos remotos


asíncrono pública Tarea DeleteAsync ( cuerda uri, cuerda token = "" )
{
HttpClient HttpClient = CreateHttpClient (token);
esperar httpClient.DeleteAsync (URI);
}

Este método llama al CreateHttpClient método, que devuelve una instancia de la HttpClient clase con las cabeceras apropiadas
establecidas. A continuación, envía una solicitud DELETE asíncrona al recurso identificado por el URI. Para obtener más información
acerca de la CreateHttpClient método, ver Hacer una petición GET .

Cuando el DeleteAsync método en el RequestProvider llamadas de clase HttpClient.DeleteAsync, el


Borrar método en el BasketController clase en el Basket.API proyecto se invoca, que se muestra en el ejemplo de código siguiente:

[HttpDelete ( "{carné de identidad}" )]

public void Borrar( cuerda carné de identidad)

{
_repository.DeleteBasketAsync (id);
}

Este método utiliza una instancia de la RedisBasketRepository clase para borrar los datos de la cesta de la caché Redis.

El almacenamiento en caché de datos


El rendimiento de una aplicación puede ser mejorada mediante el almacenamiento en caché de datos de acceso frecuente a un almacenamiento rápido que está situado

cerca de la aplicación. Si el almacenamiento rápido se encuentra más cerca de la aplicación de la fuente original, a continuación, el almacenamiento en caché puede

mejorar significativamente los tiempos de respuesta cuando se recuperan datos.

La forma más común de almacenamiento en caché es de lectura a través de la memoria caché, donde una aplicación recupera datos haciendo referencia a la memoria
caché. Si los datos no está en la caché, se recupera de la memoria de datos y se añade a la caché. Las aplicaciones pueden implementar el almacenamiento en caché
de lectura a través de caché con el patrón de retirada de tierras. Este patrón determina si el artículo está actualmente en la memoria caché. Si el artículo no está en la
caché, se lee desde el almacén de datos y se añade a la caché. Para obtener más información, consulte la Cache-Aparte patrón en Microsoft Docs.

Propina: caché de datos que se leen con frecuencia y que cambia con poca frecuencia

Estos datos se pueden añadir a la memoria caché en la demanda de la primera vez que se recupera mediante una aplicación. Esto significa que la aplicación
necesita para recuperar los datos una sola vez desde el almacén de datos, y que el acceso posterior puede ser satisfecho mediante el uso de la caché.

Las aplicaciones distribuidas, tales como los eShopOnContainers referencia de aplicación, debe proporcionar una o ambas de las siguientes
cachés:

• Una memoria caché compartida, que se puede acceder por múltiples procesos o máquinas.

• Una memoria caché privada, donde se lleva a cabo a nivel local de datos en el dispositivo que ejecuta la aplicación.

La aplicación móvil eShopOnContainers utiliza una caché privada, donde se lleva a cabo a nivel local de datos en el dispositivo que se está ejecutando una
instancia de la aplicación. Para obtener información acerca de la caché utilizada por la aplicación de referencia eShopOnContainers, véase .NET
microservicios: Arquitectura de Aplicaciones .NET en contenedores .

81 CAPÍTULO 10 | El acceso a datos remotos


Propina: Piense en la caché como un almacén de datos transitoria que podría desaparecer en cualquier momento

Asegúrese de que los datos se mantienen en la memoria de datos original, así como la memoria caché. Las posibilidades de perder datos se reducen
al mínimo si la caché no está disponible.

La gestión de los datos de caducidad

Es práctico esperar que los datos almacenados en caché siempre serán consistentes con los datos originales. Los datos en el almacén de datos original puede cambiar

después de que haya sido almacenado en caché, haciendo que los datos en caché para convertirse en rancio. Por lo tanto, las aplicaciones deben poner en práctica

una estrategia que ayuda a asegurar que los datos en la memoria caché es como hasta la fecha como sea posible, pero también se pueden detectar y manejar

situaciones que surgen cuando los datos de la caché se ha convertido rancio. La mayoría de los mecanismos de caché permiten la caché para ser configurado para

caducar los datos, y por lo tanto reducir el período para el que los datos podrían estar fuera de fecha.

Propina: Establecer un tiempo de caducidad por defecto cuando se configura una caché

Muchos cachés implementan de caducidad, lo que invalida los datos y lo elimina de la memoria caché si no se accede por un período determinado. Sin
embargo, se debe tener cuidado al elegir el período de caducidad. Si se hace demasiado corto, los datos expirarán demasiado rápido y se reducirán los
beneficios del almacenamiento en caché. Si se hace demasiado tiempo, los datos corre el riesgo de convertirse en obsoletos. Por lo tanto, el tiempo de
expiración debe coincidir con el patrón de acceso de las aplicaciones que utilizan los datos.

Cuando los datos en caché expira, debe ser retirado de la caché, y la aplicación debe recuperar los datos del almacén de datos original y
colocarlo de nuevo en la memoria caché.

También es posible que una caché podría llenarse si se permite que los datos de permanecer durante un período demasiado largo. Por lo tanto, las solicitudes para añadir

nuevos elementos a la caché podrían ser necesarias para eliminar algunos artículos en un proceso conocido como desalojo. servicios de caché típicamente desalojar a los

datos en una base menos utilizado recientemente. Sin embargo, hay otras políticas de desalojo, incluyendo más recientemente-usado, y primero en entrar, primero en

salir. Para más información, ver

Orientación de almacenamiento en caché en Microsoft Docs.

imágenes de almacenamiento en caché

La aplicación móvil eShopOnContainers consume imágenes de productos remotos que se benefician de ser cacheada. Estas imágenes
se muestran por el Imagen control, y el CachedImage control proporcionado por el FFImageLoading biblioteca.

los Xamarin.Forms Imagen control admite el almacenamiento en caché de las imágenes descargadas. El almacenamiento en caché está habilitado por defecto, y
almacenará la imagen a nivel local durante 24 horas. Además, el tiempo de expiración se puede configurar con la CacheValidity propiedad. Para más información, ver El
almacenamiento en caché imagen descargada
en el Centro de desarrolladores de Xamarin.

FFImageLoading de CachedImage el control es un reemplazo para los Xamarin.Forms Imagen control, proporcionar propiedades adicionales que
permiten la funcionalidad complementaria. Entre esta funcionalidad, el control proporciona el almacenamiento en caché configurable, mientras que
el apoyo de la imagen de carga de error y marcadores de posición. El ejemplo de código siguiente muestra cómo la aplicación móvil utiliza la
eShopOnContainers CachedImage
control en el ProductTemplate, que es la plantilla de datos utilizado por el Vista de la lista control en el
CatalogView:

< ffimageloading: CachedImage


Cuadrícula. fila = "0"
fuente = "{Binding} PictureUri"
aspecto = "AspectFill" >
< ffimageloading: CachedImage.LoadingPlaceholder >
< OnPlatform
x: TypeArguments = "Fuente de imagen"

82 CAPÍTULO 10 | El acceso a datos remotos


iOS = "Default_product"
Android = "Default_product"
WinPhone = "Activos / default_product.png" />
</ ffimageloading: CachedImage.LoadingPlaceholder >
< ffimageloading: CachedImage.ErrorPlaceholder >
< OnPlatform
x: TypeArguments = "Fuente de imagen"
iOS = "Sin imágen"
Android = "Sin imágen"
WinPhone = "Activos / noimage.png" />
</ ffimageloading: CachedImage.ErrorPlaceholder >
</ ffimageloading: CachedImage >

los CachedImage control ajusta el LoadingPlaceholder y ErrorPlaceholder propiedades a las imágenes específicas de la plataforma. los LoadingPlaceholder
propiedad especifica la imagen que se mostrará mientras la imagen especificada por el Fuente la propiedad se recupera, y la ErrorPlaceholder
propiedad especifica la imagen que se mostrará si se produce un error al intentar recuperar la imagen especificada por el Fuente propiedad.

Como su nombre lo indica, el CachedImage el control almacena imágenes remotas en el dispositivo durante el tiempo especificado por el valor de la CacheDuration
propiedad. Cuando no se establece explícitamente este valor de la propiedad, se aplica el valor predeterminado de 30 días.

El aumento de la capacidad de recuperación


Todas las aplicaciones que se comunican con los servicios y recursos remotos deben ser sensibles a los fallos transitorios. los fallos transitorios incluyen
la pérdida momentánea de la conectividad de red a los servicios, la indisponibilidad temporal de un servicio, o los tiempos de espera que surgen cuando
un servicio está ocupado. Estos fallos son a menudo selfcorrecting, y si la acción se repite después de un retardo adecuado Es probable que tenga éxito.

los fallos transitorios pueden tener un gran impacto en la calidad percibida de una aplicación, incluso si se ha probado exhaustivamente bajo todas las
circunstancias previsibles. Para garantizar que una aplicación que se comunica con los servicios a distancia funciona de forma fiable, debe ser capaz de
hacer todo lo siguiente:

• Detectar fallos cuando se producen, y determinar si los defectos son propensos a ser transitoria.

• Vuelva a intentar la operación si se determina que el fallo es probable que sea transitoria y realizar un seguimiento del número de veces que se
vuelve a intentar la operación.

• Utilizar una estrategia de reintento apropiada, que especifica el número de reintentos, el retardo entre cada intento, y las
acciones a tomar después de un intento fallido.

Este manejo de errores transitorios puede lograrse envolviendo todos los intentos de acceso a un servicio remoto en el código que implementa el
patrón de reintento.

reintentar patrón

Si una aplicación detecta un fallo cuando se trata de enviar una petición a un servicio remoto, que puede manejar el fracaso en cualquiera de las siguientes
maneras:

• Volver a intentar la operación. La aplicación podría volver a intentar la petición no inmediatamente.

• Volviendo a intentar la operación después de un retraso. La aplicación debe esperar a que una cantidad adecuada de tiempo antes de volver a intentar la

solicitud.

• Cancelación de la operación. La aplicación debe cancelar la operación y reportar una excepción.

83 CAPÍTULO 10 | El acceso a datos remotos


La estrategia de reintento debe ajustarse para satisfacer las necesidades de negocio de la aplicación. Por ejemplo, es importante para optimizar el
número de reintentos y el intervalo de reintento de la operación que se intentó. Si la operación es parte de una interacción con el usuario, el intervalo de
reintento debe ser corta y sólo unos pocos reintentos intentó evitar que los usuarios esperan una respuesta. Si la operación es parte de un flujo de
trabajo de larga duración, donde la cancelación o reiniciar el flujo de trabajo es caro o consume mucho tiempo, es conveniente esperar más tiempo
entre intentos y para volver a intentarlo más veces.

Nota: Una estrategia de reintento agresivo con la mínima demora entre los intentos, y un gran número de reintentos, podría degradar un servicio
remoto que se está ejecutando cerca o en capacidad. Además, esta estrategia de reintento también podría afectar a la capacidad de respuesta de
la aplicación si está continuamente tratando de realizar una operación de fallar.

Si una solicitud sigue fallando después de un número de reintentos, que es mejor para la aplicación evite que pide además que van al mismo recurso
y para informar de un fallo. Entonces, después de un período determinado, la aplicación puede hacer una o más peticiones al recurso para ver si
tienen éxito. Para más información, ver patrón de disyuntor .

Propina: Nunca poner en práctica un mecanismo de reintento sin fin

Utilice un número finito de reintentos, o implementar la Cortacircuitos patrón para permitir un servicio para recuperarse.

La aplicación móvil eShopOnContainers actualmente no implementar el patrón de reintento al hacer peticiones web RESTful. sin embargo, el CachedImage
control, proporcionada por el FFImageLoading biblioteca es compatible con el manejo de errores transitorios de la carga de imágenes de volver a
intentarlo. Si falla la carga de imágenes, se harán nuevos intentos. El número de intentos es especificado por el RetryCount de propiedad, y reintentos
ocurrirán después de un retardo especificado por el RetryDelay propiedad. Si estos valores de propiedad no se establecen explícitamente, sus valores por
omisión se aplican - 3 para el RetryCount de propiedad y 250 ms para el RetryDelay propiedad. Para obtener más información acerca de la CachedImage control,
consulte imágenes de almacenamiento en caché .

La aplicación de referencia eShopOnContainers hace implementar el patrón de reintento. Para obtener más información, incluyendo una
discusión de cómo combinar el patrón con el reintento HttpClient clase, consulte .NET microservicios: Arquitectura de Aplicaciones .NET en
contenedores .

Para obtener más información sobre el patrón de reintento, consulte la Rever patrón en Microsoft Docs.

patrón de disyuntor
En algunas situaciones, los fallos pueden ocurrir debido a eventos anticipados que requieren más tiempo para arreglar. Estos fallos pueden variar desde una
pérdida parcial de la conectividad con el fallo completo de un servicio. En estas situaciones, no tiene sentido para una aplicación para volver a intentar una
operación que es improbable que tenga éxito, y en su lugar debe aceptar que la operación ha fallado y manejar este fracaso en consecuencia.

El patrón disyuntor puede evitar que una aplicación de intentar varias veces para ejecutar una operación que es probable que falle, al
tiempo que permite la aplicación para detectar si el fallo ha sido resuelto.

Nota: El propósito del patrón de disyuntor de circuito es diferente del patrón de reintento. El patrón de reintento permite a una aplicación para volver a
intentar una operación en la expectativa de que va a tener éxito. El patrón disyuntor impide una aplicación de la realización de una operación que es
probable que falle.

Un interruptor de circuito actúa como un proxy para las operaciones que pudieran fallar. El proxy debe controlar el número de fallos que han
ocurrido recientemente, y utilizar esta información para decidir si se debe permitir el funcionamiento de proceder, o para volver de inmediato una
excepción.

La aplicación móvil eShopOnContainers actualmente no implementar el patrón disyuntor. Sin embargo, los
eShopOnContainers hace. Para más información, ver .NET microservicios: Arquitectura de Aplicaciones .NET en contenedores .

84 CAPÍTULO 10 | El acceso a datos remotos


Propina: Combinar los patrones de interruptores de circuito y de reintento

Una aplicación puede combinar los patrones de reintento y disyuntor de circuito mediante el uso del patrón de reintento para invocar una
operación a través de un interruptor de circuito. Sin embargo, la lógica de reintento debe ser sensible a las excepciones devueltas por el
disyuntor y abandonar los reintentos si el disyuntor indica que un fallo no es transitoria.

Para obtener más información sobre el patrón de disyuntor, consulte la Cortacircuitos patrón en Microsoft Docs.

Resumen
Muchas de las soluciones basadas en la web modernos hacen uso de los servicios web, alojados en servidores web, para proporcionar la
funcionalidad de las aplicaciones de cliente remoto. Las operaciones que expone un servicio web constituyen una API web, y aplicaciones de cliente
deben ser capaces de utilizar la API de Web sin saber que las revelaciones de la API se implementan los datos u operaciones.

El rendimiento de una aplicación puede ser mejorada mediante el almacenamiento en caché de datos de acceso frecuente a un almacenamiento rápido que está

situado cerca de la aplicación. Las aplicaciones pueden implementar el almacenamiento en caché de lectura a través de caché con el patrón de retirada de

tierras. Este patrón determina si el artículo está actualmente en la memoria caché. Si el artículo no está en la caché, se lee desde el almacén de datos y se

añade a la caché.

Cuando se comunica con las API web, las aplicaciones deben ser sensibles a los fallos transitorios. los fallos transitorios incluyen la pérdida momentánea de la
conectividad de red a los servicios, la indisponibilidad temporal de un servicio, o los tiempos de espera que surgen cuando un servicio está ocupado. Estos fallos
son a menudo auto-corrección, y si la acción se repite después de un retardo adecuado, entonces es probable que tenga éxito. Por lo tanto, las aplicaciones
deben envolver todos los intentos de acceso a una API Web de código que implementa un mecanismo de manejo de errores transitorios.

85 CAPÍTULO 10 | El acceso a datos remotos


CAPÍTULO 11

Examen de la unidad
Las aplicaciones móviles tienen problemas únicos que escritorio y aplicaciones basadas en web no tienen que preocuparse. Los usuarios móviles
serán diferentes por los dispositivos que utilizan, por la conectividad de red, por la disponibilidad de los servicios, y una serie de otros factores. Por lo
tanto, las aplicaciones móviles deben ser probados, ya que serán utilizados en el mundo real con el fin de mejorar su calidad, fiabilidad y rendimiento.
Hay muchos tipos de pruebas que se deben realizar en una aplicación, incluyendo la unidad de pruebas, las pruebas de integración, y pruebas de la
interfaz de usuario, con la unidad de pruebas es la forma más común de pruebas.

Una prueba de unidad toma una pequeña unidad de la aplicación, típicamente un método, lo aísla del resto del código, y verifica que se comporta como se
esperaba. Su objetivo es comprobar que cada unidad de funcionalidad lleva a cabo como se esperaba, por lo que los errores no se propagan a lo largo de la
aplicación. La detección de un error en el que se produce es más eficiente que la observación del efecto de un error indirectamente en un punto de fallo
secundario.

Prueba de la unidad tiene el mayor efecto sobre la calidad del código cuando es una parte integral del flujo de trabajo de desarrollo de software. Tan pronto
como un método ha sido escrita, las pruebas unitarias deben ser escritos que verifican el comportamiento del método en respuesta a las normas, límites y
casos incorrectas de los datos de entrada, y que marque ninguna hipótesis explícitas o implícitas hechas por el código. Por otra parte, con el desarrollo
basado en pruebas, las pruebas unitarias se escriben antes del código. En este escenario, las pruebas unitarias tanto actúan como documentación de
diseño y las especificaciones funcionales.

Nota: Las pruebas unitarias son muy eficaces contra la regresión - es decir, la funcionalidad que se utiliza para trabajar, pero ha sido perturbado por una
actualización defectuosa.

Las pruebas unitarias suelen utilizar el patrón de organizar-acto-aserción:

• los organizar sección del método de prueba de la unidad inicializa objetos y establece el valor de los datos que se pasa al
método bajo prueba.

• los acto sección invoca el método bajo prueba con los argumentos necesarios.

• los afirmar sección verifica que la acción del método bajo prueba se comporta como se esperaba.

Siguiendo este patrón asegura que las pruebas unitarias se pueden leer y consistente.

La inyección de dependencia y la unidad de pruebas


Una de las motivaciones para adoptar una arquitectura de acoplamiento flexible es que facilita la unidad de pruebas. Uno de los tipos
registrados con Autofac es el OrderService clase. El ejemplo de código siguiente muestra un esquema de esta clase:

clase pública OrderDetailViewModel : ViewModelBase


{
privado IOrderService _ordersService;

público OrderDetailViewModel ( IOrderService ordersService)

86 CAPÍTULO 11 | Examen de la unidad


{
_ordersService = ordersService;
}
. . .
}

los OrderDetailViewModel clase tiene una dependencia en el IOrderService escriba la que el recipiente se resuelve cuando se crea una
instancia OrderDetailViewModel objeto. Sin embargo, en lugar de crear una
OrderService objeto a unidad de prueba del OrderDetailViewModel clase, en cambio, sustituir el
OrderService objeto con una maqueta con el propósito de las pruebas. Figura 10-1 ilustra esta relación.

Figura 10-1: Las clases que implementan la interfaz IOrderService

Este enfoque permite que el OrderService objeto que se pasa a la OrderDetailViewModel clase en tiempo de ejecución, y en aras de la
capacidad de prueba, permite que el OrderMockService clase que se pasa en el
OrderDetailViewModel clase en tiempo de prueba. La principal ventaja de este enfoque es que permite a las pruebas de unidad para ser ejecutado sin

necesidad de recursos difíciles de manejar, tales como servicios web o bases de datos.

Prueba de aplicaciones MVVM


Prueba de modelos y modelos de vista de las aplicaciones MVVM es idéntica a someter a prueba las otras clases, y las mismas herramientas y técnicas -
tales como la unidad de pruebas y de burla, se puede utilizar. Sin embargo, hay algunos patrones que son típicos para modelar y clases de vista del
modelo, que pueden beneficiarse de las técnicas específicas de pruebas unitarias.

Propina: Prueba una cosa con cada unidad de prueba

No tener la tentación de hacer un ejercicio de prueba de unidad más de un aspecto del comportamiento de la unidad. Si lo hace, conduce a pruebas que
son difíciles de leer y actualizar. También puede conducir a la confusión en la interpretación de un fracaso.

Los eShopOnContainers usos de aplicaciones móviles xUnit para llevar a cabo pruebas de unidad, que soporta dos tipos diferentes de pruebas de unidad:

• Los hechos son pruebas que son siempre verdaderas, que ponen a prueba las condiciones invariables.

• Las teorías son pruebas de que sólo son verdaderas para un determinado conjunto de datos.

87 CAPÍTULO 11 | Examen de la unidad


Las pruebas de unidad incluidos con los eShopOnContainers app móvil son pruebas de datos, y así cada método de prueba de la unidad está
decorado con la [ Hecho] atributo.

Nota: pruebas xUnit son ejecutados por un corredor de prueba. Para ejecutar el corredor de prueba, ejecutar el proyecto
eShopOnContainers.TestRunner para la plataforma necesaria.

funcionalidad asincrónica de pruebas


Al implementar el patrón MVVM, modelos de vista generalmente invocan operaciones en servicios, a menudo de forma asíncrona. Las
pruebas de código que invoca estas operaciones suelen utilizar burla como sustitutos de los servicios reales. El ejemplo de código
siguiente demuestra probar la funcionalidad asíncrono pasando un servicio simulado en un modelo de vista:

[ Hecho ]
asíncrono pública Tarea OrderPropertyIsNotNullAfterViewModelInitializationTest ()
{
var OrderService = nuevo OrderMockService ();
var orderViewModel = nuevo OrderDetailViewModel (OrderService);

var para = esperar orderService.GetOrderAsync ( 1 , Configuración global .Instance.AuthToken);


esperar orderViewModel.InitializeAsync (orden);

Afirmar .NotNull (orderViewModel.Order);


}

Esta prueba de la unidad comprueba que la Orden propiedad de la OrderDetailViewModel instancia tendrá un valor después de la InitializeAsync método
ha sido invocado. los InitializeAsync método se invoca cuando vista correspondiente del modelo de vista se navega a. Para obtener más
información acerca de la navegación, véase Navegación .

Cuando el OrderDetailViewModel Se crea ejemplo, se espera una OrderService instancia a ser especificado como un argumento. sin
embargo, el OrderService recupera los datos de un servicio Web. Por lo tanto, una OrderMockService ejemplo, que es una versión de la
maqueta OrderService clase, se especifica como el argumento de la OrderDetailViewModel constructor. Entonces, cuando el modelo de
vista
InitializeAsync método se invoca, que invoca IOrderService las operaciones, los datos simulacro se recuperan en lugar de
comunicarse con un servicio web.

implementaciones de pruebas INotifyPropertyChanged


la aplicación de la INotifyPropertyChanged interfaz permite vistas a reaccionar a los cambios que se originan a partir de modelos de vista y modelos.
Estos cambios no se limitan a datos que se muestran en los controles - que también se usan para controlar la vista, tales como estados de vista del
modelo que causan animaciones para iniciar o controles para ser desactivados.

Propiedades que pueden ser actualizados directamente por la unidad de prueba pueden ser probados por la fijación de un controlador de eventos al PropertyChanged
evento y comprobar si el evento se produce después de establecer un nuevo valor para la propiedad. El ejemplo de código siguiente muestra tal prueba:

[ Hecho ]
asíncrono pública Tarea SettingOrderPropertyShouldRaisePropertyChanged ()
{
bool invocado = falso ;
var OrderService = nuevo OrderMockService ();
var orderViewModel = nuevo OrderDetailViewModel (OrderService);

orderViewModel.PropertyChanged + = (remitente, e) =>


{

88 CAPÍTULO 11 | Examen de la unidad


Si (e.PropertyName.Equals ( "Orden" ))
invocado = cierto ;
};
var para = esperar orderService.GetOrderAsync ( 1 , Configuración global .Instance.AuthToken);
esperar orderViewModel.InitializeAsync (orden);

Afirmar .TRUE (invocado);


}

Esta prueba invoca la unidad InitializeAsync método de la OrderViewModel clase, lo que hace que su
Orden propiedad que se actualiza. La prueba de la unidad va a pasar, siempre que el PropertyChanged evento se produce para el Orden propiedad.

la comunicación basada en mensajes de pruebas

Ver los modelos que utilizan el MessagingCenter clase para comunicarse entre las clases débilmente acoplados se puede unidad probada mediante la
suscripción de mensaje que está siendo enviado por el código bajo prueba, como se demuestra en el ejemplo de código siguiente:

[ Hecho ]
public void AddCatalogItemCommandSendsAddProductMessageTest ()
{
bool messageReceived = falso ;
var CatalogService = nuevo CatalogMockService ();
var catalogViewModel = nuevo CatalogViewModel (CatalogService);

Xamarin.Forms. MessagingCenter .Subscribe < CatalogViewModel , CatalogItem > (


esta , MessageKeys .AddProduct, (remitente, arg) =>
{
messageReceived = cierto ;
});
catalogViewModel.AddCatalogItemCommand.Execute ( nulo );

Afirmar .TRUE (messageReceived);


}

Esta prueba de la unidad comprueba que la CatalogViewModel publica la Agregar producto mensaje en respuesta a su AddCatalogItemCommand ser ejecutado.
Porque el MessagingCenter clase admite suscripciones de mensajes de multidifusión, la prueba unitaria puede suscribirse a la Agregar producto mensaje y
ejecutar un delegado de devolución de llamada en respuesta a la recepción de la misma. Este delegado de devolución de llamada, especificado como una
expresión lambda, establece una
booleano campo que se utiliza por el Afirmar declaración para verificar el comportamiento de la prueba.

Pruebas de manejo de excepciones

Las pruebas unitarias también pueden ser escritos que comprobar que las excepciones específicas son arrojados por las acciones o entradas no válidas, como

se demuestra en el siguiente ejemplo de código:

[ Hecho ]
public void InvalidEventNameShouldThrowArgumentExceptionText ()
{
var comportamiento = nuevo MockEventToCommandBehavior
{
eventName = "OnItemTapped"
};
var Listview = nuevo Vista de la lista ();

Afirmar .Throws < ArgumentException > (() => ListView.Behaviors.Add (comportamiento));


}

89 CAPÍTULO 11 | Examen de la unidad


Esta prueba de la unidad emitirá una excepción, ya que el Vista de la lista de control no tiene un evento denominado
OnItemTapped. los Assert.Throws <T> método es un método genérico donde T es el tipo de la excepción esperada. El argumento
que se pasa a la Assert.Throws <T> método es una expresión lambda que emitir la excepción. Por lo tanto, la prueba de la unidad
pasará a condición de que la expresión lambda lanza una ArgumentException.

Propina: Evite escribir pruebas unitarias que examinan cadenas de mensajes de excepción

cadenas de mensajes de excepción podría cambiar con el tiempo, por lo que las pruebas unitarias que dependen de su presencia se considera como frágil.

la validación de pruebas

Hay dos aspectos a probar la aplicación de validación: la prueba de que las reglas de validación se aplican correctamente, y
prueba de que el ValidatableObject <T> clase lleva a cabo como se esperaba.

lógica de validación es generalmente simple para probar, porque es típicamente un proceso autónomo donde la salida depende de la entrada.
Debe haber pruebas sobre los resultados de la invocación de la Validar método en cada propiedad que tiene al menos una regla de validación
asociada, como se demuestra en el ejemplo de código siguiente:

[ Hecho ]
public void CheckValidationPassesWhenBothPropertiesHaveDataTest ()
{
var mockViewModel = nuevo MockViewModel ();
mockViewModel.Forename.Value = "John" ;
mockViewModel.Surname.Value = "Herrero" ;

bool isValid = mockViewModel.Validate ();

Afirmar .TRUE (isValid);


}

Esta prueba de la unidad comprueba que la validación tiene éxito cuando los dos ValidatableObject <T> propiedades en el
MockViewModel ejemplo ambos tienen datos.

Además de comprobar que la validación tiene éxito, pruebas de unidad de validación también deben comprobar los valores de la
Valor, Es válida, y errores propiedad de cada ValidatableObject <T> ejemplo, para verificar que la clase funcione como se espera. El
siguiente ejemplo de código se muestra una prueba de unidad que hace esto:

[ Hecho ]
public void CheckValidationFailsWhenOnlyForenameHasDataTest ()
{
var mockViewModel = nuevo MockViewModel ();
mockViewModel.Forename.Value = "John" ;

bool isValid = mockViewModel.Validate ();

Afirmar .FALSE (isValid);


Afirmar .NotNull (mockViewModel.Forename.Value);
Afirmar .NULL (mockViewModel.Surname.Value);
Afirmar .TRUE (mockViewModel.Forename.IsValid);
Afirmar .FALSE (mockViewModel.Surname.IsValid);
Afirmar .Empty (mockViewModel.Forename.Errors);
Afirmar .NotEmpty (mockViewModel.Surname.Errors);
}

90 CAPÍTULO 11 | Examen de la unidad


Esta unidad de prueba comprueba que la validación falla cuando el Apellido propiedad de la MockViewModel no tiene ningún dato, y el Valor,
IsValid, Errores y propiedad de cada ValidatableObject <T> instancia se establecen correctamente.

Resumen
Una prueba de unidad toma una pequeña unidad de la aplicación, típicamente un método, lo aísla del resto del código, y verifica que se comporta como se
esperaba. Su objetivo es comprobar que cada unidad de funcionalidad lleva a cabo como se esperaba, por lo que los errores no se propagan a lo largo de
la aplicación.

El comportamiento de un objeto bajo prueba se puede aislar mediante la sustitución de objetos dependientes con objetos simulados que simulan el
comportamiento de los objetos dependientes. Esto permite que las pruebas unitarias para ser ejecutado sin necesidad de recursos difíciles de manejar, tales
como servicios web o bases de datos.

Prueba de modelos y modelos de vista de las aplicaciones MVVM es idéntica a someter a prueba las otras clases, y las mismas herramientas y
técnicas se pueden utilizar.

91 CAPÍTULO 11 | Examen de la unidad


92 CAPÍTULO 11 | Examen de la unidad

También podría gustarte