Compartir a través de


Uso compartido de recursos entre clientes web y nativos mediante una biblioteca de clases Razor (RCL)

Nota

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión .NET 8 de este artículo.

Use una biblioteca de clases Razor (RCL) para compartir componentes, código de C# y recursos estáticos de Razor en proyectos de cliente web y nativos.

Este artículo se basa en los conceptos generales que se encuentran en los artículos siguientes:

Los ejemplos de este artículo comparten recursos entre una aplicación de Blazor del lado servidor y una aplicación de .NET MAUIBlazor Hybrid en la misma solución:

  • Aunque se usa una aplicación Blazor del lado servidor, la guía se aplica igualmente a las aplicaciones Blazor WebAssembly que comparten recursos con una aplicación Blazor Hybrid.
  • Los proyectos están en la misma solución, pero una RCL puede proporcionar recursos compartidos a proyectos fuera de una solución.
  • La RCL se agrega como un proyecto a la solución, pero cualquier RCL se puede publicar como un paquete NuGet. Un paquete NuGet puede proporcionar recursos compartidos a proyectos de cliente web y nativos.
  • El orden en que se crean los proyectos no es importante. Sin embargo, los proyectos que se basan en una RCL para los recursos deben crear una referencia de proyecto a la RCL después de crear la RCL.

Para más información sobre las RCL, consulte Consumo de componentes de ASP.NET CoreRazor a partir de una biblioteca de clases Razor (RCL). Opcionalmente, acceda a las instrucciones adicionales sobre las RCL que se aplican ampliamente a aplicaciones ASP.NET Core en la interfaz de usuario Razor reutilizable en bibliotecas de clases con ASP.NET Core.

Plataformas de destino para implementaciones de ClickOnce

Para publicar un proyecto de WPF o Windows Forms con una biblioteca de clases Razor (RCL) en .NET 6 con ClickOnce, la RCL debe tener como destino net6.0-windows además de net6.0.

Ejemplo:

<TargetFrameworks>net6.0;net6.0-windows</TargetFrameworks>

Para más información, consulte los siguientes artículos.

Uso compartido de componentes, código y recursos estáticos de Razor de la interfaz de usuario

Los componentes de una RCL se pueden compartir simultáneamente por aplicaciones web y cliente nativas compiladas mediante Blazor. En las instrucciones de Consumo de componentes de Razor de ASP.NET Core de una biblioteca de clases (RCL)Razor se explica cómo compartir componentes de Razor mediante una biblioteca de clases Razor (RCL). La misma guía se aplica a la reutilización de componentes de Razor de una RCL en una aplicación Blazor Hybrid.

Los espacios de nombres de componentes se derivan del identificador de paquete o el nombre del ensamblado de la RCL y la ruta de acceso de la carpeta del componente dentro de la RCL. Para más información, vea Componentes Razor de ASP.NET Core. Las directivas @using pueden colocarse en archivos _Imports.razor para componentes y código, como demuestra el siguiente ejemplo para una RCL llamada SharedLibrary con una carpeta Shared de componentes compartidos de Razor y una carpeta Data de clases de datos compartidos:

@using SharedLibrary
@using SharedLibrary.Shared
@using SharedLibrary.Data

Coloque los recursos estáticos compartidos en la carpeta wwwroot de la RCL y actualice las rutas de acceso de recursos estáticos en la aplicación para usar el siguiente formato de ruta de acceso:

_content/{PACKAGE ID/ASSEMBLY NAME}/{PATH}/{FILE NAME}

Marcadores de posición:

  • {PACKAGE ID/ASSEMBLY NAME}: identificador de paquete o el nombre del ensamblado de la RCL.
  • {PATH}: ruta de acceso opcional dentro de la carpeta wwwroot de la RCL.
  • {FILE NAME}: nombre de archivo del recurso estático.

El formato de ruta de acceso anterior también se usa en la aplicación para los recursos estáticos proporcionados por un paquete NuGet agregado a la RCL.

Para una RCL denominada SharedLibrary y que use la hoja de estilos de arranque minimizada como ejemplo:

_content/SharedLibrary/css/bootstrap/bootstrap.min.css

Para más información sobre cómo compartir recursos estáticos entre proyectos, consulte los artículos siguientes:

El archivo raíz index.html suele ser específico de la aplicación y debe permanecer en la aplicación Blazor Hybrid o en la aplicación Blazor WebAssembly. Normalmente, el archivo index.html no se comparte.

El componente raíz Razor (App.razor o Main.razor) se puede compartir, pero a menudo es posible que tenga que ser específico de la aplicación de hospedaje. Por ejemplo, App.razor es diferente en las plantillas de proyecto Blazor del lado servidor y Blazor WebAssembly cuando la autenticación está habilitada. Puede agregar el parámetro AdditionalAssemblies para especificar la ubicación de los componentes enrutables compartidos, y puede especificar un componente de diseño predeterminado compartido para el enrutador por nombre de tipo.

Provisión de código y servicios independientes del modelo de hospedaje

Cuando el código debe diferir entre modelos de hospedaje o plataformas de destino, abstraiga el código como interfaces e inserte las implementaciones de servicio en cada proyecto.

El siguiente ejemplo de datos meteorológicos abstrae diferentes implementaciones del servicio de previsión meteorológica:

  • Uso de una solicitud HTTP para Blazor Hybrid y Blazor WebAssembly.
  • Solicitar datos directamente para una aplicación de Blazor del lado servidor.

En el ejemplo se usan las siguientes especificaciones y convenciones:

  • La RCL se denomina SharedLibrary y contiene las siguientes carpetas y espacios de nombres:
    • Data: contiene la clase WeatherForecast, que actúa como modelo para los datos meteorológicos.
    • Interfaces: contiene la interfaz de servicio para las implementaciones de servicio, denominadas IWeatherForecastService.
  • El componente FetchData se mantiene en la carpeta Pages de la RCL, que es enrutable por cualquiera de las aplicaciones que consumen la RCL.
  • Cada aplicación Blazor mantiene una implementación de servicio que implementa la interfaz IWeatherForecastService.

Data/WeatherForecast.cs en la RCL:

namespace SharedLibrary.Data;

public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    public string? Summary { get; set; }
}

Interfaces/IWeatherForecastService.cs en la RCL:

using SharedLibrary.Data;

namespace SharedLibrary.Interfaces;

public interface IWeatherForecastService
{
    Task<WeatherForecast[]?> GetForecastAsync(DateTime startDate);
}

El archivo _Imports.razor de la RCL incluye los siguientes espacios de nombres agregados:

@using SharedLibrary.Data
@using SharedLibrary.Interfaces

Services/WeatherForecastService.cs en las aplicaciones Blazor Hybrid y Blazor WebAssembly:

using System.Net.Http.Json;
using SharedLibrary.Data;
using SharedLibrary.Interfaces;

namespace {APP NAMESPACE}.Services;

public class WeatherForecastService : IWeatherForecastService
{
    private readonly HttpClient http;

    public WeatherForecastService(HttpClient http)
    {
        this.http = http;
    }

    public async Task<WeatherForecast[]?> GetForecastAsync(DateTime startDate) =>
        await http.GetFromJsonAsync<WeatherForecast[]?>("WeatherForecast");
}

En el ejemplo anterior, el marcador de posición {APP NAMESPACE} es el nombre de espacio de la aplicación.

Services/WeatherForecastService.cs en la aplicación de Blazor del lado servidor:

using SharedLibrary.Data;
using SharedLibrary.Interfaces;

namespace {APP NAMESPACE}.Services;

public class WeatherForecastService : IWeatherForecastService
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot"
    };

    public async Task<WeatherForecast[]?> GetForecastAsync(DateTime startDate) =>
        await Task.FromResult(Enumerable.Range(1, 5)
            .Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            }).ToArray());
}

En el ejemplo anterior, el marcador de posición {APP NAMESPACE} es el nombre de espacio de la aplicación.

Las aplicaciones Blazor Hybrid, Blazor WebAssembly y Blazor del lado servidor registran sus implementaciones del servicio de previsión meteorológica (Services.WeatherForecastService) para IWeatherForecastService.

El proyecto Blazor WebAssembly también registra el valor HttpClient. El valor HttpClient registrado de forma predeterminada en una aplicación creada a partir de la plantilla de proyecto Blazor WebAssembly es suficiente para este propósito. Para obtener más información, vea Llamada a una API web desde una aplicación Blazor de ASP.NET Core.

Pages/FetchData.razor en la RCL:

@page "/fetchdata"
@inject IWeatherForecastService ForecastService

<PageTitle>Weather forecast</PageTitle>

<h1>Weather forecast</h1>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
    }
}

Recursos adicionales