Uso del cliente administrado para Azure Mobile AppsHow to use the managed client for Azure Mobile Apps

Nota

Visual Studio App Center admite servicios integrados de un extremo a otro fundamentales para el desarrollo de aplicaciones móviles.Visual Studio App Center supports end to end and integrated services central to mobile app development. Los desarrolladores pueden usar los servicios de compilación, prueba y distribución para configurar la canalización de integración y entrega continuas.Developers can use Build, Test and Distribute services to set up Continuous Integration and Delivery pipeline. Una vez que se ha implementado la aplicación, los desarrolladores pueden supervisar el estado y el uso de su aplicación con los servicios de análisis y diagnóstico, e interactuar con los usuarios que utilizan el servicio de Push (inserción).Once the app is deployed, developers can monitor the status and usage of their app using the Analytics and Diagnostics services, and engage with users using the Push service. Además, los desarrolladores pueden aprovechar Auth para autenticar a los usuarios y el servicio de datos para almacenar y sincronizar los datos de la aplicación en la nube.Developers can also leverage Auth to authenticate their users and Data service to persist and sync app data in the cloud.

Si está pensando en integrar servicios en la nube en su aplicación para dispositivos móviles, regístrese en App Center hoy mismo.If you are looking to integrate cloud services in your mobile application, sign up with App Center today.

Información generalOverview

En esta guía se muestra cómo realizar escenarios comunes con la biblioteca de cliente administrada para Azure App Service Mobile Apps para Windows y Xamarin.This guide shows you how to perform common scenarios using the managed client library for Azure App Service Mobile Apps for Windows and Xamarin apps. Si no tiene experiencia en el uso de Mobile Apps, considere primero la opción de completar el tutorial Guía de inicio rápido de Azure Mobile Apps .If you are new to Mobile Apps, you should consider first completing the Azure Mobile Apps quickstart tutorial. En esta guía, nos centramos en el SDK administrado de cliente.In this guide, we focus on the client-side managed SDK. Para más información sobre los SDK para Mobile Apps del lado servidor, consulte la documentación de SDK de servidor .NET o de SDK de servidor Node.js.To learn more about the server-side SDKs for Mobile Apps, see the documentation for the .NET Server SDK or the Node.js Server SDK.

Documentación de referenciaReference documentation

La documentación de referencia para el SDK de cliente se encuentra aquí: Referencia de cliente de .NET de Azure Mobile Apps.The reference documentation for the client SDK is located here: Azure Mobile Apps .NET client reference. También encontrará varios ejemplos de cliente en el repositorio de GitHub Azure-Samples.You can also find several client samples in the Azure-Samples GitHub repository.

Plataformas compatiblesSupported Platforms

La plataforma de .NET es compatible con las siguientes plataformas:The .NET Platform supports the following platforms:

  • Versiones de Xamarin Android para niveles de API 19-24 (de KitKat a Nougat)Xamarin Android releases for API 19 through 24 (KitKat through Nougat)
  • Versiones de Xamarin iOS para iOS 8.0 y posteriorXamarin iOS releases for iOS versions 8.0 and later
  • Plataforma universal de WindowsUniversal Windows Platform
  • Windows Phone 8,1Windows Phone 8.1
  • Windows Phone 8.0, salvo para aplicaciones de SilverlightWindows Phone 8.0 except for Silverlight applications

La autenticación de flujo de servidor utiliza una vista web para la interfaz de usuario presentada.The "server-flow" authentication uses a WebView for the presented UI. Si el dispositivo no puede presentar una interfaz de usuario de vista web, hay que utilizar otros métodos de autenticación.If the device is not able to present a WebView UI, then other methods of authentication are needed. Por tanto, este SDK no es adecuado para dispositivos de tipo reloj o con restricciones similares.This SDK is thus not suitable for Watch-type or similarly restricted devices.

Configuración y requisitos previosSetup and Prerequisites

Se supone que ya ha creado y publicado el proyecto de back-end de la aplicación móvil, que incluye al menos una tabla.We assume that you have already created and published your Mobile App backend project, which includes at least one table. En el código usado en este tema, el nombre de la tabla es TodoItem y dispondrá de las siguientes columnas: Id, Text y Complete.In the code used in this topic, the table is named TodoItem and it has the following columns: Id, Text, and Complete. Esta tabla es la misma que se crea cuando se completa la guía de inicio rápido de Mobile Apps de Azure.This table is the same table created when you complete the Azure Mobile Apps quickstart.

El tipo del cliente con tipo correspondiente en C# es la siguiente clase:The corresponding typed client-side type in C# is the following class:

public class TodoItem
{
    public string Id { get; set; }

    [JsonProperty(PropertyName = "text")]
    public string Text { get; set; }

    [JsonProperty(PropertyName = "complete")]
    public bool Complete { get; set; }
}

Tenga en cuenta que JsonPropertyAttribute se usa para definir la asignación PropertyName entre el campo de cliente y de tabla.The JsonPropertyAttribute is used to define the PropertyName mapping between the client field and the table field.

Para aprender a crear tablas en el back-end de Mobile Apps, consulte el tema del SDK de servidor .NET o el tema del SDK de servidor Node.js.To learn how to create tables in your Mobile Apps backend, see the .NET Server SDK topic or the Node.js Server SDK topic. Si creó el back-end de aplicación móvil en Azure Portal mediante la guía de inicio rápido, también puede usar la opción Tablas fáciles en Azure Portal.If you created your Mobile App backend in the Azure portal using the QuickStart, you can also use the Easy tables setting in the Azure portal.

Procedimientos para: Instalación del paquete del SDK de cliente administradoHow to: Install the managed client SDK package

Utilice uno de los métodos siguientes para instalar el paquete del SDK de cliente administrado para Mobile Apps desde NuGet:Use one of the following methods to install the managed client SDK package for Mobile Apps from NuGet:

  • Visual Studio Haga clic con el botón derecho en el proyecto, haga clic en Administrar paquetes NuGet, busque el paquete Microsoft.Azure.Mobile.Client y haga clic en Instalar.Visual Studio Right-click your project, click Manage NuGet Packages, search for the Microsoft.Azure.Mobile.Client package, then click Install.
  • Xamarin Studio Haga clic con el botón derecho en el proyecto, haga clic en Add (Agregar) >Add NuGet Packages (Agregar paquetes NuGet), busque el paquete Microsoft.Azure.Mobile.Client y haga clic en Add Package (Agregar paquete).Xamarin Studio Right-click your project, click Add > Add NuGet Packages, search for the Microsoft.Azure.Mobile.Client package, and then click Add Package.

En el archivo de la actividad principal, no olvide agregar la siguiente instrucción using :In your main activity file, remember to add the following using statement:

using Microsoft.WindowsAzure.MobileServices;

Nota

Tenga en cuenta que todos los paquetes de soporte a los que se hace referencia en el proyecto Android deben tener la misma versión.Please note that all the support packages referenced in your Android project must have the same version. El SDK tiene la dependencia Xamarin.Android.Support.CustomTabs para la plataforma Android, por lo que si el proyecto usa paquetes de soporte más recientes, tiene que instalar este paquete con la versión necesaria directamente para evitar conflictos.The SDK has Xamarin.Android.Support.CustomTabs dependency for Android platform, so if your project uses newer support packages you need to install this package with required version directly to avoid conflicts.

Instrucciones: Trabajo con símbolos de depuración en Visual StudioHow to: Work with debug symbols in Visual Studio

Los símbolos del espacio de nombres Microsoft.Azure.Mobile están disponibles en SymbolSource.The symbols for the Microsoft.Azure.Mobile namespace are available on SymbolSource. Consulte las instrucciones de SymbolSource para integrar SymbolSource con Visual Studio.Refer to the SymbolSource instructions to integrate SymbolSource with Visual Studio.

Creación del cliente de Mobile AppsCreate the Mobile Apps client

El código siguiente crea el objeto MobileServiceClient que se usa para obtener acceso al back-end de la Aplicación móvil.The following code creates the MobileServiceClient object that is used to access your Mobile App backend.

var client = new MobileServiceClient("MOBILE_APP_URL");

En el código anterior, reemplace MOBILE_APP_URL por la dirección URL del back-end de aplicación móvil, que se encuentra en la hoja de la aplicación móvil en Azure Portal.In the preceding code, replace MOBILE_APP_URL with the URL of the Mobile App backend, which is found in the blade for your Mobile App backend in the Azure portal. El objeto MobileServiceClient debe ser un singleton.The MobileServiceClient object should be a singleton.

Trabajo con tablasWork with Tables

La siguiente sección describe cómo buscar y recuperar registros y modificar los datos de la tabla.The following section details how to search and retrieve records and modify the data within the table. Se tratan los siguientes temas:The following topics are covered:

Instrucciones: Creación de una referencia de tablaHow to: Create a table reference

Todo el código que obtiene acceso a datos o los modifica en una tabla de back-end llama a las funciones del objeto MobileServiceTable .All the code that accesses or modifies data in a backend table calls functions on the MobileServiceTable object. Obtenga una referencia a la tabla llamando al método GetTable del modo indicado a continuación:Obtain a reference to the table by calling the GetTable method, as follows:

IMobileServiceTable<TodoItem> todoTable = client.GetTable<TodoItem>();

El objeto devuelto usa el modelo de serialización con tipo.The returned object uses the typed serialization model. También se admite un modelo de serialización sin tipo.An untyped serialization model is also supported. El siguiente ejemplo crea una referencia a una tabla sin tipo:The following example creates a reference to an untyped table:

// Get an untyped table reference
IMobileServiceTable untypedTodoTable = client.GetTable("TodoItem");

En las consultas sin tipo, debe especificar la cadena de consulta de OData subyacente.In untyped queries, you must specify the underlying OData query string.

Instrucciones: Consulta de datos desde la aplicación móvilHow to: Query data from your Mobile App

En esta sección se describe cómo generar consultas al back-end de la aplicación móvil, lo cual incluye la siguiente funcionalidad:This section describes how to issue queries to the Mobile App backend, which includes the following functionality:

Nota

Se aplica el tamaño de página del servidor para evitar que se devuelvan todas las filas.A server-driven page size is enforced to prevent all rows from being returned. La paginación evita que las solicitudes predeterminadas de los conjuntos de datos de gran tamaño incidan negativamente en el servicio.Paging keeps default requests for large data sets from negatively impacting the service. Para devolver más de 50 filas, use los métodos Skip y Take, como se describe en Devolución de datos en páginas.To return more than 50 rows, use the Skip and Take method, as described in Return data in pages.

Instrucciones: Filtro de datos devueltosHow to: Filter returned data

El siguiente código muestra cómo filtrar los datos incluyendo una cláusula Where en una consulta.The following code illustrates how to filter data by including a Where clause in a query. Devuelve todos los elementos de todoTable cuya propiedad Complete es igual a false.It returns all items from todoTable whose Complete property is equal to false. La función Where aplica un predicado de filtrado de filas a la consulta en relación con la tabla.The Where function applies a row filtering predicate to the query against the table.

// This query filters out completed TodoItems and items without a timestamp.
List<TodoItem> items = await todoTable
    .Where(todoItem => todoItem.Complete == false)
    .ToListAsync();

Puede ver el identificador URI de la solicitud que se ha enviado al back-end mediante el software de inspección de mensajes, como las herramientas para desarrolladores del explorador o Fiddler.You can view the URI of the request sent to the backend by using message inspection software, such as browser developer tools or Fiddler. Si consulta el URI de solicitud, tenga en cuenta que se ha modificado la cadena de consulta:If you look at the request URI, notice that the query string is modified:

GET /tables/todoitem?$filter=(complete+eq+false) HTTP/1.1

El SDK de servidor traduce esta solicitud de OData a una consulta SQL:This OData request is translated into an SQL query by the Server SDK:

SELECT *
    FROM TodoItem
    WHERE ISNULL(complete, 0) = 0

La función que se pasa al método Where puede disponer de un número arbitrario de condiciones.The function that is passed to the Where method can have an arbitrary number of conditions.

// This query filters out completed TodoItems where Text isn't null
List<TodoItem> items = await todoTable
    .Where(todoItem => todoItem.Complete == false && todoItem.Text != null)
    .ToListAsync();

El SDK de servidor traduciría este ejemplo a una consulta SQL:This example would be translated into an SQL query by the Server SDK:

SELECT *
    FROM TodoItem
    WHERE ISNULL(complete, 0) = 0
          AND ISNULL(text, 0) = 0

Esta consulta también se puede dividir en varias cláusulas:This query can also be split into multiple clauses:

List<TodoItem> items = await todoTable
    .Where(todoItem => todoItem.Complete == false)
    .Where(todoItem => todoItem.Text != null)
    .ToListAsync();

Los dos métodos son equivalentes y pueden usarse indistintamente.The two methods are equivalent and may be used interchangeably. La opción anterior—de concatenación de varios predicados en una consulta—es más compacta y es la que se recomienda.The former option—of concatenating multiple predicates in one query—is more compact and recommended.

La cláusula Where admite las operaciones que pueden traducirse en el subconjunto OData.The Where clause supports operations that be translated into the OData subset. Estas son algunas de las operaciones:Operations include:

  • Operadores relacionales (==, !=, <, <=, >, >=)Relational operators (==, !=, <, <=, >, >=),
  • Operadores aritméticos (+, -, /, *, %)Arithmetic operators (+, -, /, *, %),
  • Precisión numérica (Math.Floor, Math.Ceiling)Number precision (Math.Floor, Math.Ceiling),
  • Funciones de cadena (Length, Substring, Replace, IndexOf, StartsWith, EndsWith)String functions (Length, Substring, Replace, IndexOf, StartsWith, EndsWith),
  • Propiedades de fecha (Year, Month, Day, Hour, Minute, Second)Date properties (Year, Month, Day, Hour, Minute, Second),
  • Propiedades de acceso de un objetoAccess properties of an object, and
  • Expresiones combinando cualquiera de estas operacionesExpressions combining any of these operations.

Al tener en cuenta lo que admite el SDK de servidor, puede consultar la documentación de OData v3.When considering what the Server SDK supports, you can consider the OData v3 Documentation.

Instrucciones: Ordenar datos devueltosHow to: Sort returned data

El siguiente código muestra cómo ordenar datos incluyendo una función OrderBy u OrderByDescending en la consulta.The following code illustrates how to sort data by including an OrderBy or OrderByDescending function in the query. Devuelve los elementos de todoTable ordenados de manera ascendente por el campo Text.It returns items from todoTable sorted ascending by the Text field.

// Sort items in ascending order by Text field
MobileServiceTableQuery<TodoItem> query = todoTable
                .OrderBy(todoItem => todoItem.Text)
List<TodoItem> items = await query.ToListAsync();

// Sort items in descending order by Text field
MobileServiceTableQuery<TodoItem> query = todoTable
                .OrderByDescending(todoItem => todoItem.Text)
List<TodoItem> items = await query.ToListAsync();

Instrucciones: Devolver datos en páginasHow to: Return data in pages

De manera predeterminada, el back-end devuelve solo las primeras 50 filas.By default, the backend returns only the first 50 rows. Aumente el número de filas devueltas llamando al método Take .You can increase the number of returned rows by calling the Take method. Use Take junto con el método Skip para solicitar una "página" específica del conjunto de datos total devuelto por la consulta.Use Take along with the Skip method to request a specific "page" of the total dataset returned by the query. Cuando se ejecuta la siguiente consulta, se devuelven los tres primeros elementos de la tabla.The following query, when executed, returns the top three items in the table.

// Define a filtered query that returns the top 3 items.
MobileServiceTableQuery<TodoItem> query = todoTable.Take(3);
List<TodoItem> items = await query.ToListAsync();

La siguiente consulta revisada omite los tres primeros resultados y devuelve los tres siguientes.The following revised query skips the first three results and returns the next three results. Esta consulta genera la segunda página de datos en la que el tamaño de página cuenta con tres elementos.This query produces the second "page" of data, where the page size is three items.

// Define a filtered query that skips the top 3 items and returns the next 3 items.
MobileServiceTableQuery<TodoItem> query = todoTable.Skip(3).Take(3);
List<TodoItem> items = await query.ToListAsync();

El método IncludeTotalCount solicita el recuento total de todos los registros que habría que devolver, con lo que se omite cualquier cláusula de limitación/paginación especificada:The IncludeTotalCount method requests the total count for all the records that would have been returned, ignoring any paging/limit clause specified:

query = query.IncludeTotalCount();

En una aplicación real, puede usar consultas similares a las anteriores con un control de paginación o una interfaz de usuario comparable para permitir a los usuarios desplazarse entre las páginas.In a real world app, you can use queries similar to the preceding example with a pager control or comparable UI to navigate between pages.

Nota

Para reemplazar el límite de 50 filas de un back-end de aplicación móvil, también debe aplicar EnableQueryAttribute al método público GET y especificar el comportamiento de paginación.To override the 50-row limit in a Mobile App backend, you must also apply the EnableQueryAttribute to the public GET method and specify the paging behavior. Cuando se aplica al método, lo siguiente establece el máximo de filas devueltas a 1000:When applied to the method, the following sets the maximum returned rows to 1000:

[EnableQuery(MaxTop=1000)]

Instrucciones: Selección de columnas específicasHow to: Select specific columns

Puede especificar qué conjunto de propiedades incluir en los resultados agregando una cláusula Select a su consulta.You can specify which set of properties to include in the results by adding a Select clause to your query. Por ejemplo, el siguiente código muestra cómo seleccionar solo un campo y también cómo seleccionar varios campos y darle formato:For example, the following code shows how to select just one field and also how to select and format multiple fields:

// Select one field -- just the Text
MobileServiceTableQuery<TodoItem> query = todoTable
                .Select(todoItem => todoItem.Text);
List<string> items = await query.ToListAsync();

// Select multiple fields -- both Complete and Text info
MobileServiceTableQuery<TodoItem> query = todoTable
                .Select(todoItem => string.Format("{0} -- {1}",
                    todoItem.Text.PadRight(30), todoItem.Complete ?
                    "Now complete!" : "Incomplete!"));
List<string> items = await query.ToListAsync();

Todas las funciones descritas hasta ahora son aditivas, por lo que podemos mantener el encadenamiento.All the functions described so far are additive, so we can keep chaining them. Cada llamada encadenada afecta a más elementos aparte de la consulta.Each chained call affects more of the query. A continuación se muestra un ejemplo más:One more example:

MobileServiceTableQuery<TodoItem> query = todoTable
                .Where(todoItem => todoItem.Complete == false)
                .Select(todoItem => todoItem.Text)
                .Skip(3).
                .Take(3);
List<string> items = await query.ToListAsync();

Instrucciones: Buscar datos por identificadorHow to: Look up data by ID

La función LookupAsync puede usarse para buscar objetos desde la base de datos con un identificador determinado.The LookupAsync function can be used to look up objects from the database with a particular ID.

// This query filters out the item with the ID of 37BBF396-11F0-4B39-85C8-B319C729AF6D
TodoItem item = await todoTable.LookupAsync("37BBF396-11F0-4B39-85C8-B319C729AF6D");

Instrucciones: Ejecución de consultas sin tipoHow to: Execute untyped queries

Al ejecutar una consulta mediante un objeto de tabla sin tipo, debe especificar explícitamente la cadena de consulta de OData llamando a ReadAsync, como en el ejemplo siguiente:When executing a query using an untyped table object, you must explicitly specify the OData query string by calling ReadAsync, as in the following example:

// Lookup untyped data using OData
JToken untypedItems = await untypedTodoTable.ReadAsync("$filter=complete eq 0&$orderby=text");

Vuelva a obtener valores JSON que puede usar como un contenedor de propiedades.You get back JSON values that you can use like a property bag. Para obtener más información sobre JToken y Newtonsoft Json.NET, visite el sitio de Json.NET .For more information on JToken and Newtonsoft Json.NET, see the Json.NET site.

Instrucciones: Inserción de datos en el back-end de una aplicación móvilHow to: Insert data into a Mobile App backend

Todos los tipos de cliente deben incluir un miembro llamado Id, que es una cadena de forma predeterminada.All client types must contain a member named Id, which is by default a string. Este elemento Id es necesario para realizar operaciones CRUD y para trabajar sin conexión. El siguiente código muestra cómo usar el método InsertAsync para insertar filas nuevas en una tabla.This Id is required to perform CRUD operations and for offline sync. The following code illustrates how to use the InsertAsync method to insert new rows into a table. El parámetro contiene los datos que se van a insertar como un objeto .NET.The parameter contains the data to be inserted as a .NET object.

await todoTable.InsertAsync(todoItem);

Si no se incluye un valor de identificador personalizado exclusivo en todoItem durante una inserción, el servidor genera un GUID.If a unique custom ID value is not included in the todoItem during an insert, a GUID is generated by the server. Puede recuperar el identificador generado inspeccionando el objeto después de que se devuelva la llamada.You can retrieve the generated Id by inspecting the object after the call returns.

Para insertar datos sin tipo, puede utilizar Json.NET:To insert untyped data, you may take advantage of Json.NET:

JObject jo = new JObject();
jo.Add("Text", "Hello World");
jo.Add("Complete", false);
var inserted = await table.InsertAsync(jo);

A continuación, se muestra un ejemplo en el que se usa una dirección de correo electrónico como identificador de cadena exclusivo:Here is an example using an email address as a unique string id:

JObject jo = new JObject();
jo.Add("id", "myemail@emaildomain.com");
jo.Add("Text", "Hello World");
jo.Add("Complete", false);
var inserted = await table.InsertAsync(jo);

Trabajar con valores de Id.Working with ID values

El servicio Mobile Apps admite valores de cadena personalizados únicos para la columna id de la tabla.Mobile Apps supports unique custom string values for the table's id column. Esto permite a las aplicaciones usar valores personalizados como direcciones de correo electrónico o nombres de usuario para el id.A string value allows applications to use custom values such as email addresses or user names for the ID. Los identificadores de cadena proporcionan las siguientes ventajas:String IDs provide you with the following benefits:

  • Se generan identificadores sin realizar una vuelta a la base de datos.IDs are generated without making a round trip to the database.
  • Los registros son más fáciles de fusionar desde diferentes tablas o bases de datos.Records are easier to merge from different tables or databases.
  • Los valores de los identificadores pueden integrarse mejor con una lógica de aplicación.IDs values can integrate better with an application's logic.

Cuando no se establece un valor de identificador de cadena en un registro insertado, el back-end de la aplicación móvil genera un valor único para el identificador.When a string ID value is not set on an inserted record, the Mobile App backend generates a unique value for the ID. Puede usar el método Guid.NewGuid para generar sus propios valores de identificador, ya sea en el cliente o en el back-end.You can use the Guid.NewGuid method to generate your own ID values, either on the client or in the backend.

JObject jo = new JObject();
jo.Add("id", Guid.NewGuid().ToString("N"));

Instrucciones: Modificación de datos en el back-end de una aplicación móvilHow to: Modify data in a Mobile App backend

El siguiente código muestra cómo usar el método UpdateAsync para actualizar un registro existente con el mismo identificador con nueva información.The following code illustrates how to use the UpdateAsync method to update an existing record with the same ID with new information. El parámetro contiene los datos que se van a actualizar como un objeto .NET.The parameter contains the data to be updated as a .NET object.

await todoTable.UpdateAsync(todoItem);

Para actualizar datos sin tipo, puede usar Json.NET de la siguiente manera:To update untyped data, you may take advantage of Json.NET as follows:

JObject jo = new JObject();
jo.Add("id", "37BBF396-11F0-4B39-85C8-B319C729AF6D");
jo.Add("Text", "Hello World");
jo.Add("Complete", false);
var inserted = await table.UpdateAsync(jo);

Debe especificarse un campo id al realizar una actualización.An id field must be specified when making an update. El back-end utiliza el campo id para identificar qué fila actualizar.The backend uses the id field to identify which row to update. El campo id puede obtenerse a partir del resultado de la llamada a InsertAsync.The id field can be obtained from the result of the InsertAsync call. Se genera una excepción ArgumentException cuando trata de actualizar un elemento sin proporcionar el valor id.An ArgumentException is raised if you try to update an item without providing the id value.

Instrucciones: Eliminación de datos del back-end de una aplicación móvilHow to: Delete data in a Mobile App backend

El siguiente código muestra cómo usar el método DeleteAsync para eliminar una instancia existente.The following code illustrates how to use the DeleteAsync method to delete an existing instance. La instancia se identifica mediante el campo id establecido en todoItem.The instance is identified by the id field set on the todoItem.

await todoTable.DeleteAsync(todoItem);

Para eliminar datos sin tipo, puede aprovechar Json.NET de la siguiente manera:To delete untyped data, you may take advantage of Json.NET as follows:

JObject jo = new JObject();
jo.Add("id", "37BBF396-11F0-4B39-85C8-B319C729AF6D");
await table.DeleteAsync(jo);

Al realizar una solicitud de eliminación, debe especificarse un identificador.When you make a delete request, an ID must be specified. Otras propiedades no se pasan al servicio o se omiten en este.Other properties are not passed to the service or are ignored at the service. El resultado de una llamada DeleteAsync normalmente es null.The result of a DeleteAsync call is usually null. El identificador puede obtenerse a partir del resultado de la llamada InsertAsync .The ID to pass in can be obtained from the result of the InsertAsync call. Se produce una excepción MobileServiceInvalidOperationException cuando se trata de eliminar un elemento sin especificar el campo id.A MobileServiceInvalidOperationException is thrown when you try to delete an item without specifying the id field.

Instrucciones: Uso de la simultaneidad optimista para resolver conflictosHow to: Use Optimistic Concurrency for conflict resolution

Dos o más clientes pueden escribir cambios en el mismo elemento y al mismo tiempo.Two or more clients may write changes to the same item at the same time. Si no se produjera la detección de conflictos, la última escritura sobrescribiría cualquier actualización anterior.Without conflict detection, the last write would overwrite any previous updates. control de simultaneidad optimista asume que cada transacción puede confirmarse y, por lo tanto, no usa ningún bloqueo de recursos.Optimistic concurrency control assumes that each transaction can commit and therefore does not use any resource locking. Antes de confirmar una transacción, el control de simultaneidad optimista comprueba que ninguna otra transacción haya modificado los datos.Before committing a transaction, optimistic concurrency control verifies that no other transaction has modified the data. Si los datos se han modificado, la transacción de confirmación se desecha.If the data has been modified, the committing transaction is rolled back.

El servicio Mobile Apps es compatible con el control de simultaneidad optimista gracias al seguimiento de cambios en cada elemento mediante la columna de propiedades del sistema version que se definió en cada tabla en el back-end de la aplicación móvil.Mobile Apps supports optimistic concurrency control by tracking changes to each item using the version system property column that is defined for each table in your Mobile App backend. Cada vez que se actualiza un registro, el servicio Mobile Apps establece la propiedad version de ese registro en un nuevo valor.Each time a record is updated, Mobile Apps sets the version property for that record to a new value. Durante cada solicitud de actualización, la propiedad version del registro incluido con la solicitud se compara con la misma propiedad del registro en el servidor.During each update request, the version property of the record included with the request is compared to the same property for the record on the server. Si la versión que pasa con la solicitud no coincide con el back-end, la biblioteca de cliente genera una excepción MobileServicePreconditionFailedException<T> .If the version passed with the request does not match the backend, then the client library raises a MobileServicePreconditionFailedException<T> exception. El tipo incluido con la excepción es el registro del back-end que contiene la versión del registro del servidor.The type included with the exception is the record from the backend containing the servers version of the record. A continuación, la aplicación puede usar esta información para decidir si ejecutar la solicitud de actualización de nuevo con el valor version correcto del back-end para confirmar los cambios.The application can then use this information to decide whether to execute the update request again with the correct version value from the backend to commit changes.

Defina una columna en la clase de tabla para la propiedad del sistema version con el fin de habilitar la simultaneidad optimista.Define a column on the table class for the version system property to enable optimistic concurrency. Por ejemplo:For example:

public class TodoItem
{
    public string Id { get; set; }

    [JsonProperty(PropertyName = "text")]
    public string Text { get; set; }

    [JsonProperty(PropertyName = "complete")]
    public bool Complete { get; set; }

    // *** Enable Optimistic Concurrency *** //
    [JsonProperty(PropertyName = "version")]
    public string Version { set; get; }
}

Las aplicaciones con tablas sin tipo permiten la simultaneidad optimista mediante el establecimiento de la marca Version en SystemProperties de la tabla de la siguiente forma.Applications using untyped tables enable optimistic concurrency by setting the Version flag on the SystemProperties of the table as follows.

//Enable optimistic concurrency by retrieving version
todoTable.SystemProperties |= MobileServiceSystemProperties.Version;

Además de habilitar la simultaneidad optimista, se debe detectar la excepción MobileServicePreconditionFailedException<T> en el código al llamar a UpdateAsync.In addition to enabling optimistic concurrency, you must also catch the MobileServicePreconditionFailedException<T> exception in your code when calling UpdateAsync. Resuelva el conflicto aplicando el valor version correcto al registro actualizado y llame a UpdateAsync con el registro resuelto.Resolve the conflict by applying the correct version to the updated record and call UpdateAsync with the resolved record. El siguiente código muestra cómo resolver un conflicto de escritura detectado:The following code shows how to resolve a write conflict once detected:

private async void UpdateToDoItem(TodoItem item)
{
    MobileServicePreconditionFailedException<TodoItem> exception = null;

    try
    {
        //update at the remote table
        await todoTable.UpdateAsync(item);
    }
    catch (MobileServicePreconditionFailedException<TodoItem> writeException)
    {
        exception = writeException;
    }

    if (exception != null)
    {
        // Conflict detected, the item has changed since the last query
        // Resolve the conflict between the local and server item
        await ResolveConflict(item, exception.Item);
    }
}


private async Task ResolveConflict(TodoItem localItem, TodoItem serverItem)
{
    //Ask user to choose the resolution between versions
    MessageDialog msgDialog = new MessageDialog(
        String.Format("Server Text: \"{0}\" \nLocal Text: \"{1}\"\n",
        serverItem.Text, localItem.Text),
        "CONFLICT DETECTED - Select a resolution:");

    UICommand localBtn = new UICommand("Commit Local Text");
    UICommand ServerBtn = new UICommand("Leave Server Text");
    msgDialog.Commands.Add(localBtn);
    msgDialog.Commands.Add(ServerBtn);

    localBtn.Invoked = async (IUICommand command) =>
    {
        // To resolve the conflict, update the version of the item being committed. Otherwise, you will keep
        // catching a MobileServicePreConditionFailedException.
        localItem.Version = serverItem.Version;

        // Updating recursively here just in case another change happened while the user was making a decision
        UpdateToDoItem(localItem);
    };

    ServerBtn.Invoked = async (IUICommand command) =>
    {
        RefreshTodoItems();
    };

    await msgDialog.ShowAsync();
}

Para obtener más información, consulte el tema Sincronización de datos sin conexión en Aplicaciones móviles de Azure .For more information, see the Offline Data Sync in Azure Mobile Apps topic.

Instrucciones: Enlace de datos de Mobile Apps a una interfaz de usuario de WindowsHow to: Bind Mobile Apps data to a Windows user interface

En esta sección se describe cómo mostrar objetos de datos devueltos mediante elementos de la interfaz de usuario en una aplicación Windows.This section shows how to display returned data objects using UI elements in a Windows app. El ejemplo de código siguiente se enlaza al origen de la lista con una consulta de elementos incompletos.The following example code binds to the source of the list with a query for incomplete items. MobileServiceCollection crea una colección de enlaces compatible con Mobile Apps.The MobileServiceCollection creates a Mobile Apps-aware binding collection.

// This query filters out completed TodoItems.
MobileServiceCollection<TodoItem, TodoItem> items = await todoTable
    .Where(todoItem => todoItem.Complete == false)
    .ToCollectionAsync();

// itemsControl is an IEnumerable that could be bound to a UI list control
IEnumerable itemsControl  = items;

// Bind this to a ListBox
ListBox lb = new ListBox();
lb.ItemsSource = items;

Algunos controles en tiempo de ejecución administrado admiten una interfaz denominada ISupportIncrementalLoading.Some controls in the managed runtime support an interface called ISupportIncrementalLoading. Esta interfaz permite a los controles solicitar datos adicionales cuando el usuario se desplaza.This interface allows controls to request extra data when the user scrolls. Las aplicaciones universales de Windows integran compatibilidad con esta interfaz mediante MobileServiceIncrementalLoadingCollection, que administra automáticamente las llamadas desde los controles.There is built-in support for this interface for universal Windows apps via MobileServiceIncrementalLoadingCollection, which automatically handles the calls from the controls. Use MobileServiceIncrementalLoadingCollection en aplicaciones Windows de la siguiente manera:Use MobileServiceIncrementalLoadingCollection in Windows apps as follows:

MobileServiceIncrementalLoadingCollection<TodoItem,TodoItem> items;
items = todoTable.Where(todoItem => todoItem.Complete == false).ToIncrementalLoadingCollection();

ListBox lb = new ListBox();
lb.ItemsSource = items;

Para usar la nueva colección en aplicaciones de Windows Phone 8 y "Silverlight", use los métodos de extensión ToCollection en IMobileServiceTableQuery<T> y IMobileServiceTable<T>.To use the new collection on Windows Phone 8 and "Silverlight" apps, use the ToCollection extension methods on IMobileServiceTableQuery<T> and IMobileServiceTable<T>. Para cargar datos, llame a LoadMoreItemsAsync().To load data, call LoadMoreItemsAsync().

MobileServiceCollection<TodoItem, TodoItem> items = todoTable.Where(todoItem => todoItem.Complete==false).ToCollection();
await items.LoadMoreItemsAsync();

Cuando use la colección creada mediante la llamada a ToCollectionAsync o ToCollection, obtendrá una colección que puede enlazarse a los controles de la interfaz de usuario.When you use the collection created by calling ToCollectionAsync or ToCollection, you get a collection that can be bound to UI controls. Esta colección es para la paginación.This collection is paging-aware. Como la colección está cargando datos desde la red, a veces, pueden producirse errores en este proceso.Since the collection is loading data from the network, loading sometimes fails. Para gestionar esos errores, puede reemplazar el método OnException de MobileServiceIncrementalLoadingCollection con el fin de controlar las excepciones resultantes de las llamadas a LoadMoreItemsAsync.To handle such failures, override the OnException method on MobileServiceIncrementalLoadingCollection to handle exceptions resulting from calls to LoadMoreItemsAsync.

Imagine que la tabla contiene muchos campos, pero solo quiere que se muestren algunos en el control.Consider if your table has many fields but you only want to display some of them in your control. Puede usar la guía de la sección anteriorSelección de columnas específicascon el fin de elegir las columnas específicas que se mostrarán en la interfaz de usuario.You may use the guidance in the preceding section "Select specific columns" to select specific columns to display in the UI.

Cambio del tamaño de páginaChange the Page size

Mobile Apps de Azure devuelve un máximo de 50 elementos por cada solicitud de forma predeterminada.Azure Mobile Apps returns a maximum of 50 items per request by default. Puede cambiar el tamaño de paginación si aumenta el de página máximo en el cliente y el servidor.You can change the paging size by increasing the maximum page size on both the client and server. Para aumentar el tamaño de página solicitado, especifique PullOptions al usar PullAsync():To increase the requested page size, specify PullOptions when using PullAsync():

PullOptions pullOptions = new PullOptions
    {
        MaxPageSize = 100
    };

Suponiendo que ha establecido el valor de PageSize igual o mayor que 100 en el servidor, se devolverán hasta 100 elementos en cada solicitud.Assuming you have made the PageSize equal to or greater than 100 within the server, a request returns up to 100 items.

Trabajo con tablas sin conexiónWork with Offline Tables

Las tablas sin conexión utilizan un almacén SQLite local para almacenar datos para usarlos cuando estén sin conexión.Offline tables use a local SQLite store to store data for use when offline. Todas las operaciones de las tablas se realizan en el almacén SQLite local, en lugar del almacén del servidor remoto.All table operations are done against the local SQLite store instead of the remote server store. Para crear una tabla sin conexión, prepare primero el proyecto:To create an offline table, first prepare your project:

  1. En Visual Studio, haga clic con el botón derecho en la solución > Administrar paquetes NuGet para la solución... y, después, busque e instale el paquete NuGet Microsoft.Azure.Mobile.Client.SQLiteStore en todos los proyectos de la solución.In Visual Studio, right-click the solution > Manage NuGet Packages for Solution..., then search for and install the Microsoft.Azure.Mobile.Client.SQLiteStore NuGet package for all projects in the solution.

  2. (Opcional) Para admitir dispositivos de Windows, instale uno de los siguientes paquetes en el sistema de tiempo de ejecución de SQLite:(Optional) To support Windows devices, install one of the following SQLite runtime packages:

  3. (Opcional).(Optional). En el caso de los dispositivos Windows, haga clic en Referencias > Agregar referencia... , expanda la carpeta Windows > Extensiones y habilite el SDK de SQLite para Windows apropiado, junto con el SDK de Runtime de Visual C++ 2013 para Windows.For Windows devices, click References > Add Reference..., expand the Windows folder > Extensions, then enable the appropriate SQLite for Windows SDK along with the Visual C++ 2013 Runtime for Windows SDK. Los nombres de SDK de SQLite varían ligeramente con cada plataforma de Windows.The SQLite SDK names vary slightly with each Windows platform.

Para poder crear una referencia de tabla, debe prepararse el almacén local:Before a table reference can be created, the local store must be prepared:

var store = new MobileServiceSQLiteStore(Constants.OfflineDbPath);
store.DefineTable<TodoItem>();

//Initializes the SyncContext using the default IMobileServiceSyncHandler.
await this.client.SyncContext.InitializeAsync(store);

Normalmente, la inicialización del almacén se realiza inmediatamente después de que se crea el cliente.Store initialization is normally done immediately after the client is created. OfflineDbPath debe ser un nombre de archivo adecuado para usarlo en todas las plataformas compatibles.The OfflineDbPath should be a filename suitable for use on all platforms that you support. Si la ruta de acceso es completa (es decir, comienza con una barra diagonal), se utiliza dicha ruta.If the path is a fully qualified path (that is, it starts with a slash), then that path is used. Si la ruta de acceso no es completa, el archivo se coloca en una ubicación específica de la plataforma.If the path is not fully qualified, the file is placed in a platform-specific location.

  • En los dispositivos iOS y Android, la ruta de acceso predeterminada es la carpeta "Personal Files".For iOS and Android devices, the default path is the "Personal Files" folder.
  • En los dispositivos de Windows, la ruta de acceso predeterminada es la carpeta "AppData" específica de la aplicación.For Windows devices, the default path is the application-specific "AppData" folder.

Un referencia de tabla se puede obtener mediante el método GetSyncTable<>:A table reference can be obtained using the GetSyncTable<> method:

var table = client.GetSyncTable<TodoItem>();

No es necesario autenticarse para usar una tabla sin conexión.You do not need to authenticate to use an offline table. Solo es preciso hacerlo cuando se establece comunicación con el servicio back-end.You only need to authenticate when you are communicating with the backend service.

Sincronización de una tabla sin conexiónSyncing an Offline Table

Las tablas sin conexión no se sincronizan con el back-end de manera predeterminada.Offline tables are not synchronized with the backend by default. La sincronización se divide en dos partes.Synchronization is split into two pieces. Mediante la descarga de elementos nuevos puede insertar los cambios por separado.You can push changes separately from downloading new items. Éste es un método de sincronización típico:Here is a typical sync method:

public async Task SyncAsync()
{
    ReadOnlyCollection<MobileServiceTableOperationError> syncErrors = null;

    try
    {
        await this.client.SyncContext.PushAsync();

        await this.todoTable.PullAsync(
            //The first parameter is a query name that is used internally by the client SDK to implement incremental sync.
            //Use a different query name for each unique query in your program
            "allTodoItems",
            this.todoTable.CreateQuery());
    }
    catch (MobileServicePushFailedException exc)
    {
        if (exc.PushResult != null)
        {
            syncErrors = exc.PushResult.Errors;
        }
    }

    // Simple error/conflict handling. A real application would handle the various errors like network conditions,
    // server conflicts and others via the IMobileServiceSyncHandler.
    if (syncErrors != null)
    {
        foreach (var error in syncErrors)
        {
            if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
            {
                //Update failed, reverting to server's copy.
                await error.CancelAndUpdateItemAsync(error.Result);
            }
            else
            {
                // Discard local change.
                await error.CancelAndDiscardItemAsync();
            }

            Debug.WriteLine(@"Error executing sync operation. Item: {0} ({1}). Operation discarded.", error.TableName, error.Item["id"]);
        }
    }
}

Si el primer argumento para PullAsync es nulo, no se usa la sincronización incremental.If the first argument to PullAsync is null, then incremental sync is not used. Todas las operaciones de sincronización recuperan todos los registros.Each sync operation retrieves all records.

El SDK realiza una tarea PushAsync() implícita antes de extraer registros.The SDK performs an implicit PushAsync() before pulling records.

El control de los conflictos se realiza en un método PullAsync().Conflict handling happens on a PullAsync() method. Los conflictos se pueden tratar de la misma manera que las tablas en línea.You can deal with conflicts in the same way as online tables. El conflicto se produce cuando se llama a PullAsync(), en lugar de durante la inserción, actualización o eliminación.The conflict is produced when PullAsync() is called instead of during the insert, update, or delete. Si se producen varios conflictos, se agrupan en una sola clase MobileServicePushFailedException.If multiple conflicts happen, they are bundled into a single MobileServicePushFailedException. Trate cada error por separado.Handle each failure separately.

Trabajo con una API personalizadaWork with a custom API

Una API personalizada le permite definir extremos personalizados que exponen la funcionalidad del servidor que no se asigna a una operación de inserción, actualización, eliminación o lectura.A custom API enables you to define custom endpoints that expose server functionality that does not map to an insert, update, delete, or read operation. Al usar una API personalizada, puede tener más control sobre la mensajería, incluida la lectura y el establecimiento de encabezados de mensajes HTTP y la definición del formato del cuerpo de un mensaje diferente de JSON.By using a custom API, you can have more control over messaging, including reading and setting HTTP message headers and defining a message body format other than JSON.

Puede llamar a una API personalizada al realizar una llamada a una de las sobrecargas del método InvokeApiAsync en el cliente.You call a custom API by calling one of the InvokeApiAsync methods on the client. Por ejemplo, la siguiente línea de código envía una solicitud POST a la API completeAll en el back-end:For example, the following line of code sends a POST request to the completeAll API on the backend:

var result = await client.InvokeApiAsync<MarkAllResult>("completeAll", System.Net.Http.HttpMethod.Post, null);

Se trata de una llamada de método con tipo que requiere que se defina el tipo de devolución de MarkAllResult .This form is a typed method call and requires that the MarkAllResult return type is defined. Se admiten métodos con y sin tipos.Both typed and untyped methods are supported.

El método InvokeApiAsync() antepone "/api/" a la API a la que desea llamar, a menos que la API comience por "/".The InvokeApiAsync() method prepends '/api/' to the API that you wish to call unless the API starts with a '/'. Por ejemplo:For example:

  • InvokeApiAsync("completeAll",...)llama a /api/completeAll en el back-endInvokeApiAsync("completeAll",...) calls /api/completeAll on the backend
  • InvokeApiAsync("/.auth/me",...)llama a /.auth/me en el back-endInvokeApiAsync("/.auth/me",...) calls /.auth/me on the backend

Puede usar InvokeApiAsync para llamar a cualquier WebAPI, incluidas las que no están definidas en Azure Mobile Apps.You can use InvokeApiAsync to call any WebAPI, including those WebAPIs that are not defined with Azure Mobile Apps. Cuando usa InvokeApiAsync(), se envían los encabezados correspondientes, incluidos los encabezados de autenticación, con la solicitud.When you use InvokeApiAsync(), the appropriate headers, including authentication headers, are sent with the request.

Autenticación de usuariosAuthenticate users

Mobile Apps admite la autenticación y autorización de usuarios de la aplicación que usan diversos proveedores de identidades externos: Facebook, Google, cuenta de Microsoft, Twitter, y Azure Active Directory.Mobile Apps supports authenticating and authorizing app users using various external identity providers: Facebook, Google, Microsoft Account, Twitter, and Azure Active Directory. Puede establecer permisos en tablas para restringir el acceso a operaciones específicas solo a usuarios autenticados.You can set permissions on tables to restrict access for specific operations to only authenticated users. También puede usar la identidad de usuarios autenticados para implementar reglas de autorización en scripts del servidor.You can also use the identity of authenticated users to implement authorization rules in server scripts. Para obtener más información, consulte el tutorial Incorporación de autenticación a la aplicación.For more information, see the tutorial Add authentication to your app.

Se admiten dos flujos de autenticación: administrado por cliente y administrado por servidor.Two authentication flows are supported: client-managed and server-managed flow. Este último ofrece la experiencia de autenticación más simple, ya que se basa en la interfaz de autenticación web del proveedor.The server-managed flow provides the simplest authentication experience, as it relies on the provider's web authentication interface. El flujo administrado por cliente permite una mayor integración con funcionalidades específicas del dispositivo, ya que se basa en SDK específicos del dispositivo y específicos del proveedor.The client-managed flow allows for deeper integration with device-specific capabilities as it relies on provider-specific device-specific SDKs.

Nota

En las aplicaciones de producción se recomienda usar un flujo administrado por el cliente.We recommend using a client-managed flow in your production apps.

Para configurar la autenticación, debe registrar la aplicación en uno o varios proveedores de identidades.To set up authentication, you must register your app with one or more identity providers. El proveedor de identidades generará un identificador y un secreto de cliente para la aplicación.The identity provider generates a client ID and a client secret for your app. Estos valores se establecen en el back-end para habilitar la autenticación y autorización de Azure App Service.These values are then set in your backend to enable Azure App Service authentication/authorization. Para obtener más información, siga las instrucciones detalladas del tutorial Incorporación de autenticación a la aplicación.For more information, follow the detailed instructions in the tutorial Add authentication to your app.

En esta sección se tratan los siguientes temas:The following topics are covered in this section:

Autenticación administrada por el clienteClient-managed authentication

La aplicación puede ponerse en contacto de manera independiente con el proveedor de identidades y proporcionar el token devuelto en el inicio de sesión junto con el back-end.Your app can independently contact the identity provider and then provide the returned token during login with your backend. Este flujo de cliente permite proporcionar una experiencia de inicio de sesión único a los usuarios o recuperar datos de usuario adicionales del proveedor de identidades.This client flow enables you to provide a single sign-on experience for users or to retrieve additional user data from the identity provider. Se prefiere la autenticación de flujo de cliente al uso de una de flujo de servidor, ya que el SDK de proveedor de identidades proporciona una experiencia UX más nativa y permite realizar más personalizaciones.Client flow authentication is preferred to using a server flow as the identity provider SDK provides a more native UX feel and allows for additional customization.

Se proporcionan ejemplos de los siguientes patrones de autenticación de flujo de cliente:Examples are provided for the following client-flow authentication patterns:

Autenticación de usuarios con la biblioteca de autenticación de Active DirectoryAuthenticate users with the Active Directory Authentication Library

La biblioteca de autenticación de Active Directory (ADAL) se puede usar para iniciar la autenticación de usuarios desde el cliente con la autenticación de Azure Active Directory.You can use the Active Directory Authentication Library (ADAL) to initiate user authentication from the client using Azure Active Directory authentication.

  1. Configure su back-end de aplicación móvil para el inicio de sesión en AAD siguiendo el tutorial Configuración de App Service para usar el inicio de sesión de Azure Active Directory .Configure your mobile app backend for AAD sign-on by following the How to configure App Service for Active Directory login tutorial. Asegúrese de completar el paso opcional de registrar una aplicación cliente nativa.Make sure to complete the optional step of registering a native client application.

  2. En Visual Studio o Xamarin Studio, abra el proyecto y agregue una referencia al paquete NuGet Microsoft.IdentityModel.Clients.ActiveDirectory .In Visual Studio or Xamarin Studio, open your project and add a reference to the Microsoft.IdentityModel.Clients.ActiveDirectory NuGet package. Al buscar, incluya las versiones preliminares.When searching, include pre-release versions.

  3. Agregue el siguiente código a la aplicación, según la plataforma que utilice.Add the following code to your application, according to the platform you are using. En cada caso, realice las sustituciones siguientes:In each, make the following replacements:

    • Reemplace INSERT-AUTHORITY-HERE por el nombre del inquilino en el que aprovisionó la aplicación.Replace INSERT-AUTHORITY-HERE with the name of the tenant in which you provisioned your application. El formato debe ser https://login.microsoftonline.com/contoso.onmicrosoft.com.The format should be https://login.microsoftonline.com/contoso.onmicrosoft.com. Este valor se puede copiar de la pestaña Dominio de Azure Active Directory en Azure Portal.This value can be copied from the Domain tab in your Azure Active Directory in the Azure portal.

    • Reemplace INSERT-RESOURCE-ID-HERE por el Id. de cliente del back-end de la aplicación móvil.Replace INSERT-RESOURCE-ID-HERE with the client ID for your mobile app backend. El Id. de cliente en la pestaña Opciones avanzadas de Configuración de Azure Active Directory en el portal.You can obtain the client ID from the Advanced tab under Azure Active Directory Settings in the portal.

    • Reemplace INSERT-CLIENT-ID-HERE por el Id. de cliente que copió de la aplicación cliente nativa.Replace INSERT-CLIENT-ID-HERE with the client ID you copied from the native client application.

    • Reemplace INSERT-REDIRECT-URI-HERE por el punto de conexión /.auth/login/done del sitio, mediante el esquema HTTPS.Replace INSERT-REDIRECT-URI-HERE with your site's /.auth/login/done endpoint, using the HTTPS scheme. El valor debería parecerse al siguiente: https://contoso.azurewebsites.net/.auth/login/done .This value should be similar to https://contoso.azurewebsites.net/.auth/login/done.

      El código necesario para cada plataforma es el siguiente:The code needed for each platform follows:

      Windows:Windows:

      private MobileServiceUser user;
      private async Task AuthenticateAsync()
      {
      
         string authority = "INSERT-AUTHORITY-HERE";
         string resourceId = "INSERT-RESOURCE-ID-HERE";
         string clientId = "INSERT-CLIENT-ID-HERE";
         string redirectUri = "INSERT-REDIRECT-URI-HERE";
         while (user == null)
         {
             string message;
             try
             {
                 AuthenticationContext ac = new AuthenticationContext(authority);
                 AuthenticationResult ar = await ac.AcquireTokenAsync(resourceId, clientId,
                     new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto, false) );
                 JObject payload = new JObject();
                 payload["access_token"] = ar.AccessToken;
                 user = await App.MobileService.LoginAsync(
                     MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
                 message = string.Format("You are now logged in - {0}", user.UserId);
             }
             catch (InvalidOperationException)
             {
                 message = "You must log in. Login Required";
             }
             var dialog = new MessageDialog(message);
             dialog.Commands.Add(new UICommand("OK"));
             await dialog.ShowAsync();
         }
      }
      

      Xamarin.iOSXamarin.iOS

      private MobileServiceUser user;
      private async Task AuthenticateAsync(UIViewController view)
      {
      
         string authority = "INSERT-AUTHORITY-HERE";
         string resourceId = "INSERT-RESOURCE-ID-HERE";
         string clientId = "INSERT-CLIENT-ID-HERE";
         string redirectUri = "INSERT-REDIRECT-URI-HERE";
         try
         {
             AuthenticationContext ac = new AuthenticationContext(authority);
             AuthenticationResult ar = await ac.AcquireTokenAsync(resourceId, clientId,
                 new Uri(redirectUri), new PlatformParameters(view));
             JObject payload = new JObject();
             payload["access_token"] = ar.AccessToken;
             user = await client.LoginAsync(
                 MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
         }
         catch (Exception ex)
         {
             Console.Error.WriteLine(@"ERROR - AUTHENTICATION FAILED {0}", ex.Message);
         }
      }
      

      Xamarin.AndroidXamarin.Android

      private MobileServiceUser user;
      private async Task AuthenticateAsync()
      {
      
         string authority = "INSERT-AUTHORITY-HERE";
         string resourceId = "INSERT-RESOURCE-ID-HERE";
         string clientId = "INSERT-CLIENT-ID-HERE";
         string redirectUri = "INSERT-REDIRECT-URI-HERE";
         try
         {
             AuthenticationContext ac = new AuthenticationContext(authority);
             AuthenticationResult ar = await ac.AcquireTokenAsync(resourceId, clientId,
                 new Uri(redirectUri), new PlatformParameters(this));
             JObject payload = new JObject();
             payload["access_token"] = ar.AccessToken;
             user = await client.LoginAsync(
                 MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
         }
         catch (Exception ex)
         {
             AlertDialog.Builder builder = new AlertDialog.Builder(this);
             builder.SetMessage(ex.Message);
             builder.SetTitle("You must log in. Login Required");
             builder.Create().Show();
         }
      }
      protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
      {
      
         base.OnActivityResult(requestCode, resultCode, data);
         AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode, resultCode, data);
      }
      

Inicio de sesión único mediante un token de Facebook o GoogleSingle Sign-On using a token from Facebook or Google

Puede usar el flujo de cliente como se muestra en este fragmento para Facebook o Google.You can use the client flow as shown in this snippet for Facebook or Google.

var token = new JObject();
// Replace access_token_value with actual value of your access token obtained
// using the Facebook or Google SDK.
token.Add("access_token", "access_token_value");

private MobileServiceUser user;
private async Task AuthenticateAsync()
{
    while (user == null)
    {
        string message;
        try
        {
            // Change MobileServiceAuthenticationProvider.Facebook
            // to MobileServiceAuthenticationProvider.Google if using Google auth.
            user = await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);
            message = string.Format("You are now logged in - {0}", user.UserId);
        }
        catch (InvalidOperationException)
        {
            message = "You must log in. Login Required";
        }

        var dialog = new MessageDialog(message);
        dialog.Commands.Add(new UICommand("OK"));
        await dialog.ShowAsync();
    }
}

Autenticación administrada por el servidorServer-managed authentication

Una vez que haya registrado el proveedor de identidades, llame al método LoginAsync de [MobileServiceClient] con el valor MobileServiceAuthenticationProvider del proveedor.Once you have registered your identity provider, call the LoginAsync method on the [MobileServiceClient] with the MobileServiceAuthenticationProvider value of your provider. Por ejemplo, el siguiente código activa un inicio de sesión de flujo de servidor mediante Facebook.For example, the following code initiates a server flow sign-in by using Facebook.

private MobileServiceUser user;
private async System.Threading.Tasks.Task Authenticate()
{
    while (user == null)
    {
        string message;
        try
        {
            user = await client
                .LoginAsync(MobileServiceAuthenticationProvider.Facebook);
            message =
                string.Format("You are now logged in - {0}", user.UserId);
        }
        catch (InvalidOperationException)
        {
            message = "You must log in. Login Required";
        }

        var dialog = new MessageDialog(message);
        dialog.Commands.Add(new UICommand("OK"));
        await dialog.ShowAsync();
    }
}

Si usa un proveedor de identidades que no sea Facebook, cambie el valor de MobileServiceAuthenticationProvider al valor de su proveedor.If you are using an identity provider other than Facebook, change the value of MobileServiceAuthenticationProvider to the value for your provider.

En un flujo de servidor, Azure App Service administra el flujo de autenticación OAuth mostrando la página de inicio de sesión del proveedor seleccionado.In a server flow, Azure App Service manages the OAuth authentication flow by displaying the sign-in page of the selected provider. Cuando se devuelve el proveedor de identidades, Azure App Service genera un token de autenticación de este servicio.Once the identity provider returns, Azure App Service generates an App Service authentication token. El método LoginAsync devuelve MobileServiceUser, que proporciona el elemento UserId del usuario autenticado y MobileServiceAuthenticationToken como JSON Web Token (JWT).The LoginAsync method returns a MobileServiceUser, which provides both the UserId of the authenticated user and the MobileServiceAuthenticationToken, as a JSON web token (JWT). El token puede almacenarse en caché y volver a usarse hasta que expire.This token can be cached and reused until it expires. Para obtener más información, consulte Almacenamiento en caché del token de autenticación.For more information, see Caching the authentication token.

Almacenamiento en caché del token de autenticaciónCaching the authentication token

En algunos casos, la llamada al método de inicio de sesión se puede evitar tras la primera autenticación correcta. Para ello, es preciso almacenar el token de autenticación del proveedor.In some cases, the call to the login method can be avoided after the first successful authentication by storing the authentication token from the provider. Las aplicaciones de Microsoft Store y UWP pueden usar PasswordVault para almacenar en caché el token de autenticación actual después de un inicio de sesión correcto, como se indica a continuación:Microsoft Store and UWP apps can use PasswordVault to cache the current authentication token after a successful sign-in, as follows:

await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook);

PasswordVault vault = new PasswordVault();
vault.Add(new PasswordCredential("Facebook", client.currentUser.UserId,
    client.currentUser.MobileServiceAuthenticationToken));

El valor de UserId se almacena como el nombre de usuario de la credencial y el token se almacena como la contraseña.The UserId value is stored as the UserName of the credential and the token is the stored as the Password. En los inicios posteriores, puede comprobar si PasswordVault tiene credenciales almacenadas en caché.On subsequent start-ups, you can check the PasswordVault for cached credentials. En el ejemplo siguiente se utilizan credenciales almacenadas en la caché cuando se encuentran y también se intenta volver a realizar la autenticación con el back-end:The following example uses cached credentials when they are found, and otherwise attempts to authenticate again with the backend:

// Try to retrieve stored credentials.
var creds = vault.FindAllByResource("Facebook").FirstOrDefault();
if (creds != null)
{
    // Create the current user from the stored credentials.
    client.currentUser = new MobileServiceUser(creds.UserName);
    client.currentUser.MobileServiceAuthenticationToken =
        vault.Retrieve("Facebook", creds.UserName).Password;
}
else
{
    // Regular login flow and cache the token as shown above.
}

Cuando se cierre la sesión de un usuario, también es preciso quitar la credencial almacenada, tal y como se muestra a continuación:When you sign out a user, you must also remove the stored credential, as follows:

client.Logout();
vault.Remove(vault.Retrieve("Facebook", client.currentUser.UserId));

Las aplicaciones de Xamarin utilizan las API de Xamarin.Auth para almacenar de forma segura las credenciales en un objeto Account .Xamarin apps use the Xamarin.Auth APIs to securely store credentials in an Account object. Para ver un ejemplo de cómo usar estas API, consulte el archivo de código AuthStore.cs en el ejemplo de uso compartido de fotografías ContosoMoments.For an example of using these APIs, see the AuthStore.cs code file in the ContosoMoments photo sharing sample.

Si utiliza la autenticación administrada por el cliente, también puede almacenar en caché el token de acceso obtenido del proveedor, como Facebook o Twitter.When you use client-managed authentication, you can also cache the access token obtained from your provider such as Facebook or Twitter. Este token se puede especificar al solicitar un nuevo token de autenticación del back-end, tal como se muestra a continuación:This token can be supplied to request a new authentication token from the backend, as follows:

var token = new JObject();
// Replace <your_access_token_value> with actual value of your access token
token.Add("access_token", "<your_access_token_value>");

// Authenticate using the access token.
await client.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);

Notificaciones pushPush Notifications

Los siguientes temas tratan sobre las notificaciones push:The following topics cover Push Notifications:

Instrucciones: Registro de notificaciones pushHow to: Register for Push Notifications

El cliente de Mobile Apps permite registrar las notificaciones push con Azure Notification Hubs.The Mobile Apps client enables you to register for push notifications with Azure Notification Hubs. Al registrar, se obtiene un identificador del servicio de notificaciones push (PNS) específico de la plataforma.When registering, you obtain a handle that you obtain from the platform-specific Push Notification Service (PNS). A continuación, proporcione este valor junto con las etiquetas cuando se cree el registro.You then provide this value along with any tags when you create the registration. El código siguiente registra la aplicación de Windows para las notificaciones push en el Servicio de notificaciones de Windows.(WNS):The following code registers your Windows app for push notifications with the Windows Notification Service (WNS):

private async void InitNotificationsAsync()
{
    // Request a push notification channel.
    var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();

    // Register for notifications using the new channel.
    await MobileService.GetPush().RegisterNativeAsync(channel.Uri, null);
}

Si va a insertar en WNS, DEBE obtener un SID del paquete de Microsoft Store.If you are pushing to WNS, then you MUST obtain a Microsoft Store package SID. Para más información sobre las aplicaciones de Windows, incluyendo cómo registrarse para los registros de plantillas, vea Agregar notificaciones push a la aplicación.For more information on Windows apps, including how to register for template registrations, see Add push notifications to your app.

Tenga en cuenta que no se admite la solicitud de etiquetas del cliente.Requesting tags from the client is not supported. Las solicitudes de etiquetas se quitan del registro en modo silencioso.Tag Requests are silently dropped from registration. Si desea registrar el dispositivo con etiquetas, crear una API personalizada que use la API de Notification Hubs para realizar el registro en su nombre.If you wish to register your device with tags, create a Custom API that uses the Notification Hubs API to perform the registration on your behalf. Llame a la API personalizada, en lugar de al método RegisterNativeAsync().Call the Custom API instead of the RegisterNativeAsync() method.

Instrucciones: Obtención del SID de un paquete de Microsoft StoreHow to: Obtain a Microsoft Store package SID

Se necesita un SID del paquete para habilitar las notificaciones de inserción en aplicaciones de Microsoft Store.A package SID is needed for enabling push notifications in Microsoft Store apps. Para recibir un SID del paquete, registre la aplicación en Microsoft Store.To receive a package SID, register your application with the Microsoft Store.

Para obtener este valor:To obtain this value:

  1. En el Explorador de soluciones de Visual Studio, haga clic con el botón derecho en el proyecto de la aplicación de Microsoft Store y haga clic en Store > Asociar aplicación a Store… .In Visual Studio Solution Explorer, right-click the Microsoft Store app project, click Store > Associate App with the Store....
  2. En el asistente, haga clic en Siguiente, inicie sesión con su cuenta Microsoft, escriba un nombre para la aplicación en Reserve un nuevo nombre de aplicación y haga clic en Reservar.In the wizard, click Next, sign in with your Microsoft account, type a name for your app in Reserve a new app name, then click Reserve.
  3. Después de que el registro de la aplicación se cree correctamente, seleccione el nombre de la aplicación, haga clic en Siguiente y, después, en Asociar.After the app registration is successfully created, select the app name, click Next, and then click Associate.
  4. Inicie sesión en el Centro de desarrollo de Windows con su cuenta Microsoft.Log in to the Windows Dev Center using your Microsoft Account. En Mis aplicaciones, haga clic en el registro de la aplicación que ha creado.Under My apps, click the app registration you created.
  5. Haga clic en Administración de la aplicación > Identidad de la aplicación y, después, desplácese hacia abajo para buscar el SID del paquete.Click App management > App identity, and then scroll down to find your Package SID.

Muchos usos del SID del paquete lo tratan como un URI, en cuyo caso debe usar ms-app:// como esquema.Many uses of the package SID treat it as a URI, in which case you need to use ms-app:// as the scheme. Tome nota de la versión del SID del paquete que se forma concatenando este valor como prefijo.Make note of the version of your package SID formed by concatenating this value as a prefix.

Las aplicaciones de Xamarin requieren más código para poder registrar una aplicación que se ejecute en las plataformas Android o iOS.Xamarin apps require some additional code to be able to register an app running on the iOS or Android platforms. Para obtener más información, consulte el tema sobre su plataforma:For more information, see the topic for your platform:

Instrucciones: Registro de plantillas push para enviar notificaciones entre plataformasHow to: Register push templates to send cross-platform notifications

Para registrar plantillas, use el método RegisterAsync() con ellas, tal y como se indica a continuación:To register templates, use the RegisterAsync() method with the templates, as follows:

JObject templates = myTemplates();
MobileService.GetPush().RegisterAsync(channel.Uri, templates);

Las plantillas serán de tipo JObject y pueden contener varias plantillas con el formato JSON siguiente:Your templates should be JObject types and can contain multiple templates in the following JSON format:

public JObject myTemplates()
{
    // single template for Windows Notification Service toast
    var template = "<toast><visual><binding template=\"ToastText01\"><text id=\"1\">$(message)</text></binding></visual></toast>";

    var templates = new JObject
    {
        ["generic-message"] = new JObject
        {
            ["body"] = template,
            ["headers"] = new JObject
            {
                ["X-WNS-Type"] = "wns/toast"
            },
            ["tags"] = new JArray()
        },
        ["more-templates"] = new JObject {...}
    };
    return templates;
}

El método RegisterAsync() también acepta iconos secundarios:The method RegisterAsync() also accepts Secondary Tiles:

MobileService.GetPush().RegisterAsync(string channelUri, JObject templates, JObject secondaryTiles);

Por seguridad, todas las etiquetas se eliminan durante el registro.All tags are stripped away during registration for security. Para agregar etiquetas a las instalaciones o plantillas dentro de las instalaciones, consulte [Trabajar con el SDK del servidor back-end de .NET para Azure Mobile Apps].To add tags to installations or templates within installations, see [Work with the .NET backend server SDK for Azure Mobile Apps].

Para enviar notificaciones mediante estas plantillas registradas, consulte API de Notification Hubs.To send notifications utilizing these registered templates, refer to the Notification Hubs APIs.

Temas variadosMiscellaneous Topics

Instrucciones: erroresHow to: Handle errors

Si se produce un error en el back-end, el SDK de cliente generará una excepción MobileServiceInvalidOperationException.When an error occurs in the backend, the client SDK raises a MobileServiceInvalidOperationException. En el ejemplo siguiente se muestra cómo controlar una excepción devuelta por el back-end:The following example shows how to handle an exception that is returned by the backend:

private async void InsertTodoItem(TodoItem todoItem)
{
    // This code inserts a new TodoItem into the database. When the operation completes
    // and App Service has assigned an Id, the item is added to the CollectionView
    try
    {
        await todoTable.InsertAsync(todoItem);
        items.Add(todoItem);
    }
    catch (MobileServiceInvalidOperationException e)
    {
        // Handle error
    }
}

Puede encontrar otro ejemplo de cómo tratar las condiciones de error en el ejemplo de archivos de Mobile Apps.Another example of dealing with error conditions can be found in the Mobile Apps Files Sample. El ejemplo de LoggingHandler proporciona un controlador delegado de registro para registrar las solicitudes realizadas al back-end.The LoggingHandler example provides a logging delegate handler to log the requests being made to the backend.

Instrucciones: Personalización de encabezados de solicitudHow to: Customize request headers

Para admitir su escenario de aplicación específico, deberá personalizar la comunicación con el back-end de la aplicación móvil.To support your specific app scenario, you might need to customize communication with the Mobile App backend. Por ejemplo, es posible que desee agregar un encabezado personalizado a cada solicitud saliente o cambiar los códigos de estado de las respuestas.For example, you may want to add a custom header to every outgoing request or even change responses status codes. Puede hacer esto proporcionando un elemento DelegatingHandlerpersonalizado, como en el ejemplo siguiente:You can use a custom DelegatingHandler, as in the following example:

public async Task CallClientWithHandler()
{
    MobileServiceClient client = new MobileServiceClient("AppUrl", new MyHandler());
    IMobileServiceTable<TodoItem> todoTable = client.GetTable<TodoItem>();
    var newItem = new TodoItem { Text = "Hello world", Complete = false };
    await todoTable.InsertAsync(newItem);
}

public class MyHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage>
        SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Change the request-side here based on the HttpRequestMessage
        request.Headers.Add("x-my-header", "my value");

        // Do the request
        var response = await base.SendAsync(request, cancellationToken);

        // Change the response-side here based on the HttpResponseMessage

        // Return the modified response
        return response;
    }
}