Esta publicación nació de nuestra experiencia al migrar un proyecto existente de ASP.NET MVC a ASP.NET Core. Intentamos reunir todo el proceso de migración en una forma estructurada y describir los diversos cuellos de botella para que los desarrolladores puedan seguir confiando en este material y seguir la hoja de ruta para resolver tales problemas.
Algunas palabras sobre nuestro proyecto. Somos una plataforma de comercio electrónico de código abierto en ASP.NET, que en el momento de la transferencia ya había existido con éxito durante 9 años. Hicimos la migración hace 2 años, pero nuestras manos llegaron a escribir sobre eso solo ahora. En ese momento, fuimos uno de los primeros proyectos importantes que decidieron tal paso.
¿Por qué cambiar a ASP.NET Core?
Antes de continuar con los pasos para migrar de ASP.NET MVC a ASP.NET Core, algunas palabras sobre los beneficios de esta plataforma.

Beneficios de ASP.NET CoreEntonces, ASP.NET Core ya es un marco bastante conocido y desarrollado, que ya ha sufrido varias actualizaciones importantes, lo que significa que hoy es bastante estable, tecnológicamente avanzado y resistente a los ataques XSRF / CSRF.
La multiplataforma es una de las características que le permiten ganar más y más popularidad. A partir de ahora, su aplicación web puede ejecutarse en entornos Windows y Unix.
Modularidad : ASP.NET Core viene completamente en forma de paquetes NuGet, esto le permite optimizar la aplicación, incluidos los paquetes necesarios seleccionados. Esto mejora el rendimiento de la solución y reduce el tiempo que lleva actualizar las piezas individuales. Esta es la segunda característica importante que permite a los desarrolladores integrar de manera más flexible nuevas características en su solución.
El rendimiento es otro paso hacia la creación de una aplicación de alto rendimiento, ASP.NET Core procesa un 2300% más de solicitudes por segundo que ASP.NET 4.6, y un 800% más de solicitudes por segundo que node.js. Puede examinar pruebas de rendimiento detalladas usted mismo
aquí o
aquí .
Middleware es la nueva tubería modular ligera de alto rendimiento para solicitudes en la aplicación. Cada pieza de middleware procesa una solicitud HTTP y luego decide devolver el resultado o pasa la siguiente pieza de middleware. Este enfoque le da al desarrollador un control completo sobre la tubería HTTP y contribuye al desarrollo de módulos simples para la aplicación, lo cual es importante para un proyecto de código abierto en crecimiento.
ASP.NET Core MVC proporciona características que simplifican el desarrollo web. NopCommerce ya usaba características como la plantilla Modelo-Vista-Controlador, la sintaxis de Razor, el enlace y la validación del modelo, pero aparecieron nuevas herramientas:
- Ayudantes de etiqueta. Este es un código del lado del servidor para contribuir a la creación y representación de elementos HTML en archivos Razor.
- Ver componentes. Esta es una herramienta nueva, similar a las vistas parciales, pero mucho más poderosa. nopCommerce utiliza componentes de vista cuando se requiere reutilizar la lógica de representación y cuando la tarea es demasiado compleja para una vista parcial.
- La opinión de DI. Aunque la mayoría de los datos que se muestran en las vistas pasan del controlador, nopCommerce tiene vistas en las que la inyección de dependencias es más conveniente.
Por supuesto, ASP.NET Core tiene muchas más funciones, pero acabamos de examinar las más interesantes.
Ahora hablemos sobre lo que debe considerar al portar su aplicación a una nueva plataforma.
La migracion
El texto contendrá una gran cantidad de enlaces a la documentación oficial de ASP.NET Core para ayudar a obtener información más detallada sobre el tema. Especialmente relevante para los desarrolladores que se enfrentan a una tarea similar por primera vez.
Paso 1. Preparación de herramientas.
Lo primero que debe hacer es actualizar Visual Studio 2017 a la versión 15.3 o superior. E instale la última versión del SDK de .NET Core.
Antes de comenzar la migración, se recomienda que utilice la herramienta de análisis de portabilidad .NET
.Net Portability Analyzer . Este es un buen punto de partida para comprender cuán laboriosa será la transición de una plataforma a otra. Pero, por supuesto, esta herramienta no resuelve todos los problemas, y en el proceso habrá muchas trampas. A continuación, se describirán las etapas principales que deberán superarse y se mostrarán las soluciones utilizadas en nuestro proyecto.
Lo primero que necesita es actualizar los enlaces a las bibliotecas utilizadas en el proyecto que admitirían .NET Standard.
Paso 2. Análisis de compatibilidad de paquetes NuGet para soportar .Net Standard
Si usa paquetes NuGet en su proyecto, debe verificar si son compatibles con .NET Core. Una forma de hacerlo es utilizar la herramienta
NuGetPackageExplorer .
Paso 3. .NET Core usa el nuevo formato de archivo csproj
Es importante utilizar el nuevo enfoque para agregar enlaces de terceros introducidos en .NET Core: cuando se agrega una nueva biblioteca de clases a la solución, debe abrir el archivo principal del proyecto y reemplazar su contenido con lo siguiente:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp2.2</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.6" /> ... </ItemGroup> ... </Project>
Los enlaces de las bibliotecas conectadas se descargarán automáticamente. Puede encontrar más información sobre el mapeo entre las propiedades project.json y CSPROJ en la documentación oficial
aquí y
aquí .
Paso 4. Actualizando el espacio de nombres
Debe eliminar todos los usos de System.Web y reemplazarlos con Microsoft.AspNetCore.
Paso 5. Debe configurar el archivo Startup.cs. en lugar de usar global.asax
ASP.NET Core tiene un nuevo mecanismo para cargar la aplicación. El punto de entrada a la aplicación se convierte en
Startup
y la dependencia del archivo
Global.asax desaparece.
Startup
registra la suite de middleware en la aplicación.
Startup
debe incluir el método
Configure
. En
Configure
agregue el middleware requerido a la tubería.
Problemas de Startup.cs- Configuración de middleware para solicitudes MVC y WebAPI
- Opciones de configuración para:
app.UseMvc(routes => { routes.MapRoute("areaRoute", "{area:exists}/{controller=Admin}/{action=Index}/{id?}"); routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); });
Al mismo tiempo, la carpeta con el nombre Área debe colocarse en la raíz de la aplicación, dentro de la cual se encuentra la carpeta Admin. Ahora, el atributo
[Area("Admin")] [Route("admin")]
se utilizará para conectar el controlador con esta área.
Solo queda crear vistas para todas las acciones descritas en el controlador.
[Area("Admin")] [Route("admin")] public class AdminController : Controller { public IActionResult Index() { return View(); } }
ValidaciónAhora no necesita pasar IFormCollection a los controladores, ya que en este caso la validación del servidor asp.net está deshabilitada: MVC está suprimiendo la validación adicional si se determina que IFormCollection no es nulo. La solución al problema puede ser agregar esta propiedad al modelo, esto evitará que pasemos directamente al método del controlador. Esta regla es válida solo si hay un modelo presente, pero si no hay modelo, entonces no habrá validación.
Las propiedades secundarias ya no se validan automáticamente. Debe especificarse manualmente.
Paso 6. Transferencia de controladores HTTP y módulos HTTP a Middleware
Los controladores HTTP y los módulos HTTP son esencialmente muy similares al concepto de
Middleware en ASP.NET Core , pero a diferencia de los módulos, el orden del middleware se basa en el orden en que se insertan en la canalización de solicitudes. El orden de los módulos, en su mayor parte, se basa en los eventos del
ciclo de vida de la aplicación . El orden del middleware para las respuestas es el opuesto al orden de las solicitudes, y el orden de los módulos para las solicitudes y las respuestas es el mismo. De acuerdo con esto, puede continuar con la actualización.
Entonces, lo que queda por actualizar:
- Migración de módulos para Middleware (AuthenticationMiddleware, CultureMiddleware, etc.)
- Manejadores de Middleware
- Usando New Middleware
La autenticación en nuestro proyecto no utiliza el sistema de credenciales incorporado; para estos fines, se utiliza AuthenticationMiddleware de middleware, desarrollado de acuerdo con la nueva estructura 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 {
ASP.NET proporciona una gran cantidad de middleware integrado que puede usar en su aplicación, pero tenga en cuenta que el desarrollador tiene la capacidad
de crear su propio middleware y agregarlo a la canalización de solicitudes HTTP. Para simplificar este mecanismo, agregamos una interfaz especial, y ahora es suficiente con crear una clase que lo implemente.
public interface INopStartup {
Aquí puede agregar y configurar su middleware:
Paso 7. Usando DI integrado
La inyección de
dependencia es una de las características clave en el proceso de diseño de aplicaciones en ASP.NET Core. Le permite crear aplicaciones poco acopladas que son más comprobables, modulares y, como resultado, mantenibles. Esto fue posible siguiendo el principio de inversión de dependencia. Para instalar dependencias, se utilizan contenedores IoC (Inversión de control). En ASP.NET Core, dicho contenedor está representado por la interfaz IServiceProvider. Los servicios se instalan en la aplicación en el método
Startup.ConfigureServices()
.
Cualquier servicio registrado se puede configurar con tres ámbitos:
- transitoria
- alcance
- singleton
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddSingleton<Isingleton,MySingleton>();
Paso 8. Uso de shells de compatibilidad de proyectos de WebAPI (Shim)
Para simplificar la migración de una implementación de API web existente, se recomienda utilizar el paquete
Microsoft.AspNetCore.Mvc.WebApiCompatShim NuGet. Se admiten las siguientes
funciones compatibles:
- Agrega el tipo ApiController
- Habilita el enlace del modelo de estilo de API web
- Extiende el enlace del modelo para que las acciones del controlador puedan aceptar parámetros de tipo HttpRequestMessage.
- Agrega formateadores de mensajes que permiten que las acciones devuelvan resultados de tipo HttpResponseMessage
services.AddMvc().AddWebApiConventions(); routes.MapWebApiRoute(name: "DefaultApi", template: "api/{controller}/{id?}" );
Paso 9. Transfiera la configuración de la aplicación
Anteriormente, algunas configuraciones se guardaban en el archivo web.config. Ahora estamos adoptando un
nuevo enfoque basado en pares clave-valor establecidos
por los proveedores de configuración . Este es el mecanismo recomendado en ASP.NET Core, y utilizamos el archivo appsettings.json.
También puede usar el paquete NuGet
System.Configuration.ConfigurationManager
si por alguna razón desea continuar usando * .config. En este caso, deberá abandonar la capacidad de ejecutar la aplicación en plataformas Unix y ejecutarla solo bajo IIS.
Si desea usar el proveedor de configuración de
Azure Key Vault , debe consultar la
migración de contenido al contenido
de Azure Key Valut . En nuestro proyecto, esta no era la tarea.
Paso 10. Transferencia de contenido estático a wwwroot
Para servir
contenido estático, debe indicarle al host web la raíz del contenido del directorio actual. El valor predeterminado es wwwroot. Puede personalizar su directorio para almacenar archivos estáticos configurando middleware.
Paso 11. Portar EntityFramework a EF Core
Si el proyecto usa algunas
características específicas
de Entity Framework 6 que
no son
compatibles con
EF Core
, entonces tiene sentido ejecutar la aplicación en
NET Framework
. En este caso, sin embargo, debes sacrificar la multiplataforma. La aplicación solo funcionará en Windows y bajo IIS.
Echemos un vistazo a los principales cambios a tener en cuenta:- System.Data.Entity namespace reemplazado por Microsoft.EntityFrameworkCore
- La firma del constructor DbContext ha cambiado. Ahora necesita inyectar DbContextOptions
- Método HasDatabaseGeneratedOption (DatabaseGeneratedOption.None) reemplazado por ValueGeneratedNever ()
- Método WillCascadeOnDelete (falso) reemplazado por OnDelete (DeleteBehavior.Restrict)
- Método OnModelCreating (DbModelBuilder modelBuilder) reemplazado por OnModelCreating (ModelBuilder modelBuilder)
- El método HasOptional ya no está disponible
- la configuración de los objetos ha cambiado, ahora necesita usar OnModelCreating, porque EntityTypeConfiguration ya no está disponible
- El atributo ComplexType ya no está disponible
- Reemplazos de la interfaz IDbSet con DbSet
- ComplexType: la compatibilidad con tipos complejos apareció en EF Core 2 con el tipo de entidad propia ( https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities ) y tablas sin una clave principal con QueryType en EF Core 2.1 ( https://docs.microsoft.com/en-us/ef/core/modeling/query-types )
- Las claves foráneas en EF Core generan propiedades de sombra utilizando el patrón Id de [Entidad], a diferencia de EF6, que usa el patrón Id de [Entidad]. Por lo tanto, primero agregue claves externas como una propiedad regular a la entidad.
- Para admitir DI para DbContext, configure su DbContex en
ConfigureServices
Use la herramienta
SQL Compare para verificar que
EF Core
genera un esquema de base de datos similar al
Entity Framework
durante la
migración .
Paso 12. Eliminar todas las referencias de HttpContext, reemplazar clases obsoletas y cambiar el espacio de nombres
Durante la migración de su proyecto, encontrará que se ha cambiado el nombre o se ha movido un número suficientemente grande de clases, y ahora debe alinear todo con los nuevos requisitos. Aquí hay una lista de las principales transiciones que puede encontrar:
- HttpPostedFileBase -> IFormFile
- El acceso para acceder a HttpContext ahora es a través de IHttpContextAccessor
- HtmlHelper -> IHtmlHelper
- ActionResult -> IActionResult
- HttpUtility -> WebUtility
- En lugar de HttpSessionStateBase - ISession, se puede acceder desde HttpContext.Session. de Microsoft.AspNetCore.Http
- Request.Cookies devuelve IRequestCookieCollection: IEnumerable <KeyValuePair <string, string >>, por lo que en lugar de HttpCookie, KeyValuePair <string, string> de Microsoft.AspNetCore.Http
Reemplazo del espacio de nombres:
- SelectList -> Microsoft.AspNetCore.Mvc.Rendering
- UrlHelper -> WebUtitlity
- MimeMapping -> FileExtensionContentTypeProvider
- MvcHtmlString -> IHtmlString y HtmlString
- ModelState, ModelStateDictionary, ModelError -> Microsoft.AspNetCore.Mvc.ModelBinding
- FormCollection -> IFormCollection
- Request.Url.Scheme -> this.Url.ActionContext.HttpContext.Request.Scheme
Otros:
- MvcHtmlString.IsNullOrEmpty (IHtmlString) -> String.IsNullOrEmpty (variable.ToHtmlString ())
- [ValidateInput (false)]: generalmente ya no existe y no es necesario
- HttpUnauthorizedResult -> UnauthorizedResult
- [AllowHtml]: no hay más directiva y no es necesaria
- Se reemplazó el método TagBuilder.SetInnerText; ahora es InnerHtml.AppendHtml
- JsonRequestBehavior.AllowGet cuando devolver Json ya no es necesario
- HttpUtility.JavaScriptStringEncode. -> JavaScriptEncoder.Default.Encode
- Request.RawUrl. Es necesario conectar por separado Request.Path + Request.QueryString
- AllowHtmlAttribute: no más clases
- XmlDownloadResult: ahora puede usar simplemente regresar File (Encoding.UTF8.GetBytes (xml), "application / xml", "filename.xml");
- [ValidateInput (false)]: no hay más directiva y no es necesaria
Paso 13. Actualización de autenticación y autorización
Ya escribí anteriormente que en nuestro proyecto la autenticación no se implementa utilizando el sistema de identidad incorporado, sino que se extrae en una capa separada de middleware. Sin embargo, ASP.NET Core tiene su propio mecanismo para proporcionar credenciales. Se pueden encontrar más detalles en la documentación
aquí .
En cuanto a la protección de datos, ya no usamos
MachineKey . En cambio, utilizamos la función de protección de datos incorporada. Por defecto, las claves se generan cuando se inicia la aplicación. El almacén de datos puede ser:
- Sistema de archivos: almacén de claves basado en el sistema de archivos
- Azure Storage: claves de protección de datos en Azure Blob Storage
- Redis: claves de protección de datos en el caché de Redis
- Registro: debe usarse si la aplicación no tiene acceso al sistema de archivos
- EF Core: las claves se almacenan en la base de datos
Si los mecanismos integrados no son adecuados, puede especificar su propio mecanismo de almacenamiento de claves proporcionando un
IXmlRepository personalizado.
Paso 14. Actualización de JS / CSS
La forma de trabajar con recursos estáticos ha cambiado: ahora todos deben almacenarse en la carpeta raíz del proyecto
wwwroot , a menos que, por supuesto, se especifiquen otras configuraciones.
Cuando utilice bloques incorporados de JavaScript, se recomienda que los mueva al final de la página. Simplemente use el atributo asp-location = "Footer" para sus etiquetas. La misma regla se aplica a los archivos js.
Use la extensión
BundlerMinifier como reemplazo de System.Web.Optimization: esto le permitirá vincular y minimizar JavaScript y CSS mientras construye el proyecto.
Enlace a la documentación.
Paso 15. Migrar vistas
Las acciones secundarias ya no se usan. En cambio, ASP.NET Core ofrece una nueva herramienta poderosa:
ViewComponents , que se llama de forma asincrónica.
Cómo obtener una cadena de ViewComponent:
Ya no es necesario usar HtmlHelper: ASP.NET Core tiene una gran cantidad de funciones de etiqueta de ayuda (
Tag Helpers ) integradas. Cuando la aplicación se está ejecutando, son procesados por el motor Razor en el lado del servidor y finalmente se convierten en elementos html estándar. Esto simplifica enormemente el desarrollo de aplicaciones. Y, por supuesto, puede implementar sus propios tag-helpers.
Comenzamos a usar la inyección de dependencia en vistas en lugar de permitir configuraciones y servicios usando
EngineContext
.
Entonces, los puntos principales sobre la migración de puntos de vista:
- Convierta
Views/web.config Views/_ViewImports.cshtml
, utilizado para importar espacios de nombres e Views/web.config Views/_ViewImports.cshtml
dependencias. Este archivo no es compatible con otras funciones de Razor
, como las definiciones de funciones y secciones. - Convierta
namespaces.add
a @using
- Transfiera cualquier configuración a la configuración de la aplicación principal
Scripts.Render
y Styles.Render
no existe. Reemplace libman
o BundlerMinifier
enlaces a la salida
En conclusión
Hemos visto por nuestra experiencia que el proceso de migrar una gran aplicación web es una tarea que requiere mucho tiempo y que difícilmente se puede llevar a cabo sin dificultades. Planeamos cambiar a un nuevo marco tan pronto como se lanzó su primera versión estable, pero no pudimos terminarlo de inmediato: había algunas funciones críticas que para ese momento aún no se habían transferido a .NET Core, en particular, relacionadas con EntityFramework. Por lo tanto, primero tuvimos que lanzar la próxima versión con un enfoque mixto: la arquitectura .NET Core con las dependencias de .NET Framework.
Pudimos adaptar completamente el proyecto después del lanzamiento de .NET Core 2.1, teniendo en ese momento una solución estable que ya funcionaba en la nueva arquitectura; todo lo que quedaba era reemplazar algunos paquetes y reescribir el trabajo con EF Core. Por lo tanto, la migración completa al nuevo marco tomó varios meses de trabajo.
Puede obtener más información sobre nuestro proyecto en nuestro
repositorio en GitHub .