Partilhar via


Compartilhe ativos entre clientes Web e nativos usando uma biblioteca de classes Razor (RCL)

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.

Advertência

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.

Importante

Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.

Para a versão atual, consulte a versão .NET 9 deste artigo.

Use uma biblioteca de classe Razor (RCL) para compartilhar componentes Razor, código C# e ativos estáticos em projetos Web e clientes nativos.

Este artigo baseia-se nos conceitos gerais encontrados nos seguintes artigos:

Os exemplos neste artigo compartilham ativos entre um aplicativo Blazor do lado do servidor e um aplicativo .NET MAUIBlazor Hybrid na mesma solução:

  • Embora um aplicativo Blazor do lado do servidor seja usado, as diretrizes se aplicam igualmente a aplicativos Blazor WebAssembly que compartilham ativos com um aplicativo Blazor Hybrid.
  • Os projetos estão na mesma solução , mas uma RCL pode fornecer ativos compartilhados para projetos fora de uma solução.
  • A RCL é adicionada como um projeto à solução, mas qualquer RCL pode ser publicada como um pacote NuGet. Um pacote NuGet pode fornecer ativos compartilhados para projetos Web e clientes nativos.
  • A ordem em que os projetos são criados não é importante. No entanto, os projetos que dependem de um RCL para ativos devem criar uma referência de projeto para o RCL após o RCL for criado.

Para obter orientação sobre como criar uma RCL, consulte Consumir componentes ASP.NET Core Razor de uma biblioteca de classes Razor (RCL). Opcionalmente, acesse as orientações adicionais sobre RCLs que se aplicam amplamente aos aplicativos ASP.NET Core em interface do usuário Razor reutilizável em bibliotecas de classe com ASP.NET Core.

Estruturas de destino para implantações do ClickOnce

Para publicar um projeto WPF ou Windows Forms com uma biblioteca de classes Razor (RCL) no .NET 6 com ClickOnce, a RCL deve ter como alvo net6.0-windows além de net6.0.

Exemplo:

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

Para obter mais informações, consulte os seguintes artigos:

Aplicativo de exemplo

Para obter um exemplo dos cenários descritos neste artigo, consulte o eShop Reference Application (AdventureWorks) (dotnet/eShop repositório GitHub). O aplicativo .NET MAUIBlazor Hybrid está na pasta src/HybridApp.

Para obter uma versão do aplicativo de exemplo adaptado para hospedagem do Azure, consulte o repositório Azure-Samples/eShopOnAzure GitHub.

O aplicativo de exemplo apresenta as seguintes tecnologias:

Partilhar componentes Razor da interface de utilizador da Web, código e recursos estáticos

Os componentes de uma RCL podem ser partilhados simultaneamente por aplicações web e aplicações cliente nativas criadas com Blazor. A orientação em Consumir ASP.NET componentes do Core Razor de uma biblioteca de classes Razor (RCL) explica como compartilhar componentes Razor usando uma biblioteca de classes Razor (RCL). A mesma orientação se aplica à reutilização de componentes Razor de um RCL em um aplicativo Blazor Hybrid.

Os namespaces de componentes são derivados do ID do pacote ou nome do assembly da RCL e do caminho da pasta do componente dentro da RCL. Para obter mais informações, consulte componentes do ASP.NET Core Razor. @using diretivas podem ser colocadas em arquivos _Imports.razor para componentes e código, como o exemplo a seguir demonstra para um RCL chamado SharedLibrary com uma pasta Shared de componentes de Razor compartilhados e uma pasta Data de classes de dados compartilhados:

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

Coloque ativos estáticos compartilhados na pasta wwwroot da RCL e atualize os caminhos de ativos estáticos no aplicativo para usar o seguinte formato de caminho:

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

Marcadores de posição:

  • {PACKAGE ID/ASSEMBLY NAME}: O ID do pacote ou o nome da assemblagem da RCL.
  • {PATH}: Caminho opcional dentro da pasta wwwroot da RCL.
  • {FILE NAME}: O nome do arquivo do ativo estático.

O formato de caminho mencionado anteriormente também é usado na aplicação para recursos estáticos fornecidos por um pacote NuGet adicionado à RCL.

Para um RCL chamado SharedLibrary e usando a folha de estilo Bootstrap reduzida como exemplo:

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

Para obter informações adicionais sobre como compartilhar ativos estáticos entre projetos, consulte os seguintes artigos:

O arquivo de index.html raiz geralmente é específico para o aplicativo e deve permanecer no aplicativo Blazor Hybrid ou no aplicativo Blazor WebAssembly. O arquivo index.html normalmente não é compartilhado.

O componente de raiz Razor (App.razor ou Main.razor) pode ser partilhado, mas muitas vezes poderá necessitar ser específico para a aplicação de alojamento. Por exemplo, App.razor é diferente nos modelos de projeto do lado do servidor Blazor e Blazor WebAssembly quando a autenticação está habilitada. Você pode adicionar o parâmetro AdditionalAssemblies para especificar o local de qualquer componente roteável compartilhado e pode especificar um componente de layout padrão compartilhado para o roteador por nome de tipo.

Fornecer código e serviços independentes do modelo de hospedagem

Quando o código deve diferir entre modelos de hospedagem ou plataformas de destino, abstraia o código como interfaces e injete as implementações de serviço em cada projeto.

O exemplo de dados meteorológicos a seguir abstrai diferentes implementações de serviços de previsão do tempo:

  • Usando uma solicitação HTTP para Blazor Hybrid e Blazor WebAssembly.
  • Solicitar dados diretamente para uma aplicação Blazor do lado do servidor.

O exemplo usa as seguintes especificações e convenções:

  • O RCL é chamado SharedLibrary e contém as seguintes pastas e namespaces:
    • Data: Contém a classe WeatherForecast, que serve de modelo para dados meteorológicos.
    • Interfaces: Contém a interface de serviço para as implementações de serviço, denominada IWeatherForecastService.
  • O componente FetchData é mantido na pasta Pages da RCL, que é roteável por qualquer um dos aplicativos que consomem a RCL.
  • Cada aplicativo Blazor mantém uma implementação de serviço que implementa a interface IWeatherForecastService.

Data/WeatherForecast.cs na 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 na RCL:

using SharedLibrary.Data;

namespace SharedLibrary.Interfaces;

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

O arquivo _Imports.razor no RCL inclui os seguintes namespaces adicionados:

@using SharedLibrary.Data
@using SharedLibrary.Interfaces

Services/WeatherForecastService.cs nos aplicativos Blazor Hybrid e 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");
}

No exemplo anterior, o espaço reservado {APP NAMESPACE} é o namespace da aplicação.

Services/WeatherForecastService.cs no aplicativo Blazor do lado do 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());
}

No exemplo anterior, o espaço reservado {APP NAMESPACE} é o namespace da aplicação.

Os aplicativos Blazor Hybride Blazor WebAssembly, juntamente com o Blazor no lado do servidor, registam as suas implementações dos serviços de previsão do tempo (Services.WeatherForecastService) para o IWeatherForecastService.

O projeto Blazor WebAssembly também registra um HttpClient. O HttpClient registrado em um aplicativo criado a partir do modelo de projeto Blazor WebAssembly é suficiente para essa finalidade. Para obter mais informações, consulte Chame uma API web a partir de uma aplicação ASP.NET Core Blazor.

Pages/FetchData.razor na 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 adicionais