Миграция с ASP.NET на ASP.NET CoreMigrate from ASP.NET to ASP.NET Core

Автор Айзек Левин (Isaac Levin)By Isaac Levin

Данная статья служит руководством по миграции приложений ASP.NET на ASP.NET Core.This article serves as a reference guide for migrating ASP.NET apps to ASP.NET Core.

Предварительные требованияPrerequisites

Пакет SDK для .NET Core 2.2 или более поздней версии.NET Core SDK 2.2 or later

Требуемые версии .NET FrameworkTarget frameworks

Проекты ASP.NET Core предлагают разработчикам гибкость работы с .NET Core и .NET Framework.ASP.NET Core projects offer developers the flexibility of targeting .NET Core, .NET Framework, or both. Определить наиболее подходящую платформу поможет статья Выбор между .NET Core и .NET Framework для серверных приложений.See Choosing between .NET Core and .NET Framework for server apps to determine which target framework is most appropriate.

При разработке для .NET Framework проекты должны ссылаться на отдельные пакеты NuGet.When targeting .NET Framework, projects need to reference individual NuGet packages.

Работа с .NET Core позволяет избавиться от многочисленных явных ссылок на пакеты благодаря метапакету ASP.NET Core.Targeting .NET Core allows you to eliminate numerous explicit package references, thanks to the ASP.NET Core metapackage. Установите метапакет Microsoft.AspNetCore.App в свой проект.Install the Microsoft.AspNetCore.App metapackage in your project:

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

При использовании метапакета никакие указанные по ссылкам пакеты с приложением не развертываются.When the metapackage is used, no packages referenced in the metapackage are deployed with the app. Все эти ресурсы входят в хранилище среды выполнения .NET Core и предварительно компилируются для повышения производительности.The .NET Core Runtime Store includes these assets, and they're precompiled to improve performance. Дополнительные сведения см. в статье Метапакет Microsoft.AspNetCore.App для ASP.NET Core 2.1.See Microsoft.AspNetCore.App metapackage for ASP.NET Core for more detail.

Различия в структуре пакетовProject structure differences

В ASP.NET Core упрощен формат файла .csproj.The .csproj file format has been simplified in ASP.NET Core. Вот некоторые основные изменения.Some notable changes include:

  • Чтобы файлы считались частью проекта, включать их явно теперь не требуется.Explicit inclusion of files isn't necessary for them to be considered part of the project. Это уменьшает вероятность конфликтов слияния XML при работе в больших командах.This reduces the risk of XML merge conflicts when working on large teams.

  • GUID-ссылки на другие проекты не используются, что повышает удобочитаемость файла.There are no GUID-based references to other projects, which improves file readability.

  • Файл можно редактировать, не выгружая его в Visual Studio.The file can be edited without unloading it in Visual Studio:

    Параметр изменения файла CSPROJ в контекстном меню в Visual Studio 2017

Замена файла Global.asaxGlobal.asax file replacement

В ASP.NET Core появился новый механизм для начальной загрузки приложения.ASP.NET Core introduced a new mechanism for bootstrapping an app. Точкой входа для приложений ASP.NET стал файл Global.asax.The entry point for ASP.NET applications is the Global.asax file. Такие задачи, как конфигурация маршрута, а также регистрации фильтров и областей, теперь выполняются в файле 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);
    }
}

При этом подходе приложение сопоставляется с сервером, на котором оно развертывается, таким образом, чтобы это не мешало реализации.This approach couples the application and the server to which it's deployed in a way that interferes with the implementation. Чтобы отделить приложение от сервера, был введен OWIN, который обеспечивает более точный способ совместного использования нескольких платформ.In an effort to decouple, OWIN was introduced to provide a cleaner way to use multiple frameworks together. OWIN предоставляет конвейер для добавления только необходимых модулей.OWIN provides a pipeline to add only the modules needed. Среда внешнего размещения принимает функцию Startup для настройки служб и конвейера обработки запросов приложения.The hosting environment takes a Startup function to configure services and the app's request pipeline. Startup регистрирует набор ПО промежуточного слоя в приложении.Startup registers a set of middleware with the application. Для каждого запроса приложение вызывает каждый из компонентов ПО промежуточного слоя по заглавному указателю связанного списка на существующий набор обработчиков.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. Каждый компонент ПО промежуточного слоя может добавлять в конвейер обработки запросов один обработчик или несколько.Each middleware component can add one or more handlers to the request handling pipeline. Это достигается путем возвращения ссылки на обработчик, который представляет собой новый заголовок списка.This is accomplished by returning a reference to the handler that's the new head of the list. Каждый обработчик отвечает за запоминание и вызов следующего обработчика в списке.Each handler is responsible for remembering and invoking the next handler in the list. При работе с ASP.NET Core точкой входа в приложения становится Startup, и зависимость от файла Global.asax исчезает.With ASP.NET Core, the entry point to an application is Startup, and you no longer have a dependency on Global.asax. Используя OWIN с .NET Framework, применяйте для конвейера код следующего вида: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);
        }
    }
}

Он определяет ваши маршруты по умолчанию и изначально предусматривает XmlSerialization по Json.This configures your default routes, and defaults to XmlSerialization over Json. При необходимости добавьте другое ПО промежуточного слоя для этого конвейера (загрузка служб, параметры конфигурации, статические файлы и т. д.).Add other Middleware to this pipeline as needed (loading services, configuration settings, static files, etc.).

ASP.NET Core использует аналогичный подход, но не требует OWIN для обработки запроса.ASP.NET Core uses a similar approach, but doesn't rely on OWIN to handle the entry. Вместо этого применяется метод Program.cs Main (как в консольных приложениях) и Startup загружается через него.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 должен включать метод Configure.Startup must include a Configure method. В Configure добавьте в конвейер необходимое ПО промежуточного слоя.In Configure, add the necessary middleware to the pipeline. В примере ниже (на основе шаблона веб-сайта по умолчанию) используются методы расширения для настройки конвейера с поддержкой следующих компонентов:In the following example (from the default web site template), extension methods configure the pipeline with support for:

  • Страницы ошибокError pages
  • HTTP Strict Transport Security;HTTP Strict Transport Security
  • перенаправление с HTTP на HTTPS;HTTP 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();
}

Сервер и приложение разделены, что позволит вам в будущем легко перейти на другую платформу.The host and application have been decoupled, which provides the flexibility of moving to a different platform in the future.

Примечание

Более подробный справочник по запуску ASP.NET Core и ПО промежуточного слоя см. в статье Запуск в ASP.NET Core.For a more in-depth reference to ASP.NET Core Startup and Middleware, see Startup in ASP.NET Core

Конфигурации храненияStore configurations

ASP.NET поддерживает параметры хранения.ASP.NET supports storing settings. Эти параметры используются, например, для поддержки среды, в которой развертываются приложения.These setting are used, for example, to support the environment to which the applications were deployed. Как правило, все пользовательские пары ключей и значений хранятся в разделе <appSettings> файла 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>

Приложения считывают эти параметры с помощью коллекции ConfigurationManager.AppSettings в пространстве имен System.Configuration: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 может сохранять данные конфигурации для приложения из любого файла и загружать их в процессе начальной загрузки ПО промежуточного слоя.ASP.NET Core can store configuration data for the application in any file and load them as part of middleware bootstrapping. По умолчанию в шаблонах проектов используется файл appsettings.json:The default file used in the project templates is appsettings.json:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  // Here is where you can supply custom configuration settings, Since it is is JSON, everything is represented as key: value pairs
  // Name of section is your choice
  "AppConfiguration": {
    "UserName": "UserName",
    "Password": "Password"
  }
}

Загрузка этого файла в экземпляр IConfiguration в вашем приложении выполняется в файле 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; }

Для получения этих параметров приложение считывает данные из Configuration:The app reads from Configuration to get the settings:

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

Существуют расширения, повышающие надежность этого подхода, например, внедрение зависимостей позволяет загружать эти значения в службу.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. Метод внедрения зависимостей предоставляет строго типизированный набор объектов конфигурации.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"));

Примечание

Более подробное руководство по конфигурации ASP.NET Core см. в статье Конфигурация в ASP.NET Core.For a more in-depth reference to ASP.NET Core configuration, see Configuration in ASP.NET Core.

Собственные функции внедрения зависимостейNative dependency injection

При сборке больших, масштабируемых приложений важно обеспечить слабые взаимозависимости между компонентами и службами.An important goal when building large, scalable applications is the loose coupling of components and services. Внедрение зависимостей — популярный способ решения этой задачи и собственный компонент ASP.NET Core.Dependency Injection is a popular technique for achieving this, and it's a native component of ASP.NET Core.

В приложениях ASP.NET разработчики используют для внедрения зависимостей стороннюю библиотеку.In ASP.NET apps, developers rely on a third-party library to implement Dependency Injection. Одна из таких библиотек, Unity, входит в шаблоны и рекомендации Майкрософт.One such library is Unity, provided by Microsoft Patterns & Practices.

Пример внедрения зависимостей с использованием библиотеки Unity — это реализация IDependencyResolver как оболочки для 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();
    }
}

Создайте экземпляр UnityContainer, зарегистрируйте свою службу и установите средство разрешения зависимостей HttpConfiguration в новый экземпляр UnityResolver для вашего контейнера: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.
}

Там, где необходимо, вставьте IProductRepository:Inject IProductRepository where needed:

public class ProductsController : ApiController
{
    private IProductRepository _repository;

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

    // Other controller methods not shown.
}

Так как внедрение зависимостей является частью ASP.NET Core, вы можете добавить свою службу в метод ConfigureServices файла 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>();
}

Репозиторий, как и в Unity, можно внедрять где угодно.The repository can be injected anywhere, as was true with Unity.

Примечание

Дополнительные сведения о внедрении зависимостей см. здесь.For more information on dependency injection, see Dependency injection.

Обслуживание статических файловServe static files

Важной частью разработки веб-приложений является возможность обслуживания статических ресурсов на стороне клиента.An important part of web development is the ability to serve static, client-side assets. Наиболее распространенные примеры статических файлов — это HTML, каскадные таблицы стилей, Javascript и изображения.The most common examples of static files are HTML, CSS, Javascript, and images. Эти файлы необходимо сохранять в место публикации приложения (или CDN) и указывать по ссылкам, чтобы они могли загружаться по запросу.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. В ASP.NET Core ситуация изменилась.This process has changed in ASP.NET Core.

В ASP.NET статические файлы хранятся в разных папках со ссылками в представлениях.In ASP.NET, static files are stored in various directories and referenced in the views.

В ASP.NET Core, если не заданы другие настройки, статические файлы хранятся на корневом веб-узле (<содержимое корневой папки>/wwwroot).In ASP.NET Core, static files are stored in the "web root" (<content root>/wwwroot), unless configured otherwise. Файлы загружаются в конвейер запросов путем вызова метода расширения UseStaticFiles из 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();
}

Примечание

Для работы с .NET Framework установите пакет NuGet Microsoft.AspNetCore.StaticFiles.If targeting .NET Framework, install the NuGet package Microsoft.AspNetCore.StaticFiles.

Например, ресурс изображения в папке wwwroot/images доступен для браузера в расположении 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>.

Примечание

Более подробное руководство по обработке статических файлов в ASP.NET Core см. в статье Статические файлы.For a more in-depth reference to serving static files in ASP.NET Core, see Static files.

Дополнительные ресурсыAdditional resources