Condividi tramite


Condividere gli asset tra client Web e nativi usando una Razor libreria di classi (RCL)

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere i criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 9 di questo articolo.

Usare una Razor libreria di classi (RCL) per condividere Razor componenti, codice C# e asset statici tra progetti client Web e nativi.

Questo articolo si basa sui concetti generali disponibili negli articoli seguenti:

Gli esempi in questo articolo condividono gli asset tra un'app lato Blazor server e un'app .NET MAUIBlazor Hybrid nella stessa soluzione:

  • Anche se viene usata un'app sul lato Blazor server, le linee guida si applicano allo stesso modo alle app che Blazor WebAssembly condividono asset con un'app Blazor Hybrid .
  • I progetti si trovano nella stessa soluzione, ma un RCL può fornire asset condivisi ai progetti all'esterno di una soluzione.
  • L'RCL viene aggiunto come progetto alla soluzione, ma qualsiasi RCL può essere pubblicato come pacchetto NuGet. Un pacchetto NuGet può fornire asset condivisi a progetti client web e nativi.
  • L'ordine in cui vengono creati i progetti non è importante. Tuttavia, i progetti che si basano su un RCL per gli asset devono creare un riferimento al progetto all'RCL dopo la creazione dell'RCL.

Per indicazioni sulla creazione di un rcl, vedere Utilizzare i componenti di Razor base ASP.NET da una Razor libreria di classi (RCL). Facoltativamente, accedere alle indicazioni aggiuntive sulle DLL che si applicano su larga scala alle app core ASP.NET nell'interfaccia utente riutilizzabile Razor nelle librerie di classi con ASP.NET Core.

Framework di destinazione per le distribuzioni ClickOnce

Per pubblicare un progetto WPF o Windows Form con una Razor libreria di classi (RCL) in .NET 6 con ClickOnce, l'RCL deve essere destinato net6.0-windows oltre a net6.0.

Esempio:

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

Per altre informazioni, vedere gli articoli seguenti:

App di esempio

Per un esempio degli scenari descritti in questo articolo, vedere l'applicazione di riferimento eShop (AdventureWorks) (dotnet/eShop repository GitHub). L'app .NET MAUIBlazor Hybrid si trova nella cartella src/HybridApp.

Per una versione dell'app di esempio personalizzata per l'hosting di Azure, vedere il repository GitHub Azure-Samples/eShopOnAzure.

L'app di esempio presenta le tecnologie seguenti:

Condividere componenti, codice e asset statici dell'interfaccia utente Razor Web

I componenti di un RCL possono essere condivisi contemporaneamente dalle app Web e native client compilate usando Blazor. Le linee guida in Utilizzare componenti di Razor base di ASP.NET da una libreria di Razor classi (RCL) illustrano come condividere Razor i componenti usando una Razor libreria di classi (RCL). Le stesse indicazioni si applicano al riutilizzo dei Razor componenti da un RCL in un'app Blazor Hybrid .

Gli spazi dei nomi dei componenti derivano dall'ID pacchetto o dal nome dell'assembly rcl e dal percorso della cartella del componente all'interno dell'RCL. Per altre informazioni, vedere Razor di base. @usingLe direttive possono essere inserite nei _Imports.razor file per componenti e codice, come illustrato nell'esempio seguente per un RCL denominato SharedLibrary con una Shared cartella di componenti condivisi e una Razor cartella di classi di dati condiviseData:

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

Posizionare gli asset statici condivisi nella cartella rcl wwwroot e aggiornare i percorsi degli asset statici nell'app per usare il formato di percorso seguente:

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

Segnaposto:

  • {PACKAGE ID/ASSEMBLY NAME}: ID pacchetto o nome dell'assembly dell'RCL.
  • {PATH}: percorso facoltativo all'interno della cartella di wwwroot RCL.
  • {FILE NAME}: nome file dell'asset statico.

Il formato del percorso precedente viene usato anche nell'app per gli asset statici forniti da un pacchetto NuGet aggiunto alla libreria RCL.

Per un RCL denominato SharedLibrary e usando il foglio di stile Bootstrap minimizzato come esempio:

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

Per altre informazioni su come condividere asset statici tra progetti, vedere gli articoli seguenti:

Il file radice index.html è in genere specifico dell'app e deve rimanere nell'app Blazor Hybrid o nell'app Blazor WebAssembly . Il index.html file in genere non è condiviso.

Il componente radice Razor (App.razor o Main.razor) può essere condiviso, ma spesso potrebbe essere necessario essere specifico per l'app di hosting. Ad esempio, App.razor è diverso nei modelli di progetto e sul lato BlazorBlazor WebAssembly server quando l'autenticazione è abilitata. È possibile aggiungere il AdditionalAssemblies parametro per specificare la posizione di tutti i componenti instradabili condivisi ed è possibile specificare un componente di layout predefinito condiviso per il router in base al nome del tipo.

Fornire codice e servizi indipendenti dal modello di hosting

Quando il codice deve differire tra i modelli di hosting o le piattaforme di destinazione, astrarre il codice come interfacce e inserire le implementazioni del servizio in ogni progetto.

L'esempio di dati meteo seguente astrae diverse implementazioni del servizio previsioni meteo:

  • Uso di una richiesta HTTP per Blazor Hybrid e Blazor WebAssembly.
  • Richiesta diretta di dati per un'app lato Blazor server.

Nell'esempio vengono usate le specifiche e le convenzioni seguenti:

  • L'RCL è denominato SharedLibrary e contiene le cartelle e gli spazi dei nomi seguenti:
    • Data: contiene la WeatherForecast classe , che funge da modello per i dati meteo.
    • Interfaces: contiene l'interfaccia del servizio per le implementazioni del servizio, denominata IWeatherForecastService.
  • Il FetchData componente viene mantenuto nella Pages cartella dell'RCL, che è instradabile da una qualsiasi delle app che usano l'RCL.
  • Ogni Blazor app gestisce un'implementazione del servizio che implementa l'interfaccia IWeatherForecastService .

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

using SharedLibrary.Data;

namespace SharedLibrary.Interfaces;

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

Il _Imports.razor file nell'RCL include gli spazi dei nomi aggiunti seguenti:

@using SharedLibrary.Data
@using SharedLibrary.Interfaces

Services/WeatherForecastService.cs Blazor Hybrid nelle app 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");
}

Nell'esempio precedente il {APP NAMESPACE} segnaposto è lo spazio dei nomi dell'app.

Services/WeatherForecastService.cs nell'app lato Blazor server:

using SharedLibrary.Data;
using SharedLibrary.Interfaces;

namespace {APP NAMESPACE}.Services;

public class WeatherForecastService : IWeatherForecastService
{
    private static readonly string[] Summaries = 
    [
        "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());
}

Nell'esempio precedente il {APP NAMESPACE} segnaposto è lo spazio dei nomi dell'app.

Le Blazor Hybridapp lato server Blazor WebAssembly , Blazore registrano le implementazioni del servizio previsioni meteo (Services.WeatherForecastService) per IWeatherForecastService.

Il Blazor WebAssembly progetto registra anche un oggetto HttpClient. Il HttpClient record registrato in un'app creata dal modello di Blazor WebAssembly progetto è sufficiente a questo scopo. Per altre informazioni, vedere Blazor ASP.NET Core.

Pages/FetchData.razor nell'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);
    }
}

Risorse aggiuntive