Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Wskazówka
Ta treść jest fragmentem e-booka Blazor dla deweloperów ASP NET Web Forms na platformie Azure, dostępnego w witrynie .NET Docs lub jako bezpłatny plik PDF do pobrania do czytania w trybie offline.
Migracja kodu źródłowego z ASP.NET Web Forms do Blazor jest czasochłonnym zadaniem, które wymaga odpowiedniego planowania. W tym rozdziale opisano proces. Aby ułatwić przejście, warto zadbać o to, aby aplikacja była zgodna z architekturą N-warstwową, w której model aplikacji (w tym przypadku Web Forms) jest oddzielony od logiki biznesowej. Ta logiczna separacja warstw wyjaśnia, co należy przenieść do platformy .NET Core i Blazor.
W tym przykładzie jest używana aplikacja eShop dostępna w usłudze GitHub . eShop to usługa katalogu, która udostępnia operacje CRUD za pomocą formularza i walidacji.
Dlaczego działająca aplikacja powinna zostać zmigrowana do Blazor? Wiele razy nie ma potrzeby. ASP.NET web forms będzie nadal obsługiwana przez wiele lat. Jednak wiele funkcji, które udostępnia, Blazor są obsługiwane tylko w migrowanej aplikacji. Takie funkcje obejmują:
- Ulepszenia wydajności w strukturze, takie jak
Span<T> - Możliwość uruchamiania jako WebAssembly
- Obsługa wielu platform dla systemów Linux i macOS
- Wdrażanie lokalne aplikacji lub wdrażanie platformy udostępnionej bez wpływu na inne aplikacje
Jeśli te lub inne nowe funkcje są wystarczająco atrakcyjne, może istnieć wartość migracji aplikacji. Migracja może mieć różne kształty; może to być cała aplikacja lub tylko niektóre punkty końcowe, które wymagają zmian. Decyzja o migracji jest ostatecznie oparta na problemach biznesowych, które mają zostać rozwiązane przez dewelopera.
Hosting po stronie serwera a po stronie klienta
Zgodnie z opisem w rozdziale modele hostingu aplikacja może być hostowana Blazor na dwa różne sposoby: po stronie serwera i po stronie klienta. Model po stronie serwera używa połączeń ASP.NET Core SignalR do zarządzania aktualizacjami DOM podczas uruchamiania dowolnego rzeczywistego kodu na serwerze. Model działający po stronie klienta działa w przeglądarce jako WebAssembly i nie wymaga połączeń z serwerem. Istnieje wiele różnic, które mogą mieć wpływ na najlepszą aplikację:
- Uruchamianie jako WebAssembly nie obsługuje wszystkich funkcji (takich jak wątkowanie) w bieżącej chwili
- Rozmowa między klientem a serwerem może powodować problemy z opóźnieniami w trybie po stronie serwera
- Dostęp do baz danych i usług wewnętrznych lub chronionych wymaga oddzielnej usługi z hostingiem po stronie klienta
W chwili pisania tego tekstu model po stronie serwera bardziej przypomina Web Forms. Większa część tego rozdziału została poświęcona modelowi hostowania po stronie serwera, ponieważ nadaje się on do użycia produkcyjnego.
Tworzenie nowego projektu
Ten początkowy krok migracji polega na utworzeniu nowego projektu. Ten typ projektu jest oparty na projektach w stylu zestawu SDK platformy .NET i upraszcza większość standardowych projektów używanych w poprzednich formatach projektu. Aby uzyskać więcej informacji, zobacz rozdział dotyczący struktury projektu.
Po utworzeniu projektu zainstaluj biblioteki, które były używane w poprzednim projekcie. W starszych projektach formularzy sieci Web być może użyto pliku packages.config , aby wyświetlić listę wymaganych pakietów NuGet. W nowym projekcie typu SDK plik packages.config został zastąpiony elementami <PackageReference> w pliku projektu. Zaletą tego podejścia jest to, że wszystkie zależności są instalowane przechodnio. Wystarczy wyświetlić tylko zależności najwyższego poziomu, o których ci zależy.
Wiele używanych zależności jest dostępnych dla platformy .NET, w tym dla platformy Entity Framework 6 i log4net. Jeśli nie jest dostępna żadna wersja platformy .NET lub .NET Standard, często można użyć wersji programu .NET Framework. Wyniki mogą się różnić. Każdy używany interfejs API, który nie jest dostępny na platformie .NET, powoduje błąd środowiska uruchomieniowego. Program Visual Studio powiadamia o takich pakietach. Żółta ikona pojawia się w węźle projektu Odwołania w Eksploratorze rozwiązań.
W projekcie eShop opartym na oprogramowaniu Blazormożna zobaczyć zainstalowane pakiety. Wcześniej plik packages.config wyświetlił każdy pakiet używany w projekcie, co powoduje, że plik ma prawie 50 wierszy długości. Fragment pliku packages.config:
<?xml version="1.0" encoding="utf-8"?>
<packages>
...
<package id="Microsoft.ApplicationInsights.Agent.Intercept" version="2.4.0" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.DependencyCollector" version="2.9.1" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.PerfCounterCollector" version="2.9.1" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.Web" version="2.9.1" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.WindowsServer" version="2.9.1" targetFramework="net472" />
<package id="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel" version="2.9.1" targetFramework="net472" />
<package id="Microsoft.AspNet.FriendlyUrls" version="1.0.2" targetFramework="net472" />
<package id="Microsoft.AspNet.FriendlyUrls.Core" version="1.0.2" targetFramework="net472" />
<package id="Microsoft.AspNet.ScriptManager.MSAjax" version="5.0.0" targetFramework="net472" />
<package id="Microsoft.AspNet.ScriptManager.WebForms" version="5.0.0" targetFramework="net472" />
...
<package id="System.Memory" version="4.5.1" targetFramework="net472" />
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net472" />
<package id="System.Threading.Channels" version="4.5.0" targetFramework="net472" />
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net472" />
<package id="WebGrease" version="1.6.0" targetFramework="net472" />
</packages>
Element <packages> zawiera wszystkie niezbędne zależności. Trudno jest zidentyfikować, które z tych pakietów są uwzględnione, ponieważ są one wymagane. Niektóre elementy <package> są wymienione jedynie po to, aby spełnić wymagania zależności, których potrzebujesz.
Projekt Blazor zawiera listę zależności wymaganych w elemencie <ItemGroup> w pliku projektu:
<ItemGroup>
<PackageReference Include="Autofac" Version="4.9.3" />
<PackageReference Include="EntityFramework" Version="6.4.4" />
<PackageReference Include="log4net" Version="2.0.12" />
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="2.2.12" />
</ItemGroup>
Jednym pakietem NuGet, który upraszcza życie deweloperów formularzy internetowych, jest pakiet zgodności systemu Windows. Chociaż platforma .NET jest międzyplatformowa, niektóre funkcje są dostępne tylko w systemie Windows. Funkcje specyficzne dla systemu Windows są udostępniane przez zainstalowanie pakietu zgodności. Przykładami takich składników są Rejestr, WMI i Usługi katalogowe. Pakiet dodaje około 20 000 interfejsów API i aktywuje wiele usług, które możesz już znać. Projekt eShop nie wymaga pakietu zgodności; ale jeśli projekty korzystają z funkcji specyficznych dla systemu Windows, pakiet ułatwia migrację.
Włączanie procesu uruchamiania
Proces uruchamiania Blazor zmienił się względem modelu Web Forms i wykorzystuje konfigurację podobną do tej stosowanej w innych usługach ASP.NET Core. Po stronie serwera składniki Razor są uruchamiane w ramach normalnej aplikacji ASP.NET Core. Gdy składniki Razor są hostowane w przeglądarce za pomocą WebAssembly, używają podobnego modelu hostowania. Różnica polega na tym, że składniki są uruchamiane jako oddzielna usługa od dowolnego z procesów zaplecza. Tak czy inaczej, uruchomienie jest podobne.
Plik Global.asax.cs jest domyślną stroną uruchamiania dla projektów web forms. W projekcie eShop ten plik konfiguruje kontener Inversion of Control (IoC) i obsługuje różne zdarzenia cyklu życia aplikacji lub żądania. Niektóre z tych zdarzeń są obsługiwane za pomocą oprogramowania pośredniczącego (na przykład Application_BeginRequest). Inne zdarzenia wymagają zastąpienia określonych usług za pośrednictwem wstrzykiwania zależności (DI).
Na przykład plik Global.asax.cs dla eShop zawiera następujący kod:
public class Global : HttpApplication, IContainerProviderAccessor
{
private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
static IContainerProvider _containerProvider;
IContainer container;
public IContainerProvider ContainerProvider
{
get { return _containerProvider; }
}
protected void Application_Start(object sender, EventArgs e)
{
// Code that runs on app startup
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureContainer();
ConfigDataBase();
}
/// <summary>
/// Track the machine name and the start time for the session inside the current session
/// </summary>
protected void Session_Start(Object sender, EventArgs e)
{
HttpContext.Current.Session["MachineName"] = Environment.MachineName;
HttpContext.Current.Session["SessionStartTime"] = DateTime.Now;
}
/// <summary>
/// https://autofaccn.readthedocs.io/en/latest/integration/webforms.html
/// </summary>
private void ConfigureContainer()
{
var builder = new ContainerBuilder();
var mockData = bool.Parse(ConfigurationManager.AppSettings["UseMockData"]);
builder.RegisterModule(new ApplicationModule(mockData));
container = builder.Build();
_containerProvider = new ContainerProvider(container);
}
private void ConfigDataBase()
{
var mockData = bool.Parse(ConfigurationManager.AppSettings["UseMockData"]);
if (!mockData)
{
Database.SetInitializer<CatalogDBContext>(container.Resolve<CatalogDBInitializer>());
}
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
//set the property to our new object
LogicalThreadContext.Properties["activityid"] = new ActivityIdHelper();
LogicalThreadContext.Properties["requestinfo"] = new WebRequestInfo();
_log.Debug("Application_BeginRequest");
}
}
Powyższy plik staje się plikiem Program.cs po stronie Blazorserwera:
using eShopOnBlazor.Models;
using eShopOnBlazor.Models.Infrastructure;
using eShopOnBlazor.Services;
using log4net;
using System.Data.Entity;
using eShopOnBlazor;
var builder = WebApplication.CreateBuilder(args);
// add services
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
if (builder.Configuration.GetValue<bool>("UseMockData"))
{
builder.Services.AddSingleton<ICatalogService, CatalogServiceMock>();
}
else
{
builder.Services.AddScoped<ICatalogService, CatalogService>();
builder.Services.AddScoped<IDatabaseInitializer<CatalogDBContext>, CatalogDBInitializer>();
builder.Services.AddSingleton<CatalogItemHiLoGenerator>();
builder.Services.AddScoped(_ => new CatalogDBContext(builder.Configuration.GetConnectionString("CatalogDBContext")));
}
var app = builder.Build();
new LoggerFactory().AddLog4Net("log4Net.xml");
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
// Middleware for Application_BeginRequest
app.Use((ctx, next) =>
{
LogicalThreadContext.Properties["activityid"] = new ActivityIdHelper(ctx);
LogicalThreadContext.Properties["requestinfo"] = new WebRequestInfo(ctx);
return next();
});
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
ConfigDataBase(app);
void ConfigDataBase(IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.CreateScope())
{
var initializer = scope.ServiceProvider.GetService<IDatabaseInitializer<CatalogDBContext>>();
if (initializer != null)
{
Database.SetInitializer(initializer);
}
}
}
app.Run();
Jedną z znaczących zmian, które można zauważyć w formularzach sieci Web, jest znaczenie wstrzykiwania zależności (DI). Di jest zasadą przewodnią w projekcie ASP.NET Core. Obsługuje dostosowywanie prawie wszystkich aspektów platformy ASP.NET Core. Istnieje nawet wbudowany dostawca usług, który może być używany w wielu scenariuszach. Jeśli potrzebne są większe możliwości dostosowania, mogą je zapewnić liczne projekty społecznościowe. Możesz na przykład zachować swoją dotychczasową inwestycję w zewnętrzną bibliotekę DI.
W oryginalnej aplikacji eShop istnieje pewna konfiguracja zarządzania sesjami. Ponieważ element Blazor po stronie serwera używa platformy ASP.NET Core SignalR do komunikacji, stan sesji nie jest obsługiwany, ponieważ połączenia mogą być nawiązywane niezależnie od kontekstu HTTP. Aplikacja korzystająca ze stanu sesji wymaga przeprojektowania architektury przed jej uruchomieniem jako aplikacja Blazor.
Aby uzyskać więcej informacji na temat uruchamiania aplikacji, zobacz Uruchamianie aplikacji.
Migrowanie modułów HTTP i programów obsługi do oprogramowania pośredniczącego
Moduły HTTP i procedury obsługi są typowymi wzorcami w technologii Web Forms, służącymi do kontrolowania potoku przetwarzania żądań HTTP. Klasy, które implementują IHttpModule lub IHttpHandler mogą być zarejestrowane i przetwarzają żądania przychodzące. Web Forms konfiguruje moduły i programy obsługi w pliku web.config . Formularze internetowe są również silnie oparte na obsłudze zdarzeń cyklu życia aplikacji. ASP.NET Core używa zamiast tego oprogramowania pośredniczącego. Oprogramowanie pośrednie jest rejestrowane w metodzie Configure klasy Startup. Kolejność wykonywania middleware zależy od kolejności rejestracji.
W sekcji Włączanie procesu uruchamiania zdarzenie cyklu życia zostało wywołane przez Web Forms jako metoda Application_BeginRequest. To zdarzenie nie jest dostępne w programie ASP.NET Core. Jednym ze sposobów osiągnięcia tego zachowania jest zaimplementowanie oprogramowania pośredniczącego, jak pokazano w przykładzie pliku Startup.cs . To oprogramowanie pośredniczące realizuje tę samą logikę, a następnie przekazuje sterowanie do następnego modułu obsługi w potoku oprogramowania pośredniczącego.
Aby uzyskać więcej informacji na temat migrowania modułów i procedur obsługi, zobacz Migrowanie programów obsługi HTTP i modułów do oprogramowania pośredniczącego ASP.NET Core.
Migrowanie plików statycznych
Aby obsługiwać pliki statyczne (na przykład HTML, CSS, obrazy i JavaScript), pliki muszą być udostępniane przez oprogramowanie pośredniczące.
UseStaticFiles Wywołanie metody umożliwia obsługę plików statycznych ze ścieżki głównej sieci Web. Domyślny katalog główny sieci Web to wwwroot, ale można go dostosować. Zgodnie z plikiem Program.cs :
...
app.UseStaticFiles();
...
Projekt eShop umożliwia podstawowy dostęp do plików statycznych. Istnieje wiele dostosowań dostępnych na potrzeby dostępu do plików statycznych. Aby uzyskać informacje na temat włączania plików domyślnych lub przeglądarki plików, zobacz Pliki statyczne w programie ASP.NET Core.
Migrowanie konfiguracji pakietów i minification środowiska uruchomieniowego
Tworzenie pakietów i minimalizowanie to techniki optymalizacji wydajności w celu zmniejszenia liczby i rozmiaru żądań serwera w celu pobrania niektórych typów plików. Języki JavaScript i CSS często przechodzą jakąś formę tworzenia pakietów lub minifikacji przed wysłaniem do klienta. W ASP.NET web forms te optymalizacje są obsługiwane w czasie wykonywania. Konwencje optymalizacji są definiowane w pliku App_Start/BundleConfig.cs . W ASP.NET Core przyjęto bardziej deklaratywne podejście. Plik zawiera listę plików, które mają być zminimalizowane, wraz z określonymi ustawieniami minyfikacji.
Aby uzyskać więcej informacji na temat tworzenia pakietów i minimalizacji, zobacz artykuł Łączenie i minimalizowanie zasobów statycznych w usłudze ASP.NET Core.
Migrowanie stron ASPX
Strona w aplikacji Web Forms to plik z rozszerzeniem .aspx . Strona formularzy sieci Web może być często mapowana na składnik w programie Blazor. Składnik Razor jest tworzony w pliku z rozszerzeniem .razor. W przypadku projektu eShop pięć stron jest konwertowanych na stronę Razor.
Na przykład widok szczegółów składa się z trzech plików w projekcie Web Forms: Details.aspx, Details.aspx.cs i Details.aspx.designer.cs. Podczas konwertowania na element Blazor kod zaplecza i kod znaczników zostają połączone w pliku Details.razor. Kompilacja Razor (równoważna zawartości w plikach .designer.cs) jest przechowywana w katalogu obj i nie jest domyślnie widoczna w Eksplorator rozwiązań. Strona Web Forms składa się z następującego kodu znaczników:
<%@ Page Title="Details" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Details.aspx.cs" Inherits="eShopLegacyWebForms.Catalog.Details" %>
<asp:Content ID="Details" ContentPlaceHolderID="MainContent" runat="server">
<h2 class="esh-body-title">Details</h2>
<div class="container">
<div class="row">
<asp:Image runat="server" CssClass="col-md-6 esh-picture" ImageUrl='<%#"/Pics/" + product.PictureFileName%>' />
<dl class="col-md-6 dl-horizontal">
<dt>Name
</dt>
<dd>
<asp:Label runat="server" Text='<%#product.Name%>' />
</dd>
<dt>Description
</dt>
<dd>
<asp:Label runat="server" Text='<%#product.Description%>' />
</dd>
<dt>Brand
</dt>
<dd>
<asp:Label runat="server" Text='<%#product.CatalogBrand.Brand%>' />
</dd>
<dt>Type
</dt>
<dd>
<asp:Label runat="server" Text='<%#product.CatalogType.Type%>' />
</dd>
<dt>Price
</dt>
<dd>
<asp:Label CssClass="esh-price" runat="server" Text='<%#product.Price%>' />
</dd>
<dt>Picture name
</dt>
<dd>
<asp:Label runat="server" Text='<%#product.PictureFileName%>' />
</dd>
<dt>Stock
</dt>
<dd>
<asp:Label runat="server" Text='<%#product.AvailableStock%>' />
</dd>
<dt>Restock
</dt>
<dd>
<asp:Label runat="server" Text='<%#product.RestockThreshold%>' />
</dd>
<dt>Max stock
</dt>
<dd>
<asp:Label runat="server" Text='<%#product.MaxStockThreshold%>' />
</dd>
</dl>
</div>
<div class="form-actions no-color esh-link-list">
<a runat="server" href='<%# GetRouteUrl("EditProductRoute", new {id =product.Id}) %>' class="esh-link-item">Edit
</a>
|
<a runat="server" href="~" class="esh-link-item">Back to list
</a>
</div>
</div>
</asp:Content>
Powyższy kod znaczników zawiera następujący kod:
using eShopLegacyWebForms.Models;
using eShopLegacyWebForms.Services;
using log4net;
using System;
using System.Web.UI;
namespace eShopLegacyWebForms.Catalog
{
public partial class Details : System.Web.UI.Page
{
private static readonly ILog _log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected CatalogItem product;
public ICatalogService CatalogService { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
var productId = Convert.ToInt32(Page.RouteData.Values["id"]);
_log.Info($"Now loading... /Catalog/Details.aspx?id={productId}");
product = CatalogService.FindCatalogItem(productId);
this.DataBind();
}
}
}
Po przekonwertowaniu do Blazor strona Web Forms ma następującą postać:
@page "/Catalog/Details/{id:int}"
@inject ICatalogService CatalogService
@inject ILogger<Details> Logger
<h2 class="esh-body-title">Details</h2>
<div class="container">
<div class="row">
<img class="col-md-6 esh-picture" src="@($"/Pics/{_item.PictureFileName}")">
<dl class="col-md-6 dl-horizontal">
<dt>
Name
</dt>
<dd>
@_item.Name
</dd>
<dt>
Description
</dt>
<dd>
@_item.Description
</dd>
<dt>
Brand
</dt>
<dd>
@_item.CatalogBrand.Brand
</dd>
<dt>
Type
</dt>
<dd>
@_item.CatalogType.Type
</dd>
<dt>
Price
</dt>
<dd>
@_item.Price
</dd>
<dt>
Picture name
</dt>
<dd>
@_item.PictureFileName
</dd>
<dt>
Stock
</dt>
<dd>
@_item.AvailableStock
</dd>
<dt>
Restock
</dt>
<dd>
@_item.RestockThreshold
</dd>
<dt>
Max stock
</dt>
<dd>
@_item.MaxStockThreshold
</dd>
</dl>
</div>
<div class="form-actions no-color esh-link-list">
<a href="@($"/Catalog/Edit/{_item.Id}")" class="esh-link-item">
Edit
</a>
|
<a href="/" class="esh-link-item">
Back to list
</a>
</div>
</div>
@code {
private CatalogItem _item;
[Parameter]
public int Id { get; set; }
protected override void OnInitialized()
{
Logger.LogInformation("Now loading... /Catalog/Details/{Id}", Id);
_item = CatalogService.FindCatalogItem(Id);
}
}
Zwróć uwagę, że kod i znaczniki znajdują się w tym samym pliku. Wszystkie wymagane usługi są dostępne za pomocą atrybutu @inject . Zgodnie z dyrektywą @page dostęp do tej strony można uzyskać na Catalog/Details/{id} trasie. Wartość symbolu zastępczego trasy {id} została ograniczona do liczby całkowitej. Zgodnie z opisem w sekcji routingu, w przeciwieństwie do formularzy internetowych, składnik Razor jawnie określa trasę i wszystkie dołączone parametry. Wiele kontrolek formularzy sieci Web może nie mieć dokładnych odpowiedników w programie Blazor. Często istnieje równoważny fragment kodu HTML, który będzie służył w tym samym celu. Na przykład kontrolkę <asp:Label /> można zastąpić elementem HTML <label> .
Walidacja modelu w programie Blazor
Jeśli Twój kod Web Forms obejmuje walidację, możesz przenieść znaczną część tego, co już masz, wprowadzając niewielkie zmiany lub nie wprowadzając ich wcale. Zaletą uruchamiania w programie Blazor jest to, że można uruchomić tę samą logikę walidacji bez konieczności używania niestandardowego kodu JavaScript. Adnotacje danych umożliwiają łatwą walidację modelu.
Na przykład strona Create.aspx zawiera formularz wprowadzania danych z walidacją. Przykładowy fragment kodu wygląda następująco:
<div class="form-group">
<label class="control-label col-md-2">Name</label>
<div class="col-md-3">
<asp:TextBox ID="Name" runat="server" CssClass="form-control"></asp:TextBox>
<asp:RequiredFieldValidator runat="server" ControlToValidate="Name" Display="Dynamic"
CssClass="field-validation-valid text-danger" ErrorMessage="The Name field is required." />
</div>
</div>
W Blazor równoważny znacznik jest dostępny w pliku Create.razor:
<EditForm Model="_item" OnValidSubmit="@...">
<DataAnnotationsValidator />
<div class="form-group">
<label class="control-label col-md-2">Name</label>
<div class="col-md-3">
<InputText class="form-control" @bind-Value="_item.Name" />
<ValidationMessage For="(() => _item.Name)" />
</div>
</div>
...
</EditForm>
Kontekst EditForm obejmuje obsługę walidacji i może być opakowany wokół danych wejściowych. Adnotacje danych są typowym sposobem dodawania walidacji. Taką obsługę walidacji można dodać za pośrednictwem DataAnnotationsValidator składnika. Aby uzyskać więcej informacji na temat tego mechanizmu, zobacz ASP.NET Core Blazor formularze i walidacja.
Migrowanie konfiguracji
W projekcie web forms dane konfiguracji są najczęściej przechowywane w pliku web.config . Dostęp do danych konfiguracji jest uzyskiwany za pomocą polecenia ConfigurationManager. Usługi były często wymagane do analizowania obiektów. W .NET Framework 4.7.2 do konfiguracji dodano kompozycyjność za pośrednictwem ConfigurationBuilders. Te mechanizmy umożliwiały programistom dodawanie różnych źródeł konfiguracji, z których następnie w czasie wykonywania tworzono konfigurację, aby pobrać niezbędne wartości.
ASP.NET Core wprowadził elastyczny system konfiguracji, który umożliwia zdefiniowanie źródła konfiguracji lub źródeł używanych przez aplikację i wdrożenie. Infrastruktura ConfigurationBuilder , która może być używana w aplikacji Web Forms, została wymodelowana po pojęciach używanych w systemie konfiguracji ASP.NET Core.
Poniższy fragment kodu pokazuje, jak projekt Web Forms eShop używa pliku web.config do przechowywania wartości konfiguracji:
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<connectionStrings>
<add name="CatalogDBContext" connectionString="Data Source=(localdb)\MSSQLLocalDB; Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb; Integrated Security=True; MultipleActiveResultSets=True;" providerName="System.Data.SqlClient" />
</connectionStrings>
<appSettings>
<add key="UseMockData" value="true" />
<add key="UseCustomizationData" value="false" />
</appSettings>
</configuration>
Często wpisy tajne, takie jak parametry połączenia bazy danych, są przechowywane w pliku web.config. Wpisy tajne są nieuchronnie utrwalane w niezabezpieczonych lokalizacjach, takich jak kontrola źródła. W Blazor przypadku ASP.NET Core poprzednia konfiguracja oparta na formacie XML jest zastępowana następującym kodem JSON:
{
"ConnectionStrings": {
"CatalogDBContext": "Data Source=(localdb)\\MSSQLLocalDB; Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb; Integrated Security=True; MultipleActiveResultSets=True;"
},
"UseMockData": true,
"UseCustomizationData": false
}
Format JSON jest domyślnym formatem konfiguracji; jednak ASP.NET Core obsługuje wiele innych formatów, w tym XML. Istnieje również kilka formatów obsługiwanych przez społeczność.
Dostęp do wartości konfiguracji można uzyskać z poziomu konstruktora w Program.cs:
if (builder.Configuration.GetValue<bool>("UseMockData"))
{
builder.Services.AddSingleton<ICatalogService, CatalogServiceMock>();
}
else
{
builder.Services.AddScoped<ICatalogService, CatalogService>();
builder.Services.AddScoped<IDatabaseInitializer<CatalogDBContext>, CatalogDBInitializer>();
builder.Services.AddSingleton<CatalogItemHiLoGenerator>();
builder.Services.AddScoped(_ => new CatalogDBContext(builder.Configuration.GetConnectionString("CatalogDBContext")));
}
Domyślnie zmienne środowiskowe, pliki JSON (appsettings.json i appsettings.{ Środowisko}.json) i opcje wiersza polecenia są rejestrowane jako prawidłowe źródła konfiguracji w obiekcie konfiguracji. Dostęp do źródeł konfiguracji można uzyskać za pośrednictwem programu Configuration[key]. Bardziej zaawansowaną techniką jest powiązanie danych konfiguracji z obiektami przy użyciu wzorca opcji. Aby uzyskać więcej informacji na temat konfiguracji i wzorca opcji, zobacz Configuration in ASP.NET Core and Options pattern in ASP.NET Core ,, odpowiednio.
Migrowanie dostępu do danych
Dostęp do danych jest ważnym aspektem każdej aplikacji. Projekt eShop przechowuje informacje o katalogu w bazie danych i pobiera dane za pomocą programu Entity Framework (EF) 6. Ponieważ program EF 6 jest obsługiwany na platformie .NET 5, projekt może nadal go używać.
Następujące zmiany związane z EF były niezbędne w eShop:
- W środowisku .NET Framework obiekt
DbContextprzyjmuje ciąg znaków w postaci name=ConnectionString i używa parametrów połączenia zConfigurationManager.AppSettings[ConnectionString], aby się połączyć. W programie .NET Core nie jest to obsługiwane. Należy podać parametry połączenia. - Dostęp do bazy danych był uzyskiwany w sposób synchroniczny. Chociaż to działa, skalowalność może cierpieć. Ta logika powinna zostać przeniesiona do wzorca asynchronicznego.
Chociaż nie ma tej samej natywnej obsługi powiązania zestawu danych, Blazor zapewnia elastyczność i możliwości dzięki obsłudze języka C# na stronie Razor. Na przykład można wykonywać obliczenia i wyświetlać wynik. Aby uzyskać więcej informacji na temat wzorców danych w programie Blazor, zobacz rozdział Dostęp do danych.
Zmiany w architekturze
Na koniec należy wziąć pod uwagę pewne istotne różnice architektoniczne podczas migracji do Blazor. Wiele z tych zmian ma zastosowanie do dowolnych elementów opartych na platformie .NET Core lub ASP.NET Core.
Ponieważ Blazor jest oparta na platformie .NET Core, należy wziąć pod uwagę zapewnienie obsługi platformy .NET Core. Niektóre z głównych zmian obejmują usunięcie następujących funkcji:
- Wiele domen aplikacji
- Zdalny dostęp
- zabezpieczenia dostępu do kodu (CAS)
- Przezroczystość zabezpieczeń
Aby uzyskać więcej informacji na temat technik identyfikowania niezbędnych zmian do obsługi uruchamiania w programie .NET Core, zobacz Przenoszenie kodu z platformy .NET Framework do .NET.
ASP.NET Core to przeimaginowana wersja ASP.NET i ma pewne zmiany, które początkowo nie wydają się oczywiste. Główne zmiany to:
- Brak kontekstu synchronizacji, co oznacza, że nie ma
HttpContext.Current,Thread.CurrentPrincipalani innych statycznych akcesorów - Brak kopiowania w tle
- Brak kolejki żądań
Wiele operacji w ASP.NET Core jest asynchronicznych, co ułatwia odciążanie zadań związanych z operacjami wejścia/wyjścia. Ważne jest, aby nigdy nie blokować przy użyciu funkcji Task.Wait() lub Task.GetResult(), co może szybko wyczerpać zasoby puli wątków.
Zakończenie migracji
Do tej pory widziałeś wiele przykładów pokazujących, czego potrzeba, aby przenieść projekt Web Forms do Blazor. Pełny przykład można znaleźć w projekcie eShopOnBlazor .