Como atualizar um projeto existente do ASP.NET MVC para o ASP.NET Core. Guia prático

Esta postagem nasceu da nossa experiência na migração de um projeto existente do ASP.NET MVC para o ASP.NET Core. Tentamos reunir todo o processo de migração de forma estruturada e descrever os vários gargalos para que os desenvolvedores pudessem continuar confiando nesse material e seguir o roteiro para resolver esses problemas.

Algumas palavras sobre o nosso projeto. Somos uma plataforma de comércio eletrônico de código aberto no ASP.NET, que na época da transferência já existia com sucesso há 9 anos. Fizemos a migração há dois anos - mas nossas mãos passaram a escrever sobre isso apenas agora. Naquela época, éramos um dos primeiros grandes projetos que decidiram dar esse passo.

Por que mudar para o ASP.NET Core?


Antes de prosseguir com as etapas para migrar do ASP.NET MVC para o ASP.NET Core, algumas palavras sobre os benefícios desta plataforma.



Benefícios do ASP.NET Core
Portanto, o ASP.NET Core já é uma estrutura bastante conhecida e desenvolvida, que já passou por várias atualizações importantes, o que significa que hoje é bastante estável, tecnologicamente avançado e resistente a ataques XSRF / CSRF.

A plataforma cruzada é uma das características que lhe permitem ganhar cada vez mais popularidade. A partir de agora, seu aplicativo da Web poderá ser executado nos ambientes Windows e Unix.

Modularidade - O ASP.NET Core vem totalmente na forma de pacotes NuGet, permitindo otimizar o aplicativo, incluindo os pacotes necessários selecionados. Isso melhora o desempenho da solução e reduz o tempo necessário para atualizar partes individuais. Esse é o segundo recurso importante que permite que os desenvolvedores integrem de maneira mais flexível novos recursos em suas soluções.

O desempenho é outro passo para a criação de um aplicativo de alto desempenho. O ASP.NET Core processa 2300% mais solicitações por segundo que o ASP.NET 4.6 e 800% mais solicitações por segundo que node.js. Você pode examinar os testes de desempenho detalhados aqui ou aqui .



Middleware é o novo pipeline modular leve e de alto desempenho para solicitações no aplicativo. Cada parte do middleware processa uma solicitação HTTP e decide retornar o resultado ou passa a próxima parte do middleware. Essa abordagem fornece ao desenvolvedor controle completo sobre o pipeline HTTP e contribui para o desenvolvimento de módulos simples para o aplicativo, o que é importante para um crescente projeto de código aberto.

O ASP.NET Core MVC fornece recursos que simplificam o desenvolvimento da web. O NopCommerce já usava recursos como o modelo Model-View-Controller, sintaxe Razor, vinculação e validação de modelos, mas surgiram novas ferramentas:

  • Tag Helpers. Este é o código do lado do servidor para contribuir com a criação e renderização de elementos HTML nos arquivos do Razor.
  • Exibir componentes. Esta é uma nova ferramenta, semelhante a visualizações parciais, mas muito mais poderosa. O nopCommerce usa componentes de exibição quando a reutilização da lógica de renderização é necessária e quando a tarefa é muito complexa para uma exibição parcial.
  • Visão de DI. Embora a maioria dos dados exibidos nas visualizações seja passada do controlador, o nopCommerce tem visualizações nas quais a injeção de dependência é mais conveniente.

Obviamente, o ASP.NET Core possui muito mais recursos, mas acabamos de examinar o mais interessante.

Agora vamos falar sobre o que você deve considerar ao transportar seu aplicativo para uma nova plataforma.

A migração


O texto conterá um grande número de links para a documentação oficial do ASP.NET Core para ajudar a obter informações mais detalhadas sobre o tópico. Especialmente relevante para desenvolvedores que enfrentam uma tarefa semelhante pela primeira vez.

Etapa 1. Preparação das ferramentas


A primeira coisa que você precisa fazer é atualizar o Visual Studio 2017 para a versão 15.3 ou superior. E instale a versão mais recente do .NET Core SDK.

Antes de iniciar a migração, é recomendável usar a ferramenta de análise de portabilidade do .NET .Net Portability Analyzer . Este é um bom ponto de partida para entender o quão trabalhosa será a transição de uma plataforma para outra. Mas, é claro, essa ferramenta não resolve todos os problemas e, no processo, haverá muitas armadilhas. A seguir, serão descritos os principais estágios que precisarão ser aprovados e as soluções utilizadas em nosso projeto serão mostradas.

A primeira coisa que você precisa é atualizar os links para as bibliotecas usadas no projeto que suportam o .NET Standard.

Etapa 2. Análise de compatibilidade dos pacotes NuGet para suportar o padrão .Net


Se você usa pacotes NuGet em seu projeto, precisa verificar se eles são compatíveis com o .NET Core. Uma maneira de fazer isso é usar a ferramenta NuGetPackageExplorer .

Etapa 3. O .NET Core usa o novo formato de arquivo csproj


É importante usar a nova abordagem para adicionar links de terceiros introduzidos no .NET Core: quando uma nova biblioteca de classes for adicionada à solução, você deverá abrir o arquivo principal do projeto e substituir seu conteúdo pelo seguinte:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.6" /> ... </ItemGroup> ... </Project> 

Os links das bibliotecas conectadas serão baixados automaticamente. Mais informações sobre o mapeamento entre as propriedades project.json e CSPROJ podem ser encontradas na documentação oficial aqui e aqui .

Etapa 4. Atualizando o espaço para nome


Você deve remover todos os usos do System.Web e substituí-los por Microsoft.AspNetCore.

Etapa 5. Você deve configurar o arquivo Startup.cs. em vez de usar global.asax


O ASP.NET Core possui um novo mecanismo para carregar o aplicativo. O ponto de entrada para o aplicativo se torna Startup e a dependência no arquivo Global.asax desaparece. Startup registra o pacote de middleware no aplicativo. Startup deve incluir o método Configure . No Configure adicione o middleware necessário ao pipeline.

Problemas com o Startup.cs

  1. Configurando Middleware para Solicitações MVC e WebAPI
  2. Definições de configuração para:


 //add basic MVC feature var mvcBuilder = services.AddMvc(); //add custom model binder provider (to the top of the provider list) mvcBuilder.AddMvcOptions(options => options.ModelBinderProviders.Insert(0, new NopModelBinderProvider())); /// <summary> /// Represents model binder provider for the creating NopModelBinder /// </summary> public class NopModelBinderProvider : IModelBinderProvider { /// <summary> /// Creates a nop model binder based on passed context /// </summary> /// <param name="context">Model binder provider context</param> /// <returns>Model binder</returns> public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); var modelType = context.Metadata.ModelType; if (!typeof(BaseNopModel).IsAssignableFrom(modelType)) return null; //use NopModelBinder as a ComplexTypeModelBinder for BaseNopModel if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType) { //create binders for all model properties var propertyBinders = context.Metadata.Properties .ToDictionary(modelProperty => modelProperty, modelProperty => context.CreateBinder(modelProperty)); return new NopModelBinder(propertyBinders, EngineContext.Current.Resolve<ILoggerFactory>()); } //or return null to further search for a suitable binder return null; } } 



 app.UseMvc(routes => { routes.MapRoute("areaRoute", "{area:exists}/{controller=Admin}/{action=Index}/{id?}"); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); 

Ao mesmo tempo, a pasta com o nome Área, dentro da qual a pasta Admin está localizada, deve ser colocada na raiz do aplicativo. Agora, o atributo [Area("Admin")] [Route("admin")] será usado para conectar o controlador a esta área.
Resta apenas criar visualizações para todas as ações descritas no controlador.


 [Area("Admin")] [Route("admin")] public class AdminController : Controller { public IActionResult Index() { return View(); } } 

Validação

Agora você não precisa passar o IFormCollection para os controladores, pois, neste caso, a validação do servidor asp.net está desabilitada - o MVC está suprimindo a validação adicional se o IFormCollection não for nulo. A solução para o problema pode ser adicionar essa propriedade ao modelo, isso nos impedirá de passar diretamente para o método do controlador. Esta regra é válida apenas se um modelo estiver presente, mas se não houver um modelo, não haverá validação.

As propriedades filho não são mais validadas automaticamente. Deve ser especificado manualmente.

Etapa 6. Transferindo manipuladores HTTP e módulos HTTP para Middleware


Os manipuladores HTTP e os módulos HTTP são essencialmente muito semelhantes ao conceito de Middleware no ASP.NET Core , mas, diferentemente dos módulos, a ordem do middleware é baseada na ordem em que eles são inseridos no pipeline de solicitação. A ordem dos módulos, na maioria das vezes, é baseada nos eventos do ciclo de vida do aplicativo . A ordem do middleware para as respostas é o oposto da ordem das solicitações, e a ordem dos módulos para as solicitações e respostas é a mesma.Com base nisso, você pode prosseguir com a atualização.

Então, o que resta ser atualizado:

  • Migração de módulos para Middleware (AuthenticationMiddleware, CultureMiddleware, etc.)
  • Manipuladores para Middleware
  • Usando o novo middleware

A autenticação em nosso projeto não usa o sistema de credenciais interno; para esses fins, é usado o middleware AuthenticationMiddleware, desenvolvido de acordo com a nova estrutura do ASP.NET Core.

 public class AuthenticationMiddleware { private readonly RequestDelegate _next; public AuthenticationMiddleware(IAuthenticationSchemeProvider schemes, RequestDelegate next) { Schemes = schemes ?? throw new ArgumentNullException(nameof(schemes)); _next = next ?? throw new ArgumentNullException(nameof(next)); } public IAuthenticationSchemeProvider Schemes { get; set; } public async Task Invoke(HttpContext context) { context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature { OriginalPath = context.Request.Path, OriginalPathBase = context.Request.PathBase }); var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>(); foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync()) { try { if (await handlers.GetHandlerAsync(context, scheme.Name) is IAuthenticationRequestHandler handler && await handler.HandleRequestAsync()) return; } catch { // ignored } } var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync(); if (defaultAuthenticate != null) { var result = await context.AuthenticateAsync(defaultAuthenticate.Name); if (result?.Principal != null) { context.User = result.Principal; } } await _next(context); } } 

O ASP.NET fornece muitos middlewares incorporados que você pode usar em seu aplicativo, mas observe que o desenvolvedor pode criar seu próprio middleware e adicioná-lo ao pipeline de solicitação HTTP. Para simplificar esse mecanismo, adicionamos uma interface especial e agora basta criar uma classe que a implemente.

 public interface INopStartup { /// <summary> /// Add and configure any of the middleware /// </summary> /// <param name="services">Collection of service descriptors</param> /// <param name="configuration">Configuration of the application</param> void ConfigureServices(IServiceCollection services, IConfiguration configuration); /// <summary> /// Configure the using of added middleware /// </summary> /// <param name="application">Builder for configuring an application's request pipeline</param> void Configure(IApplicationBuilder application); /// <summary> /// Gets order of this startup configuration implementation /// </summary> int Order { get; } } 

Aqui você pode adicionar e configurar seu middleware:

 /// <summary> /// Represents object for the configuring authentication middleware on application startup /// </summary> public class AuthenticationStartup : INopStartup { /// <summary> /// Add and configure any of the middleware /// </summary> /// <param name="services">Collection of service descriptors</param> /// <param name="configuration">Configuration of the application</param> public void ConfigureServices(IServiceCollection services, IConfiguration configuration) { //add data protection services.AddNopDataProtection(); //add authentication services.AddNopAuthentication(); } /// <summary> /// Configure the using of added middleware /// </summary> /// <param name="application">Builder for configuring an application's request pipeline</param> public void Configure(IApplicationBuilder application) { //configure authentication application.UseNopAuthentication(); } /// <summary> /// Gets order of this startup configuration implementation /// </summary> public int Order => 500; //authentication should be loaded before MVC } 

Etapa 7. Usando DI Integrado


A injeção de dependência é um dos principais recursos do processo de design do aplicativo no ASP.NET Core. Ele permite que você crie aplicativos fracamente acoplados que sejam mais testáveis, modulares e, como resultado, sustentáveis. Isso foi possível seguindo o princípio da inversão de dependência. Para instalar dependências, os contêineres IoC (Inversion of Control) são usados. No ASP.NET Core, esse contêiner é representado pela interface IServiceProvider. Os serviços são instalados no aplicativo no método Startup.ConfigureServices() .

Qualquer serviço registrado pode ser configurado com três escopos:

  • transitório
  • escopo
  • singleton

 services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddSingleton<Isingleton,MySingleton>(); 

Etapa 8. Usando conchas de compatibilidade do projeto WebAPI (calço)


Para simplificar a migração de uma implementação de API da Web existente, é recomendável usar o pacote Microsoft.AspNetCore.Mvc.WebApiCompatShim NuGet. As seguintes funções compatíveis são suportadas:

  • Adiciona o tipo ApiController
  • Ativa a ligação do modelo de estilo da API da web
  • Estende a ligação do modelo para que as ações do controlador possam aceitar parâmetros do tipo HttpRequestMessage.
  • Adiciona formatadores de mensagens que permitem que as ações retornem resultados do tipo HttpResponseMessage

 services.AddMvc().AddWebApiConventions(); routes.MapWebApiRoute(name: "DefaultApi", template: "api/{controller}/{id?}" ); 

Etapa 9. Transferir a configuração do aplicativo


Anteriormente, algumas configurações eram salvas no arquivo web.config. Agora estamos adotando uma nova abordagem baseada em pares de valores-chave estabelecidos pelos provedores de configuração . Esse é o mecanismo recomendado no ASP.NET Core e usamos o arquivo appsettings.json.

Você também pode usar o pacote NuGet System.Configuration.ConfigurationManager se, por algum motivo, quiser continuar usando * .config. Nesse caso, você terá que abandonar a capacidade de executar o aplicativo nas plataformas Unix e executá-lo apenas no IIS.

Se você deseja usar o provedor de configuração do Azure Key Vault , consulte o conteúdo da Migração de conteúdo para o Azure Key Valut . Em nosso projeto, essa não era a tarefa.

Etapa 10. Transferindo conteúdo estático para wwwroot


Para veicular conteúdo estático, você deve informar ao host a raiz do conteúdo do diretório atual. O padrão é wwwroot. Você pode personalizar seu diretório para armazenar arquivos estáticos, configurando o middleware.



Etapa 11. Portando o EntityFramework para o EF Core


Se o projeto usa alguns recursos específicos do Entity Framework 6 que não são suportados no EF Core , faz sentido executar o aplicativo no NET Framework . Nesse caso, no entanto, você precisa sacrificar a multiplataforma. O aplicativo funcionará apenas no Windows e no IIS.

Vamos dar uma olhada nas principais alterações a serem levadas em consideração:

  • Namespace System.Data.Entity substituído por Microsoft.EntityFrameworkCore
  • A assinatura do construtor DbContext foi alterada. Agora você precisa injetar DbContextOptions
  • Método HasDatabaseGeneratedOption (DatabaseGeneratedOption.None) substituído por ValueGeneratedNever ()
  • Método WillCascadeOnDelete (false) substituído por OnDelete (DeleteBehavior.Restrict)
  • Método OnModelCreating (DbModelBuilder modelBuilder) substituído por OnModelCreating (ModelBuilder modelBuilder)
  • O método HasOptional não está mais disponível
  • a configuração dos objetos foi alterada, agora você precisa usar OnModelCreating, porque EntityTypeConfiguration não está mais disponível
  • O atributo ComplexType não está mais disponível
  • Substituições da interface IDbSet com DbSet
  • ComplexType - o suporte a tipos complexos apareceu no EF Core 2 com o tipo de Entidade Própria ( https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities ) e tabelas sem uma Chave Primária com QueryType no EF Core 2.1 ( https://docs.microsoft.com/en-us/ef/core/modeling/query-types )
  • chaves estrangeiras no EF Core geram propriedades de sombra usando o padrão de ID [Entity], diferente do EF6, que usa o padrão [Entity] _Id. Portanto, primeiro adicione chaves estrangeiras como uma propriedade regular à entidade.
  • Para oferecer suporte ao DI para DbContext, configure seu DbContex em ConfigureServices

 /// <summary> /// Register base object context /// </summary> /// <param name="services">Collection of service descriptors</param> public static void AddNopObjectContext(this IServiceCollection services) { services.AddDbContextPool<NopObjectContext>(optionsBuilder => { optionsBuilder.UseSqlServerWithLazyLoading(services); }); } /// <summary> /// SQL Server specific extension method for Microsoft.EntityFrameworkCore.DbContextOptionsBuilder /// </summary> /// <param name="optionsBuilder">Database context options builder</param> /// <param name="services">Collection of service descriptors</param> public static void UseSqlServerWithLazyLoading(this DbContextOptionsBuilder optionsBuilder, IServiceCollection services) { var nopConfig = services.BuildServiceProvider().GetRequiredService<NopConfig>(); var dataSettings = DataSettingsManager.LoadSettings(); if (!dataSettings?.IsValid ?? true) return; var dbContextOptionsBuilder = optionsBuilder.UseLazyLoadingProxies(); if (nopConfig.UseRowNumberForPaging) dbContextOptionsBuilder.UseSqlServer(dataSettings.DataConnectionString, option => option.UseRowNumberForPaging()); else dbContextOptionsBuilder.UseSqlServer(dataSettings.DataConnectionString); } 

Use a ferramenta Comparar SQL para verificar se o EF Core gera um esquema de banco de dados semelhante ao Entity Framework durante a migração .

Etapa 12. Removendo todas as referências HttpContext, substituindo classes obsoletas e alterando o espaço para nome


Durante a migração do seu projeto, você descobrirá que um número suficientemente grande de classes foi renomeado ou movido e agora é necessário alinhar tudo com os novos requisitos. Aqui está uma lista das principais transições que você pode encontrar:

  • HttpPostedFileBase -> IFormFile
  • O acesso para acessar o HttpContext agora é através do IHttpContextAccessor
  • HtmlHelper -> IHtmlHelper
  • ActionResult -> IActionResult
  • HttpUtility -> WebUtility
  • Em vez de HttpSessionStateBase - ISession, é acessível a partir HttpContext.Session. de Microsoft.AspNetCore.Http
  • Request.Cookies retorna IRequestCookieCollection: IEnumerable <KeyValuePair <string, string >>, portanto, em vez de HttpCookie, KeyValuePair <string, string> de Microsoft.AspNetCore.Http

Substituição de namespace:

  • SelectList -> Microsoft.AspNetCore.Mvc.Rendering
  • UrlHelper -> WebUtitlity
  • MimeMapping -> FileExtensionContentTypeProvider
  • MvcHtmlString -> IHtmlString e HtmlString
  • ModelState, ModelStateDictionary, ModelError -> Microsoft.AspNetCore.Mvc.ModelBinding
  • FormCollection -> IFormCollection
  • Request.Url.Scheme -> this.Url.ActionContext.HttpContext.Request.Scheme

Outro:

  • MvcHtmlString.IsNullOrEmpty (IHtmlString) -> String.IsNullOrEmpty (variable.ToHtmlString ())
  • [ValidateInput (false)] - geralmente não existe mais e não é necessário
  • HttpUnauthorizedResult -> UnauthorizedResult
  • [AllowHtml] - não há mais diretiva e não é necessário
  • Método TagBuilder.SetInnerText substituído - agora é InnerHtml.AppendHtml
  • JsonRequestBehavior.AllowGet ao retornar Json não é mais necessário
  • HttpUtility.JavaScriptStringEncode. -> JavaScriptEncoder.Default.Encode
  • Request.RawUrl. É necessário conectar separadamente Request.Path + Request.QueryString
  • AllowHtmlAttribute - não há mais classe
  • XmlDownloadResult - agora você pode usar apenas retornar File (Encoding.UTF8.GetBytes (xml), "application / xml", "filename.xml");
  • [ValidateInput (false)] - não há mais diretiva e não é necessário

Etapa 13. Atualizando autenticação e autorização


Eu já escrevi acima que em nosso projeto a autenticação não é implementada usando o sistema de identidade interno, mas é removida em uma camada separada de middleware. No entanto, o ASP.NET Core possui seu próprio mecanismo para fornecer credenciais. Mais detalhes podem ser encontrados na documentação aqui .

Quanto à proteção de dados - não usamos mais o MachineKey . Em vez disso, usamos o recurso interno de proteção de dados. Por padrão, as chaves são geradas quando o aplicativo é iniciado. O armazém de dados pode ser:

  • Sistema de arquivos - keystore baseado em sistema de arquivos
  • Armazenamento do Azure - chaves de proteção de dados no Armazenamento de Blob do Azure
  • Redis - chaves de proteção de dados no cache Redis
  • Registro - deve ser usado se o aplicativo não tiver acesso ao sistema de arquivos
  • EF Core - chaves são armazenadas no banco de dados

Se os mecanismos internos não forem adequados, você poderá especificar seu próprio mecanismo de armazenamento de chaves fornecendo um IXmlRepository personalizado.

Etapa 14. Atualizando JS / CSS


A maneira de trabalhar com recursos estáticos mudou: agora todos eles devem ser armazenados na pasta raiz do projeto wwwroot , a menos que, é claro, outras configurações sejam especificadas.

Ao usar blocos embutidos javascript, é recomendável movê-los para o final da página. Basta usar o atributo asp-location = "Footer" para suas tags. A mesma regra se aplica aos arquivos js.

Use a extensão BundlerMinifier como um substituto para System.Web.Optimization - isso permitirá vincular e minimizar JavaScript e CSS ao criar o projeto. Link para a documentação.

Etapa 15. Migrando visualizações


Ações filho não são mais usadas. Em vez disso, o ASP.NET Core oferece uma nova ferramenta poderosa - ViewComponents , chamada de forma assíncrona.

Como obter uma string do ViewComponent:

 /// <summary> /// Render component to string /// </summary> /// <param name="componentName">Component name</param> /// <param name="arguments">Arguments</param> /// <returns>Result</returns> protected virtual string RenderViewComponentToString(string componentName, object arguments = null) { if (string.IsNullOrEmpty(componentName)) throw new ArgumentNullException(nameof(componentName)); var actionContextAccessor = HttpContext.RequestServices.GetService(typeof(IActionContextAccessor)) as IActionContextAccessor; if (actionContextAccessor == null) throw new Exception("IActionContextAccessor cannot be resolved"); var context = actionContextAccessor.ActionContext; var viewComponentResult = ViewComponent(componentName, arguments); var viewData = ViewData; if (viewData == null) { throw new NotImplementedException(); } var tempData = TempData; if (tempData == null) { throw new NotImplementedException(); } using (var writer = new StringWriter()) { var viewContext = new ViewContext( context, NullView.Instance, viewData, tempData, writer, new HtmlHelperOptions()); // IViewComponentHelper is stateful, we want to make sure to retrieve it every time we need it. var viewComponentHelper = context.HttpContext.RequestServices.GetRequiredService<IViewComponentHelper>(); (viewComponentHelper as IViewContextAware)?.Contextualize(viewContext); var result = viewComponentResult.ViewComponentType == null ? viewComponentHelper.InvokeAsync(viewComponentResult.ViewComponentName, viewComponentResult.Arguments): viewComponentHelper.InvokeAsync(viewComponentResult.ViewComponentType, viewComponentResult.Arguments); result.Result.WriteTo(writer, HtmlEncoder.Default); return writer.ToString(); } } 

Não é mais necessário usar o HtmlHelper - o ASP.NET Core possui um grande número de funções de tag auxiliar ( Tag Helpers ) incorporadas. Quando o aplicativo está em execução, eles são processados ​​pelo mecanismo Razor no lado do servidor e, finalmente, são convertidos em elementos html padrão. Isso simplifica bastante o desenvolvimento de aplicativos. E, é claro, você pode implementar seus próprios auxiliares de tags.

Começamos a usar injeção de dependência nas visualizações em vez de permitir configurações e serviços usando o EngineContext .

Portanto, os principais pontos sobre a migração de visualizações:

  • Converter Views/web.config Views/_ViewImports.cshtml - usado para importar namespaces e Views/web.config Views/_ViewImports.cshtml dependências. Este arquivo não suporta outras funções do Razor , como definições de função e seção.
  • Converter namespaces.add em @using
  • Transferir quaisquer configurações para a configuração principal do aplicativo
  • Scripts.Render e Styles.Render não existe. Substitua libman ou BundlerMinifier links para saída

Em conclusão


Vimos pela nossa experiência que o processo de migração de um aplicativo Web grande é uma tarefa que consome muito tempo e que dificilmente pode ser realizada sem armadilhas. Planejamos mudar para uma nova estrutura assim que sua primeira versão estável foi lançada, mas não conseguimos finalizá-la imediatamente: havia algumas funções críticas que ainda não haviam sido transferidas para o .NET Core, em particular relacionadas ao EntityFramework. Portanto, tivemos que lançar o próximo lançamento usando uma abordagem mista - a arquitetura do .NET Core com as dependências do .NET Framework.

Conseguimos adaptar completamente o projeto após o lançamento do .NET Core 2.1, tendo na época uma solução estável já trabalhando na nova arquitetura - tudo o que restava era substituir alguns pacotes e reescrever o trabalho com o EF Core. Assim, a migração completa para a nova estrutura levou vários meses de trabalho.

Você pode aprender mais sobre o nosso projeto em nosso repositório no GitHub .

Source: https://habr.com/ru/post/pt472738/


All Articles