Quoi de neuf dans .NET 10 pour ASP.NET Core

.NET 10 apporte des modifications significatives à ASP.NET Core. Dans cet article, nous passerons en revue certains des principaux changements, en couvrant des exemples pratiques du quotidien et les contextes dans lesquels ils s’intègrent le mieux.
.NET 10 est arrivé, apportant des améliorations importantes à ASP.NET Core en mettant l’accent sur l’efficacité et la productivité. Dans cet article, vous découvrirez les principales nouveautés : des applications à fichier unique écrites en C# aux nouvelles méthodes d’extension pour LINQ et EF Core, entre autres mises à jour qui promettent d’améliorer encore votre expérience de développement.
1. Nouvelles applications basées sur des fichiers C#
Malgré la prise en charge des instructions de niveau supérieur (introduites dans .NET 6), avant .NET 10, un projet traditionnel avec un fichier .csproj était toujours requis pour créer une application C#.
Avec l’arrivée de .NET 10, un nouveau modèle d’application basé sur des fichiers a été introduit, permettant la création d’une application complète en utilisant un seul fichier avec l’extension .cs, sans avoir besoin d’un fichier de projet distinct.
Regardons un exemple dans lequel nous devons créer une application qui appelle une API externe et renvoie les données utilisateur. Dans ce cas, nous pourrions procéder comme suit :
- Créez un fichier avec le
.csposte appeléGetUserData.cs. - Ouvrez le fichier avec votre éditeur de code préféré et insérez le code suivant :
using System.Net.Http;
using System.Threading.Tasks;
var url = "https://jsonplaceholder.typicode.com/users/1";
using var http = new HttpClient();
var response = await http.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine("API Response:");
Console.WriteLine(content);
Ce code appelle l’API JSON Placeholder pour renvoyer des exemples de données.
- Ouvrez un terminal où se trouve le fichier et exécutez la commande suivante :
dotnet run GetUserData.cs
Le résultat de la requête est alors affiché dans la console :

Dans cet exemple, on affiche uniquement les données dans la console, mais imaginez toutes les possibilités ! Avec un seul fichier .cs, nous pouvons créer rapidement des preuves de concept (POC), automatiser de petites routines ou même effectuer de simples tâches de mise à jour de données.
Un autre point important à souligner concernant les applications basées sur des fichiers est qu’elles prennent en charge les références aux packages SDK et NuGet via le #sdk et #package directives. Ajoutez-les simplement au début du fichier. Prenons l’exemple ci-dessous :
#:sdk Microsoft.NET.Sdk.Web
#:package Newtonsoft.Json@13.0.3
using Newtonsoft.Json;
var app = WebApplication.Create();
app.MapGet("/serialize", () =>
{
var user = new { Id = 1, Name = "Alice" };
return JsonConvert.SerializeObject(user, Formatting.Indented);
});
app.Run();
return;
Ici, nous avons un autre fichier appelé SerializeApp.cs qui contient un code simple pour renvoyer des données sérialisées. Il utilise le Microsoft.NET.Sdk.Web SDK et le Newtonsoft.Json@13.0.3 emballer. Notez que nous n’avons rien installé, nous avons uniquement utilisé les directives. Nous pouvons donc exécuter l’application avec la commande dotnet run SerializeApp.csfaites une demande à http://localhost:5000/serialize et voyez qu’il est 100% fonctionnel :


2. Membres d’extension
C# 14 est la dernière version de C#, publiée avec .NET 10, et a apporté plusieurs améliorations au langage, telles que les propriétés d’extension. Les propriétés d’extension étendent le concept de méthodes d’extension pour ajouter des propriétés aux types que vous ne contrôlez pas, sans avoir besoin de créer des wrappers ou un héritage artificiel. Prenons l’exemple ci-dessous :
public static class EnumerableExtensions
{
extension<T>(IEnumerable<T> source)
{
public bool IsEmpty => source.Any() == false;
public IEnumerable<T> Where(Func<T, bool> predicate)
{
if (predicate is null)
throw new ArgumentNullException(nameof(predicate));
return Iterator();
IEnumerable<T> Iterator()
{
foreach (var item in source)
{
if (predicate(item))
yield return item;
}
}
}
public static IEnumerable<T> Identity => Enumerable.Empty<T>();
public static IEnumerable<T> operator +(IEnumerable<T> first, IEnumerable<T> second) =>
first.Concat(second);
}
}
extension<T>(IEnumerable<T> source)
Ce bloc est la nouvelle portée de l’extension. Ici, source est traité comme l’objet récepteur, semblable à this dans des exemples d’extensions classiques. Cela signifie que toute propriété ou méthode définie dans ce bloc est perçue comme faisant partie de IEnumerable<T> lui-même.
- Propriété d’extension :
IsEmpty
IsEmpty est déclarée comme propriété d’instance attachée à IEnumerable<T>où source la collection est-elle en cours d’extension.
La propriété revient true lorsque la séquence ne contient aucun élément, équivalent à bool empty = !items.Any();seulement maintenant exposé en tant que propriété au lieu d’une méthode.
Exemple d’utilisation :
var items = new[] { 1, 2, 3 };
bool empty = items.IsEmpty;
- Méthode d’extension personnalisée : où
public IEnumerable<T> Where(Func<T, bool> predicate)
{
...
}
Cette méthode remplace conceptuellement la méthode traditionnelle. Enumerable Where()démontrant que les méthodes peuvent également être déclarées en tant que membres d’extension dans le nouveau bloc.
- Propriété d’extension statique : identité
public static IEnumerable<T> Identity => Enumerable.Empty<T>();
Il est désormais possible de créer des propriétés statiques dans la portée de l’extension. Le Identity La propriété renvoie toujours une chaîne vide du type correspondant.
Exemple d’utilisation :
var empty = EnumerableExtensions.Identity;
Notez que les propriétés statiques doivent toujours être accessibles par le type qui déclare l’extension ; ils ne deviennent pas des membres directs du type cible.
- Opérateur d’extension :
operator +
public static IEnumerable<T> operator +(IEnumerable<T> first, IEnumerable<T> second) =>
first.Concat(second);
C’est l’une des nouveautés les plus intéressantes : les opérateurs peuvent être définis comme des extensions, même lorsque le type d’origine ne les prend pas en charge. Cela vous permet d’écrire des opérations comme celle-ci :
var merged = new[] { 1, 2 } + new[] { 3, 4 };
3. Le mot-clé du champ
Le field Le mot-clé vous permet d’écrire le code d’une propriété sans avoir à créer manuellement le champ qui stocke sa valeur. Le compilateur crée ce champ automatiquement et field sert de référence à cette valeur interne.
Avant C# 14, lorsque vous souhaitiez empêcher une propriété de chaîne de recevoir la valeur null, par exemple, il était nécessaire de déclarer un champ privé et d’écrire manuellement les méthodes get et set à l’aide de ce champ. Avec fieldtout cela devient plus simple.
Comment c’était fait avant :
private int _quantity;
public int Quantity
{
get => _quantity;
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), "Quantity cannot be negative.");
_quantity = value;
}
}
Et maintenant, en utilisant le champ :
public int Quantity
{
get;
set => field = value >= 0
? value
: throw new ArgumentOutOfRangeException(nameof(value), "Quantity cannot be negative.");
}
Vous l’utilisez exactement comme n’importe quelle propriété normale.
Définition d’une valeur valide :
var item = new Product();
item.Quantity = 10;
Définition d’une valeur invalide :
item.Quantity = -5;
4. Affectation à l’aide de Null-Conditional
À partir de C# 14, vous pouvez attribuer des valeurs à l’aide de l’opérateur conditionnel nul ?:
order?.City = Normalize(city);
Avant .NET 10, pour effectuer une vérification nulle avec affectation, vous deviez procéder comme suit :
if (order != null)
{
order.City = Normalize(city);
}
order = order is null
? null
: (order.City = Normalize(city), order);
5. Validation intégrée minimale de l’API
Dans .NET 10, les API Minimal ont une validation intégrée, éliminant le besoin de bibliothèques externes ou de manuels. if blocs pour valider les DTO.
En validation intégrée, le modèle envoyé par le client est automatiquement validé dès qu’il atteint le point final, en utilisant les attributs de validation du System.ComponentModel.DataAnnotations espace de noms. S’il y a des erreurs dans le processus, .NET renvoie automatiquement 400 Bad Request avec des détails sur le problème.
Pour mettre en œuvre cela, procédez simplement comme suit :
Classe de programme
using System.ComponentModel.DataAnnotations;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddValidation();
var app = builder.Build();
app.MapPost("/users",
(CreateUserRequest user) =>
TypedResults.Created($"/users/{user.Username}", user)
);
app.Run();
Objet de transfert de données (DTO)
public class CreateUserRequest
{
[Required]
[MinLength(3)]
public string Username { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Range(1, 120)]
public int Age { get; set; }
}
Notez que pour implémenter la nouvelle validation intégrée, nous utilisons la méthode d’extension builder.Services.AddValidation();. Donc si nous exécutons la route POST – /users avec le corps suivant :
{
"username": "",
"email": "wrong-email",
"age": -1
}
Nous recevrons la réponse ci-dessous :
{
"title": "One or more validation errors occurred.",
"errors": {
"Username": [
"The Username field is required."
],
"Email": [
"The Email field is not a valid e-mail address."
],
"Age": [
"The field Age must be between 1 and 120."
]
}
}
Vous pouvez également désactiver la validation intégrée sur les points de terminaison à l’aide de l’outil .DisableValidation(); méthode d’extension :
app.MapPost("/users",
(CreateUserRequest user) =>
TypedResults.Created($"/users/{user.Username}", user)
).DisableValidation();
6. Nouvelles méthodes d’extension d’EF Core 10
.NET 10 a apporté quelques améliorations à Entity Framework Core 10. Parmi elles figurent les nouvelles méthodes d’extension, qui contribuent toujours à raccourcir le chemin. Voyons quelques-uns d’entre eux.
LeftJoin()etRightJoin()
Il est désormais possible d’utiliser directement .LeftJoin(...) et .RightJoin(...) dans les requêtes LINQ avec EF, au lieu d’avoir à utiliser l’ancien modèle avec GroupJoin + DefaultIfEmpty(). Le fournisseur EF Core traduit LEFT JOIN / RIGHT JOIN en SQL.
Les exemples ci-dessous montrent comment utiliser les deux méthodes :
GaucheRejoindre
var req = new CreateUserRequest
{
Username = "john",
Email = "john@email.com",
Age = 22,
};
var requested = new[] { req };
var result = requested
.LeftJoin(
db.Users,
req => req.Email,
user => user.Email,
(req, user) => new { Requested = req, ExistingUser = user }
)
.ToList();
return Results.Ok(result);
Même s’il n’y a aucun utilisateur dans la base de données, l’élément de gauche (CreateUserRequest) apparaît toujours.
Rejoindre à droite
var requests = new List<CreateUserRequest>
{
new()
{
Username = "bob",
Email = "bob@mail.com",
Age = 22,
},
};
var result = requests
.RightJoin(
db.Users,
req => req.Email,
user => user.Email,
(req, user) => new { Request = req, User = user }
)
.ToList();
Tous les utilisateurs de la base de données sont situés sur le côté « droit ». Même s’il n’y a pas de demande correspondante, l’utilisateur apparaît.
7. ExecuteUpdate et ExecuteUpdateAsync pour les colonnes JSON
EF Core 10 offre une prise en charge complète de la mise à jour des données JSON (lorsque la base de données le prend en charge, SQL Server 2025, Azure SQL par exemple). Cela vous permet de mapper des propriétés complexes sur des colonnes JSON.
Ainsi, il est possible d’utiliser ExecuteUpdate et ExecuteUpdateAsync pour mettre à jour directement les propriétés dans le JSON, sans avoir besoin de charger l’intégralité de l’entité en mémoire. Cela présente un avantage lors du stockage de données semi-structurées dans des colonnes JSON. L’exemple ci-dessous montre comment utiliser cette nouvelle fonctionnalité :
await db
.Users.Where(u => u.Email == request.Email)
.ExecuteUpdateAsync(setters =>
setters
.SetProperty(u => u.Username, request.Username)
.SetProperty(u => u.Age, request.Age)
);
8. Améliorations du mappage des types complexes, des structures et des types détenus
EF Core 10 étend la prise en charge des types complexes. Il est désormais possible de mapper des types composites (complexes) qui n’ont pas leur propre identité, en attribuant des propriétés complexes :
public class UserProfile
{
public string Bio { get; set; }
}
public class User
{
public int Id { get; set;}
public string Username { get; set; }
public string Email { get; set; }
public int Age { get; set; }
public UserProfile Profile { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ComplexProperty(u => u.Profile);
}
var complexUser = new User
{
Username = user.Username,
Email = user.Email,
Age = user.Age,
Profile = new UserProfile { Bio = $"Account for {user.Username}" },
};
db.Add(user);
await db.SaveChangesAsync();
Notez que nous avons informé EF que l’entité User possède une propriété complexe (Profile) via la méthode d’extension : .ComplexProperty(u => u.Profile);.
9. Filtres de requête nommés
Il est désormais possible de définir plusieurs filtres globaux (filtres de requêtes) pour une entité, chacun avec son propre nom, puis d’activer ou de désactiver des filtres spécifiques dans les requêtes. Cela vous permet d’avoir plusieurs filtres logiques différents et de contrôler lequel appliquer à chaque requête :
modelBuilder.Entity<User>().HasQueryFilter("MinAgeFilter", u => u.Age >= 18);
modelBuilder
.Entity<User>()
.HasQueryFilter("EmailDomainFilter", u => u.Email.EndsWith("@company.com"));
Ignorer les filtres :
var adults = db.Users.IgnoreQueryFilters(["EmailDomainFilter"]).ToList();
Conclusion
.NET 10 a apporté plusieurs améliorations à ASP.NET Core, rendant des fonctionnalités telles que EF Core encore plus polyvalentes. De plus, l’une des nouvelles fonctionnalités les plus attendues concernait les applications basées sur des fichiers, qui vous permettent d’exécuter du code C# en utilisant uniquement un fichier .cs.
Dans cet article, nous avons couvert les points principaux avec des exemples pratiques à utiliser au quotidien. J’espère que ces nouvelles fonctionnalités vous aideront à mettre en œuvre des solutions plus simples, plus modernes et plus efficaces dans vos projets.
Source link
