Razor クラス ライブラリ (RCL) を使用して Web クライアントとネイティブ クライアント全体で資産を共有する

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

Razor クラス ライブラリ (RCL) を使用して、Web およびネイティブ クライアント プロジェクト全体で、Razor コンポーネント、C# コード、静的資産を共有する方法について説明します。

この記事は、次の記事にある一般的な概念に基づいています。

この記事の例では、同じソリューション内のサーバー側 Blazor アプリと .NET MAUIBlazor Hybrid アプリ間で資産を共有しています。

  • サーバー側 Blazor アプリが使われていますが、ガイダンスは Blazor Hybrid アプリと資産を共有する Blazor WebAssembly アプリにも同様に適用されます。
  • プロジェクトは同じソリューション内にありますが、RCL によりソリューションの外部のプロジェクトに共有資産を提供できます。
  • RCL はプロジェクトとしてソリューションに追加されますが、どの RCL も NuGet パッケージとして公開できます。 NuGet パッケージにより、Web およびネイティブ クライアント プロジェクトに共有資産を提供できます。
  • プロジェクトが作成される順序は重要ではありません。 ただし、資産を RCL に依存するプロジェクトでは、RCL の作成に RCL へのプロジェクト参照を作成する必要があります。

RCL の作成のガイダンスについては、「ASP.NET Core Razor コンポーネントを Razor クラス ライブラリ (RCL) から使用する」を参照してください。 必要に応じて、「ASP.NET Core のクラス ライブラリの再利用可能 Razor UI」の ASP.NET Core アプリに広く適用される RCL に関する追加のガイダンスにアクセスしてください。

ClickOnce デプロイ用のターゲット フレームワーク

ClickOnce によって、.NET 6 の Razor クラス ライブラリ (RCL) を使用して WPF または Windows フォーム プロジェクトを公開するには、RCL で net6.0 に加えて net6.0-windows をターゲットにする必要があります。

例:

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

詳細については、次の記事を参照してください。

サンプル アプリ

この記事で説明されているシナリオの例については、.NET ポッドキャスト サンプル アプリを参照してください。

.NET ポッドキャスト アプリでは、次のテクノロジが紹介されています。

Web UI Razor コンポーネント、コード、静的資産を共有する

RCL のコンポーネントは、Blazor を使用してビルドされた Web アプリとネイティブ クライアント アプリで同時に共有できます。 「ASP.NET Core Razor コンポーネントを Razor クラス ライブラリ (RCL) から 使用する」のガイダンスでは、Razor クラス ライブラリ (RCL) を使用して Razor コンポーネントを共有する方法について説明します。 同じガイダンスは、Blazor Hybrid アプリで RCL からの Razor コンポーネントを再利用する場合にも適用されます。

コンポーネント名前空間は、RCL のパッケージ ID またはアセンブリ名と、RCL 内のコンポーネントのフォルダー パスから派生します。 詳細については、「ASP.NET Core Razor コンポーネント」を参照してください。 共有 Razor コンポーネントの Shared フォルダーと共有データ クラスの Data フォルダーを使用した SharedLibrary という RCL について示している次の例のように、@using ディレクティブは、コンポーネントとコード用の _Imports.razor ファイルに配置できます。

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

RCL の wwwroot フォルダーに共有静的資産を配置し、アプリの静的資産パスを更新して、次のパス形式を使用します。

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

プレースホルダー:

  • {PACKAGE ID/ASSEMBLY NAME}: RCL のパッケージ ID またはアセンブリ名。
  • {PATH}: RCL の wwwroot フォルダー内の省略可能なパス。
  • {FILE NAME}: 静的資産のファイル名。

上記のパス形式はアプリで、RCL に追加された NuGet パッケージによって提供される静的資産についても使用します。

例として SharedLibrary という名前の、縮小されたブートストラップ スタイルシートを使用した RCL の場合:

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

プロジェクト間で静的資産を共有する方法の詳細については、次の記事を参照してください。

ルート index.html ファイルは通常、アプリに固有であり、Blazor Hybrid アプリまたは Blazor WebAssembly アプリに残す必要があります。 index.html ファイルは通常、共有されません。

ルート Razor コンポーネント (App.razor または Main.razor) は共有できますが、多くの場合に、ホスティング アプリに固有にする必要がある可能性があります。 たとえば、認証が有効になっている場合、サーバー側 Blazor プロジェクト テンプレートと Blazor WebAssembly プロジェクト テンプレートで、App.razor が異なるとします。 AdditionalAssemblies パラメーターを追加して、ルーティング可能な共有コンポーネントの場所を指定し、種類名でルーターの既定の共有レイアウト コンポーネントを指定できます。

ホスティング モデルに依存しないコードとサービスを提供する

ホスティング モデルやターゲット プラットフォーム間でコードを異なるものにする必要がある場合は、コードをインターフェイスとして抽象化して、サービス実装を各プロジェクトに挿入します。

次の気象データの例では、さまざまな天気予報サービスの実装を抽象化しています。

  • Blazor Hybrid と Blazor WebAssembly の HTTP 要求を使用する。
  • サーバー側 Blazor アプリのデータを直接要求する。

例では、次の仕様と規則を使用しています。

  • RCL には SharedLibrary という名前が付けられ、次のフォルダーと名前空間が含まれます。
    • Data: 気象データのモデルとして機能する WeatherForecast クラスが含まれます。
    • Interfaces: IWeatherForecastService という名前の、サービス実装のサービス インターフェイスが含まれます。
  • FetchData コンポーネントは RCL の Pages フォルダーに保持され、RCL を使用するすべてのアプリでルーティング可能です。
  • 各 Blazor アプリは、IWeatherForecastService インターフェイスを実装するサービス実装を保持します。

RCL の Data/WeatherForecast.cs:

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; }
}

RCL の Interfaces/IWeatherForecastService.cs:

using SharedLibrary.Data;

namespace SharedLibrary.Interfaces;

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

RCL の_Imports.razor ファイルには、次の追加された名前空間が含まれています。

@using SharedLibrary.Data
@using SharedLibrary.Interfaces

Blazor Hybrid アプリと Blazor WebAssembly アプリでの Services/WeatherForecastService.cs:

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");
}

前の例で、{APP NAMESPACE} プレースホルダーはアプリの名前空間です。

サーバー側 Blazor アプリの Services/WeatherForecastService.cs:

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());
}

前の例で、{APP NAMESPACE} プレースホルダーはアプリの名前空間です。

Blazor Hybrid、Blazor WebAssembly、およびサーバー側 Blazor アプリでは、IWeatherForecastService の天気予報サービス実装 (Services.WeatherForecastService) を登録します。

Blazor WebAssembly プロジェクトでは、HttpClient も登録します。 Blazor WebAssembly プロジェクト テンプレートから作成されたアプリでは、既定で登録された HttpClient で、この目的には十分です。 詳しくは、「ASP.NET Core Blazor アプリから Web API を呼び出す」をご覧ください。

RCL の Pages/FetchData.razor:

@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);
    }
}

その他のリソース