Migrowanie z ASP.NET do ASP.NET CoreMigrate from ASP.NET to ASP.NET Core

Autor Tomasz LevinBy Isaac Levin

Ten artykuł służy jako Przewodnik referencyjny dotyczący migrowania aplikacji ASP.NET do ASP.NET Core.This article serves as a reference guide for migrating ASP.NET apps to ASP.NET Core.

Wymagania wstępnePrerequisites

Zestaw .NET Core SDK 2,2 lub nowszy.NET Core SDK 2.2 or later

Platformy doceloweTarget frameworks

Projekty ASP.NET Core oferują deweloperom elastyczność określania platformy .NET Core, .NET Framework lub obu.ASP.NET Core projects offer developers the flexibility of targeting .NET Core, .NET Framework, or both. Dowiedz się, jak wybrać platformę .NET Core i .NET Framework dla aplikacji serwerowych , aby określić, która platforma docelowa jest najbardziej odpowiednia.See Choosing between .NET Core and .NET Framework for server apps to determine which target framework is most appropriate.

W przypadku .NET Framework określania wartości docelowej projekty muszą odwoływać się do poszczególnych pakietów NuGet.When targeting .NET Framework, projects need to reference individual NuGet packages.

Kierowanie programu .NET Core umożliwia eliminację wielu jawnych odwołań do pakietów, dzięki czemu ASP.NET Core.Targeting .NET Core allows you to eliminate numerous explicit package references, thanks to the ASP.NET Core metapackage. Zainstaluj Microsoft.AspNetCore.App pakiet w projekcie:Install the Microsoft.AspNetCore.App metapackage in your project:

<ItemGroup>
   <PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

W przypadku użycia pakietu z aplikacją nie są wdrażane żadne pakiety, do których odwołuje się pakiet.When the metapackage is used, no packages referenced in the metapackage are deployed with the app. Magazyn środowiska uruchomieniowego .NET Core zawiera te zasoby i są wstępnie skompilowane w celu zwiększenia wydajności.The .NET Core Runtime Store includes these assets, and they're precompiled to improve performance. Aby uzyskać więcej informacji, zobacz Microsoft. AspNetCore. App Package for ASP.NET Core .See Microsoft.AspNetCore.App metapackage for ASP.NET Core for more detail.

Różnice struktury projektuProject structure differences

Format pliku . csproj został uproszczony w ASP.NET Core.The .csproj file format has been simplified in ASP.NET Core. Niektóre istotne zmiany obejmują:Some notable changes include:

  • Jawne dołączenie plików nie jest konieczne, aby były uważane za część projektu.Explicit inclusion of files isn't necessary for them to be considered part of the project. Zmniejsza to ryzyko konfliktów scalania XML podczas pracy nad dużymi zespołami.This reduces the risk of XML merge conflicts when working on large teams.

  • Nie ma żadnych odwołań opartych na identyfikatorach GUID do innych projektów, co zwiększa czytelność plików.There are no GUID-based references to other projects, which improves file readability.

  • Plik można edytować bez zwalniania go w programie Visual Studio:The file can be edited without unloading it in Visual Studio:

    Edytuj opcję menu kontekstowego CSPROJ w programie Visual Studio 2017

Zastąpienie pliku Global. asaxGlobal.asax file replacement

ASP.NET Core wprowadzono nowy mechanizm uruchamiania aplikacji.ASP.NET Core introduced a new mechanism for bootstrapping an app. Punkt wejścia dla aplikacji ASP.NET to plik Global. asax .The entry point for ASP.NET applications is the Global.asax file. Zadania, takie jak konfiguracja tras oraz filtrowanie i rejestrowanie obszaru, są obsługiwane w pliku Global. asax .Tasks such as route configuration and filter and area registrations are handled in the Global.asax file.

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

Takie podejście Couples aplikację i serwer, na który jest wdrażana w sposób, który zakłóca implementację.This approach couples the application and the server to which it's deployed in a way that interferes with the implementation. W celu oddzielenia Owin został wprowadzony w celu zapewnienia bardziej przejrzystego sposobu używania wielu struktur.In an effort to decouple, OWIN was introduced to provide a cleaner way to use multiple frameworks together. OWIN zapewnia potok do dodawania tylko wymaganych modułów.OWIN provides a pipeline to add only the modules needed. Środowisko hostingu wykonuje funkcję uruchamiania , aby skonfigurować usługi i potok żądania aplikacji.The hosting environment takes a Startup function to configure services and the app's request pipeline. Startup rejestruje zestaw programów pośredniczących w aplikacji.Startup registers a set of middleware with the application. Dla każdego żądania aplikacja wywołuje każdy składnik pośredniczący ze wskaźnikiem głównym połączonej listy z istniejącym zestawem programów obsługi.For each request, the application calls each of the middleware components with the head pointer of a linked list to an existing set of handlers. Każdy składnik pośredniczący może dodać jeden lub więcej programów obsługi do potoku obsługi żądania.Each middleware component can add one or more handlers to the request handling pipeline. Jest to realizowane przez zwrócenie odwołania do programu obsługi, który jest nowym szefem listy.This is accomplished by returning a reference to the handler that's the new head of the list. Każdy program obsługi jest odpowiedzialny za zapamiętywanie i wywoływanie kolejnej procedury obsługi na liście.Each handler is responsible for remembering and invoking the next handler in the list. W przypadku ASP.NET Core punkt wejścia do aplikacji jest Startup i nie ma już zależności od elementu Global. asax .With ASP.NET Core, the entry point to an application is Startup, and you no longer have a dependency on Global.asax . W przypadku korzystania z programu OWIN z .NET Framework należy użyć podobnej do poniższej postaci potoku:When using OWIN with .NET Framework, use something like the following as a pipeline:

using Owin;
using System.Web.Http;

namespace WebApi
{
    // Note: By default all requests go through this OWIN pipeline. Alternatively you can turn this off by adding an appSetting owin:AutomaticAppStartup with value “false”. 
    // With this turned off you can still have OWIN apps listening on specific routes by adding routes in global.asax file using MapOwinPath or MapOwinRoute extensions on RouteTable.Routes
    public class Startup
    {
        // Invoked once at startup to configure your application.
        public void Configuration(IAppBuilder builder)
        {
            HttpConfiguration config = new HttpConfiguration();
            config.Routes.MapHttpRoute("Default", "{controller}/{customerID}", new { controller = "Customer", customerID = RouteParameter.Optional });

            config.Formatters.XmlFormatter.UseXmlSerializer = true;
            config.Formatters.Remove(config.Formatters.JsonFormatter);
            // config.Formatters.JsonFormatter.UseDataContractJsonSerializer = true;

            builder.UseWebApi(config);
        }
    }
}

Powoduje to skonfigurowanie tras domyślnych i wartości domyślnych XmlSerialization w formacie JSON.This configures your default routes, and defaults to XmlSerialization over Json. W razie potrzeby Dodaj inne oprogramowanie pośredniczące (ładowanie usług, ustawień konfiguracji, plików statycznych itp.).Add other Middleware to this pipeline as needed (loading services, configuration settings, static files, etc.).

ASP.NET Core używa podobnego podejścia, ale nie polega na OWIN do obsługi wpisu.ASP.NET Core uses a similar approach, but doesn't rely on OWIN to handle the entry. Zamiast tego robi to za pomocą metody program.cs Main (podobnie jak aplikacje konsolowe) i Startup jest ładowany w tym miejscu.Instead, that's done through the Program.cs Main method (similar to console applications) and Startup is loaded through there.

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace WebApplication2
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
}

Startup musi zawierać Configure metodę.Startup must include a Configure method. W programie Configure Dodaj wymagane oprogramowanie pośredniczące do potoku.In Configure, add the necessary middleware to the pipeline. W poniższym przykładzie (z domyślnego szablonu witryny sieci Web) metody rozszerzenia konfigurują potok z obsługą:In the following example (from the default web site template), extension methods configure the pipeline with support for:

  • Strony błędówError pages
  • Zabezpieczenia protokołu HTTP Strict TransportHTTP Strict Transport Security
  • Przekierowywanie HTTP do protokołu HTTPSHTTP redirection to HTTPS
  • ASP.NET Core MVCASP.NET Core MVC
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseMvc();
}

Host i aplikacja zostały odłączone, co zapewnia elastyczność przejścia do innej platformy w przyszłości.The host and application have been decoupled, which provides the flexibility of moving to a different platform in the future.

Uwaga

Aby uzyskać bardziej szczegółowe informacje dotyczące ASP.NET Core uruchamiania i oprogramowania pośredniczącego, zobacz Uruchamianie w ASP.NET CoreFor a more in-depth reference to ASP.NET Core Startup and Middleware, see Startup in ASP.NET Core

Konfiguracje magazynuStore configurations

ASP.NET obsługuje przechowywanie ustawień.ASP.NET supports storing settings. Te ustawienia są używane na przykład w celu obsługi środowiska, w którym aplikacje zostały wdrożone.These setting are used, for example, to support the environment to which the applications were deployed. Typowym celem jest przechowywanie wszystkich niestandardowych par klucz-wartość w <appSettings> sekcji pliku Web.config :A common practice was to store all custom key-value pairs in the <appSettings> section of the Web.config file:

<appSettings>
  <add key="UserName" value="User" />
  <add key="Password" value="Password" />
</appSettings>

Aplikacje odczytują te ustawienia przy użyciu ConfigurationManager.AppSettings kolekcji w System.Configuration przestrzeni nazw:Applications read these settings using the ConfigurationManager.AppSettings collection in the System.Configuration namespace:

string userName = System.Web.Configuration.ConfigurationManager.AppSettings["UserName"];
string password = System.Web.Configuration.ConfigurationManager.AppSettings["Password"];

ASP.NET Core może przechowywać dane konfiguracyjne dla aplikacji w dowolnym pliku i ładować je w ramach uruchamiania oprogramowania pośredniczącego.ASP.NET Core can store configuration data for the application in any file and load them as part of middleware bootstrapping. Domyślny plik używany w szablonach projektu to appsettings.json :The default file used in the project templates is appsettings.json :

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppConfiguration": {
    "UserName": "UserName",
    "Password": "Password"
  }
}

Załadowanie tego pliku do wystąpienia IConfiguration wewnątrz aplikacji odbywa się w Startup.cs :Loading this file into an instance of IConfiguration inside your application is done in Startup.cs :

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

Aplikacja odczytuje z Configuration programu, aby pobrać ustawienia:The app reads from Configuration to get the settings:

string userName = Configuration.GetSection("AppConfiguration")["UserName"];
string password = Configuration.GetSection("AppConfiguration")["Password"];

Istnieją rozszerzenia tego podejścia, aby proces był bardziej niezawodny, na przykład przy użyciu iniekcji zależności (di) do załadowania usługi z tymi wartościami.There are extensions to this approach to make the process more robust, such as using Dependency Injection (DI) to load a service with these values. Podejście DI zapewnia zestaw obiektów konfiguracji o jednoznacznie określonym typie.The DI approach provides a strongly-typed set of configuration objects.

// Assume AppConfiguration is a class representing a strongly-typed version of AppConfiguration section
services.Configure<AppConfiguration>(Configuration.GetSection("AppConfiguration"));

Uwaga

Aby uzyskać bardziej szczegółowe informacje na temat konfiguracji ASP.NET Core, zobacz Konfiguracja w ASP.NET Core.For a more in-depth reference to ASP.NET Core configuration, see Configuration in ASP.NET Core.

Natywny wtrysk zależnościNative dependency injection

Ważnym celem tworzenia dużych, skalowalnych aplikacji jest swobodne sprzęganie składników i usług.An important goal when building large, scalable applications is the loose coupling of components and services. Iniekcja zależności jest popularną techniką do osiągnięcia tego celu i jest natywnym składnikiem ASP.NET Core.Dependency Injection is a popular technique for achieving this, and it's a native component of ASP.NET Core.

W aplikacjach ASP.NET deweloperzy korzystają z biblioteki innej firmy w celu zaimplementowania iniekcji zależności.In ASP.NET apps, developers rely on a third-party library to implement Dependency Injection. Jedną z takich bibliotek jest platforma Unity, świadczona przez wzorce firmy Microsoft & praktyk.One such library is Unity, provided by Microsoft Patterns & Practices.

Przykładem konfiguracji iniekcji zależności przy użyciu aparatu Unity jest implementacja IDependencyResolver , która zawija UnityContainer :An example of setting up Dependency Injection with Unity is implementing IDependencyResolver that wraps a UnityContainer:

using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;

public class UnityResolver : IDependencyResolver
{
    protected IUnityContainer container;

    public UnityResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        container.Dispose();
    }
}

Utwórz wystąpienie obiektu UnityContainer , zarejestruj swoją usługę i Ustaw program rozpoznawania zależności na HttpConfiguration nowe wystąpienie UnityResolver dla kontenera:Create an instance of your UnityContainer, register your service, and set the dependency resolver of HttpConfiguration to the new instance of UnityResolver for your container:

public static void Register(HttpConfiguration config)
{
    var container = new UnityContainer();
    container.RegisterType<IProductRepository, ProductRepository>(new HierarchicalLifetimeManager());
    config.DependencyResolver = new UnityResolver(container);

    // Other Web API configuration not shown.
}

Wstrzyknięcie w IProductRepository razie konieczności:Inject IProductRepository where needed:

public class ProductsController : ApiController
{
    private IProductRepository _repository;

    public ProductsController(IProductRepository repository)  
    {
        _repository = repository;
    }

    // Other controller methods not shown.
}

Ponieważ iniekcja zależności jest częścią ASP.NET Core, możesz dodać swoją usługę w ConfigureServices metodzie Startup.cs :Because Dependency Injection is part of ASP.NET Core, you can add your service in the ConfigureServices method of Startup.cs :

public void ConfigureServices(IServiceCollection services)
{
    // Add application services.
    services.AddTransient<IProductRepository, ProductRepository>();
}

Repozytorium można wstrzyknąć w dowolnym miejscu, podobnie jak w przypadku aparatu Unity.The repository can be injected anywhere, as was true with Unity.

Uwaga

Aby uzyskać więcej informacji na temat iniekcji zależności, zobacz iniekcja zależności.For more information on dependency injection, see Dependency injection.

Obsługuj pliki statyczneServe static files

Ważną częścią programowania w sieci Web jest możliwość obsłużynia statycznych zasobów po stronie klienta.An important part of web development is the ability to serve static, client-side assets. Najczęstszymi przykładami plików statycznych są HTML, CSS, JavaScript i obrazy.The most common examples of static files are HTML, CSS, Javascript, and images. Te pliki muszą być zapisane w opublikowanej lokalizacji aplikacji (lub sieci CDN) i przywoływane, tak aby mogły zostać załadowane przez żądanie.These files need to be saved in the published location of the app (or CDN) and referenced so they can be loaded by a request. Ten proces został zmieniony w ASP.NET Core.This process has changed in ASP.NET Core.

W ASP.NET pliki statyczne są przechowywane w różnych katalogach i przywoływane w widokach.In ASP.NET, static files are stored in various directories and referenced in the views.

W ASP.NET Core pliki statyczne są przechowywane w "katalogu głównym sieci Web" ( < > /wwwroot zawartości ), chyba że zostały skonfigurowane inaczej.In ASP.NET Core, static files are stored in the "web root" ( <content root>/wwwroot ), unless configured otherwise. Pliki są ładowane do potoku żądania przez wywołanie UseStaticFiles metody rozszerzenia z Startup.Configure :The files are loaded into the request pipeline by invoking the UseStaticFiles extension method from Startup.Configure:

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();
}

Uwaga

Jeśli .NET Framework określania wartości docelowej, zainstaluj pakiet NuGet Microsoft.AspNetCore.StaticFiles .If targeting .NET Framework, install the NuGet package Microsoft.AspNetCore.StaticFiles.

Na przykład zasób obrazu w folderze wwwroot/images jest dostępny dla przeglądarki w lokalizacji takiej jak http://<app>/images/<imageFileName> .For example, an image asset in the wwwroot/images folder is accessible to the browser at a location such as http://<app>/images/<imageFileName>.

Uwaga

Aby uzyskać bardziej szczegółowe informacje na temat obsługi plików statycznych w ASP.NET Core, zobacz pliki statyczne.For a more in-depth reference to serving static files in ASP.NET Core, see Static files.

Wiele wartości cookie sMulti-value cookies

W ASP.NET Core nie są obsługiwane wiele wartości cookie s .Multi-value cookies aren't supported in ASP.NET Core. Utwórz jedną cookie na wartość.Create one cookie per value.

Migracja częściowej aplikacjiPartial app migration

Jednym z metod migracji części aplikacji jest utworzenie aplikacji podrzędnej IIS i przeniesienie niektórych tras z ASP.NET 4. x do ASP.NET Core przy zachowaniu struktury adresu URL aplikacji.One approach to partial app migration is to create an IIS sub-application and only move certain routes from ASP.NET 4.x to ASP.NET Core while preserving the URL structure the app. Rozważmy na przykład strukturę adresu URL aplikacji z pliku applicationHost.config :For example, consider the URL structure of the app from the applicationHost.config file:

<sites>
    <site name="Default Web Site" id="1" serverAutoStart="true">
        <application path="/">
            <virtualDirectory path="/" physicalPath="D:\sites\MainSite\" />
        </application>
        <application path="/api" applicationPool="DefaultAppPool">
            <virtualDirectory path="/" physicalPath="D:\sites\netcoreapi" />
        </application>
        <bindings>
            <binding protocol="http" bindingInformation="*:80:" />
            <binding protocol="https" bindingInformation="*:443:" sslFlags="0" />
        </bindings>
    </site>
    ...
</sites>

Struktura katalogów:Directory structure:

.
├── MainSite
│   ├── ...
│   └── Web.config
└── NetCoreApi
    ├── ...
    └── web.config

[BIND] i wejściowe elementy formatującego[BIND] and Input Formatters

Poprzednie wersje ASP.NET używały [Bind] atrybutu do ochrony przed atakami polegającymi na przeniesieniu.Previous versions of ASP.NET used the [Bind] attribute to protect against overposting attacks. Wejściowe elementy formatującego działają inaczej w ASP.NET Core.Input formatters work differently in ASP.NET Core. Ten [Bind] atrybut nie jest już zaprojektowany tak, aby zapobiec nadpisywaniu, gdy jest używany z danymi wejściowymi w celu przeanalizowania JSON lub XML.The [Bind] attribute is no longer designed to prevent overposting when used with input formatters to parse JSON or XML. Te atrybuty wpływają na powiązanie modelu, gdy źródło danych jest formularzem danych publikowanych z x-www-form-urlencoded typem zawartości.These attributes affect model binding when the source of data is form data posted with the x-www-form-urlencoded content type.

W przypadku aplikacji, które wysyłają informacje JSON do kontrolerów i używają programu formatującego dane wejściowe JSON w celu przeanalizowania danych, zalecamy zamianę [Bind] atrybutu z modelem widoku, który odpowiada właściwościom zdefiniowanym przez [Bind] atrybut.For apps that post JSON information to controllers and use JSON Input Formatters to parse the data, we recommend replacing the [Bind] attribute with a view model that matches the properties defined by the [Bind] attribute.

Dodatkowe zasobyAdditional resources