Eseguire l'aggiornamento da ASP.NET Framework a ASP.NET Core

Perché eseguire l'aggiornamento alla versione più recente di .NET

ASP.NET Core è il framework Web moderno per .NET. Sebbene ASP.NET Core abbia molte analogie con ASP.NET in .NET Framework, è un nuovo framework completamente riscritto. ASP.NET le app aggiornate a ASP.NET Core possono trarre vantaggio da prestazioni migliorate e dall'accesso alle funzionalità e alle funzionalità di sviluppo Web più recenti.

approcci di aggiornamento di ASP.NET Framework

La maggior parte delle app ASP.NET Framework non semplici deve prendere in considerazione l'uso dell'approccio di aggiornamento incrementale. Per altre informazioni, vedere ASP.NET incrementale per ASP.NET aggiornamento core.

Per ASP.NET app MVC e API Web, vedere Informazioni su come eseguire l'aggiornamento da ASP.NET MVC e API Web a ASP.NET Core MVC. Per le app Web Form di ASP.NET Framework, vedere Informazioni su come eseguire l'aggiornamento da Web Forms ASP.NET a ASP.NET Core.

Modelli di app Web affidabili

Per indicazioni sulla creazione di un'app moderna, affidabile, affidabile, affidabile, testabile, conveniente e scalabile ASP.NET Core, indipendentemente dal fatto che si tratti di un'app esistente o di refactoring di un'app esistente, vedi Modello di app Web Reliable Web for.NETYouTube.

Differenze di decodifica URI tra ASP.NET e core ASP.NET

ASP.NET Core presenta le differenze di decodifica URI seguenti con ASP.NET Framework:

ASCII Encoded ASP.NET Core Framework ASP.NET
\ %5C \ /
/ %2F %2F /

Durante la %2F decodifica in ASP.NET Core:

  • L'intero percorso viene rimosso dall'escape, ad eccezione %2F del fatto che la conversione in / cambierebbe la struttura del percorso. Non può essere decodificato fino a quando il percorso non viene suddiviso in segmenti.

Per generare il valore per HttpRequest.Url, usare new Uri(this.AspNetCoreHttpRequest.GetEncodedUrl()); per evitare Uri l'interpretazione errata dei valori.

Migrazione dei segreti utente da ASP.NET Framework a ASP.NET Core

Vedere il problema in GitHub.

Questo articolo offre una guida di riferimento per la migrazione delle app ASP.NET ad ASP.NET Core.

Visual Studio include strumenti che consentono di eseguire la migrazione di app ASP.NET a ASP.NET Core. Per altre informazioni, vedere Migrazione da ASP.NET a ASP.NET Core in Visual Studio.

L'Assistente aggiornamento .NET è uno strumento della riga di comando che può essere utile per eseguire la migrazione di ASP.NET in ASP.NET Core. Per altre informazioni, vedere Panoramica dell'Assistente aggiornamento .NET e Aggiornare un'app ASP.NET MVC a .NET 6 con l'Assistente aggiornamento .NET.

Vedere l'eBook Conversione delle app ASP.NET esistenti in .NET Core per una guida completa alla conversione.

Prerequisiti

.NET Core SDK 2.2 o versione successiva

Framework di destinazione

I progetti di ASP.NET Core offrono agli sviluppatori la flessibilità necessaria per scegliere .NET Core, .NET Framework o entrambi come destinazione. Vedere Scelta di .NET Core o .NET Framework per le app server per determinare quale framework di destinazione è più appropriato.

Quando la destinazione è .NET Framework, i progetti devono fare riferimento a singoli pacchetti NuGet.

La scelta di .NET Core come destinazione consente di eliminare numerosi riferimenti espliciti ai pacchetti, grazie al metapacchetto di ASP.NET Core. Installare il metapacchetto Microsoft.AspNetCore.App nel progetto:

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

Quando si usa il metapacchetto, con l'app non viene distribuito alcun pacchetto a cui si fa riferimento nel metapacchetto. L'archivio di runtime di .NET Core include questi asset, che vengono precompilati per migliorare le prestazioni. Per altri dettagli, vedere Metapacchetto Microsoft.AspNetCore.All per ASP.NET Core.

Differenze di struttura del progetto

Il formato di file .csproj è stato semplificato in ASP.NET Core. Alcune modifiche importanti includono:

  • L'inclusione esplicita dei file non è necessaria affinché i file vengano considerati parte del progetto. In questo modo si riduce il rischio di conflitti di merge XML quando si lavora con team di grandi dimensioni.

  • Non sono presenti riferimenti basati su GUID ad altri progetti e questo migliora la leggibilità dei file.

  • Il file può essere modificato senza scaricarlo in Visual Studio:

    Edit CSPROJ context menu option in Visual Studio 2017]

Sostituzione di file Global.asax

In ASP.NET Core è stato introdotto un nuovo meccanismo per l'avvio automatico delle app. Il punto di ingresso per le applicazioni ASP.NET è il file Global.asax. Attività quali la configurazione della route e le registrazioni di area e filtro vengono gestite nel file Global.asax.

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

Con questo approccio l'applicazione e il server a cui viene distribuita vengono accoppiati in un modo che interferisce con l'implementazione. Al fine di disaccoppiare gli elementi, è stata introdotta la funzionalità OWIN che offre un modo più semplice di usare più framework insieme. OWIN offre una pipeline per aggiungere solo i moduli necessari. L'ambiente host accetta una funzione di avvio per configurare i servizi e la pipeline delle richieste dell'applicazione. Startup registra un set di middleware con l'applicazione. Per ogni richiesta, l'applicazione chiama ognuno dei componenti middleware con il puntatore iniziale di un elenco collegato a un set esistente di gestori. Ogni componente middleware può aggiungere uno o più gestori alla pipeline di gestione delle richieste. Questa operazione viene eseguita restituendo un riferimento al gestore che rappresenta il nuovo inizio dell'elenco. Ogni gestore è responsabile della memorizzazione e della chiamata del gestore successivo nell'elenco. Con ASP.NET Core, il punto di ingresso a un'applicazione è Startup e non esiste più una dipendenza da Global.asax. Quando si usa OWIN con .NET Framework, usare una pipeline simile alla seguente:

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);
        }
    }
}

Ciò consente di configurare le route predefinite e impostare come predefinito XmlSerialization per Json. Aggiungere altro middleware a questa pipeline in base alle esigenze, ad esempio caricamento di servizi, impostazioni di configurazione, file statici e così via.

ASP.NET Core usa un approccio simile, ma non si basa su OWIN per gestire la voce. L'operazione viene invece eseguita usando il metodo Program.csMain (simile alle applicazioni console) e Startup viene caricato da tale posizione.

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 deve includere un metodo Configure. In Configure aggiungere il middleware necessario alla pipeline. Nell'esempio seguente, estratto dal modello di sito Web predefinito, i metodi di estensione configurano la pipeline con il supporto per:

  • Pagine di errore
  • Protocollo HTTP Strict Transport Security (HSTS)
  • Reindirizzamento HTTP a HTTPS
  • ASP.NET Core MVC
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

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

L'host e applicazione sono stati disaccoppiati e questo offre la possibilità di passare a una piattaforma diversa in futuro.

Nota

Per un riferimento più completo relativo all'avvio e al middleware di ASP.NET Core, vedere Startup in ASP.NET Core (Operazioni iniziali in ASP.NET Core)

Configurazioni di archiviazione

ASP.NET supporta le impostazioni di archiviazione. Tali impostazioni vengono usate, ad esempio, per supportare l'ambiente in cui vengono distribuite le applicazioni. Una prassi comune era archiviare tutte le coppie chiave-valore personalizzate nella sezione <appSettings> del file Web.config:

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

Le applicazioni leggono queste impostazioni usando la raccolta ConfigurationManager.AppSettings nello spazio dei nomi System.Configuration:

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

ASP.NET Core è in grado di archiviare i dati di configurazione per l'applicazione in tutti i file e di caricarli come parte dell'avvio automatico del middleware. Il file predefinito usato nei modelli di progetto è appsettings.json:

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

Il caricamento del file in un'istanza di IConfiguration all'interno dell'applicazione viene eseguito in Startup.cs:

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

public IConfiguration Configuration { get; }

L'app legge da Configuration per ottenere le impostazioni:

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

Sono disponibili estensioni di questo approccio che aumentano l'efficacia del processo, ad esempio l'uso dell'inserimento delle dipendenze per caricare un servizio con questi valori. L'approccio con inserimento delle dipendenze offre un set fortemente tipizzato di oggetti di configurazione.

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

Nota

Per informazioni più dettagliate sulla configurazione di ASP.NET Core, vedere Configurazione in ASP.NET Core.

Inserimento delle dipendenze nativo

Un obiettivo importante nella compilazione di applicazioni scalabili di grandi dimensioni è l'accoppiamento libero di componenti e servizi. L'inserimento delle dipendenze è una tecnica comune che consente di raggiungerlo ed è un componente nativo di ASP.NET Core.

Nelle app ASP.NET gli sviluppatori si affidano a una libreria di terze parti per implementare l'inserimento delle dipendenze. Una di queste librerie è Unity e fa parte di Modelli e procedure Microsoft.

Un esempio di configurazione dell'inserimento delle dipendenze con Unity è l'implementazione di IDependencyResolver che esegue il wrapping di un oggetto 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();
    }
}

Creare un'istanza di UnityContainer, registrare il servizio e impostare il sistema di risoluzione delle dipendenze di HttpConfiguration sulla nuova istanza di UnityResolver per il contenitore:

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.
}

Inserire IProductRepository dove necessario:

public class ProductsController : ApiController
{
    private IProductRepository _repository;

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

    // Other controller methods not shown.
}

Poiché l'inserimento delle dipendenze fa parte di ASP.NET Core, è possibile aggiungere il servizio nel metodo ConfigureServices di Startup.cs:

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

Il repository può essere inserito in qualsiasi posizione, analogamente a Unity.

Nota

Per altre informazioni sull'inserimento delle dipendenze, vedere Inserimento di dipendenze.

Usare i file statici

Una parte importante dello sviluppo Web è la possibilità di distribuire asset statici sul lato client. Gli esempi più comuni di file statici sono HTML, CSS, Javascript e immagini. Questi file devono essere salvati nella posizione di pubblicazione dell'app (o della rete CDN) con riferimenti che ne consentano il caricamento da parte di una richiesta. Questo processo è stato modificato in ASP.NET Core.

In ASP.NET i file statici vengono archiviati in directory diverse e viene fatto riferimento ai file nelle viste.

In ASP.NET Core i file statici vengono archiviati nella "radice Web" (<radice contenuto>/wwwroot), a meno che la configurazione non sia diversa. I file vengono caricati nella pipeline delle richieste chiamando il metodo di estensione UseStaticFiles da Startup.Configure:

Nota

Se la destinazione è .NET Framework, installare il pacchetto NuGet Microsoft.AspNetCore.StaticFiles.

Ad esempio, un asset immagine nella cartella wwwroot/images è accessibile al browser in corrispondenza di una posizione come http://<app>/images/<imageFileName>.

Nota

Per informazioni più dettagliate sulla gestione dei file statici in ASP.NET Core, vedere File statici.

cookie multivalore

I cookie multivalore non sono supportati in ASP.NET Core. Creare un cookie per ogni valore.

I cookie di autenticazione non sono compressi in ASP.NET Core

Per motivi di sicurezza, i cookie di autenticazione non sono compressi in ASP.NET Core. Quando usano i cookie di autenticazione, gli sviluppatori dovrebbero ridurre il numero di informazioni sulle attestazioni incluse al minimo necessario per le proprie esigenze.

Migrazione parziale delle app

Un approccio alla migrazione parziale delle app consiste nel creare un'applicazione secondaria IIS e spostare solo determinate route da ASP.NET 4.x ad ASP.NET Core mantenendo la struttura dell'URL dell'app. Si consideri ad esempio la struttura dell'URL dell'app dal file applicationHost.config:

<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>

Struttura di directory:

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

[BIND] e formattatori di input

Le versioni precedenti di ASP.NET usavano l'attributo [Bind] per la protezione da attacchi di overposting. I formattatori di input funzionano diversamente in ASP.NET Core. L'attributo [Bind] non è più progettato per impedire l'overposting quando viene usato con formattatori di input per analizzare JSON o XML. Questi attributi influiscono sull'associazione di modelli quando l'origine dei dati è costituita da dati di modulo inseriti con il tipo di contenuto x-www-form-urlencoded.

Per le app che inseriscono informazioni JSON nei controller e usano formattatori di input JSON per analizzare i dati, è consigliabile sostituire l'attributo [Bind] con un modello di visualizzazione che corrisponda alle proprietà definite dall'attributo [Bind].

Risorse aggiuntive