Entity Framework Core pour IBM i avec NTi

Vue d'ensemble

Entity Framework Core est l'ORM de référence dans l'écosystème .NET. Il permet de manipuler une base de données directement depuis des objets C#, sans écrire de SQL manuellement : les requêtes, les migrations et le mapping sont gérés automatiquement.

Grâce à l'extension EF Core de NTi, cette approche s'applique nativement à DB2 for i sur IBM i. Compatible avec .NET 8, .NET 9 et .NET 10, les développeurs .NET peuvent travailler avec une base IBM i exactement comme avec n'importe quelle autre base de données supportée par EF Core, avec le même outillage, les mêmes conventions et les mêmes commandes.

  • Code First ou DB First : partez de zéro avec des entités C# et laissez EF Core créer les tables, ou générez vos entités depuis une base DB2 for i existante.
  • LINQ vers SQL : écrivez vos requêtes en C#, EF Core les traduit automatiquement en SQL optimisé pour DB2 for i.
  • Migrations automatiques : faites évoluer votre schéma en modifiant vos entités, EF Core génère et applique les changements en base automatiquement.
  • Database-agnostic : la même syntaxe et le même code fonctionnent sur DB2 for i comme sur SQL Server, PostgreSQL ou Oracle.
  • Multiplateforme : déployez sur Windows, Linux, macOS, Docker...

Par exemple, cette requête LINQ écrite en C# côté .NET :

var result = db.Orders.Where(x => x.Products.Name.Contains(filter));

Est automatiquement traduite en SQL par EF Core :

SELECT o.ORDERID, o.PRODUCTID, o.QUANTITY, o.ORDERDATE
FROM ORDERS AS o
INNER JOIN PRODUCTS AS p ON o.PRODUCTID = p.PRODUCTID
WHERE POSITION(@filter, p.NAME) > 0

💡Cette documentation ne couvre pas en détails les concepts généraux d’entity Framework Core, qui sont standardisé. Pour les notions avancées et les bonnes pratiques d’EF Core, consultez la documentation officielle de Microsoft.


Prérequis et installation

Prérequis

  • .NET SDK 8, 9 ou 10
  • Visual Studio 2022 ou équivalent.
  • Une base DB2 for i accessible.

Installation

Ajoutez le package NuGet Aumerial.EntityFrameworkCore via l'interface de gestion de packages NuGet dans Visual Studio, ou via la ligne de commande :

dotnet add package Aumerial.EntityFrameworkCore

Importez ensuite l'espace de noms dans votre projet :

using Aumerial.EntityFrameworkCore;

Microsoft.EntityFrameworkCore.Design est requis pour les migrations et le scaffolding. Utilisez la version correspondant à votre version .NET pour éviter toute incompatibilité.


Configuration du DbContext

Créez une classe héritant du DbContext, qui définira les entités et les configurations de votre base :

using Microsoft.EntityFrameworkCore; 

public class AppDbContext : DbContext 
{ 
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } 
    
    // Ajoutez vos DbSet ici 
    public DbSet<MyEntity> MyEntities  { get; set; } 
}

Dans un projet web, enregistrez votre DbContext dans Program.cs via l'injection de dépendances :

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");

builder.Services.AddDbContextFactory(options =>
    options.UseNTi(connectionString));

Pour les tâches asynchrones ou les services en arrière-plan, utilisez AddDbContextFactory.


Gestion des types de données spécifiques

Support des types BLOB

Stockez des fichiers ou des images directement dans DB2 for i en utilisant des colonnes de type BLOB :

[Column(TypeName = "BLOB(1M)"), DataType(DataType.Upload)]
public byte[] ProfilePhoto { get; set; }

Configuration globale des longueurs

Définissez une longueur par défaut pour les types varchar, varbinary et vargraphic lors de l'enregistrement du DbContext :

builder.Services.AddDbContextFactory<AppDbContext>(options =>
    options.UseNTi(connectionString, opt => opt.VarfieldMaxLength(1024, 256, 512)));
  • 1024 : longueur par défaut pour VARCHAR
  • 256 : pour VARBINARY
  • 512 : pour VARGRAPHIC

Vous pouvez également spécifier la longueur directement sur vos entités avec l'attribut [MaxLength] :

[Column(TypeName = "VARCHAR"), MaxLength(128)]
public string Description { get; set; }

Création des entités et migrations (Code First)

Choisir le schéma

Ajoutez un schéma par défaut dans votre chaîne de connexion pour que toutes les tables créées y soient placées :

server=Server;user=User;password=Pwd;database=MYSCHEMA;

Pour placer certaines tables dans des schémas spécifiques, configurez-les explicitement dans le DbContext :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>().ToTable("Product", "MYSCHEMA");
    modelBuilder.Entity<Order>().ToTable("Order", "MYSCHEMA");
}

Définir les entités

Créez des classes pour représenter vos tables :

public class Order
{
    public int Id { get; set; }
    public DateTime OrderDate { get; set; }
    public decimal TotalAmount { get; set; }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Order> Orders { get; set; }
}

Ajoutez ces entités dans le DbContext :

public DbSet<Order> Orders { get; set; }
public DbSet<Product> Products { get; set; }

Générer et appliquer une migration

Créez une migration initiale :

dotnet ef migrations add InitialCreate

Appliquez la migration pour créer les tables dans DB2 for i :

dotnet ef database update

Pour ajouter ou modifier une table, modifiez l'entité concernée, puis générez une nouvelle migration :

dotnet ef migrations add NomDeLaMigration
dotnet ef database update

Pour supprimer la dernière migration avant son application :

dotnet ef migrations remove

Pour revenir à une version antérieure de la base :

dotnet ef database update NomDeLaMigration

Remplacez NomDeLaMigration par le nom de la migration vers laquelle vous souhaitez revenir, par exemple InitialCreate.


Récupération des entités depuis une base existante (DB First)

Pour générer les entités depuis une base DB2 for i existante, créez d'abord un DbContext vide :

public class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { }
}

Exécutez ensuite la commande de scaffold :

dotnet ef dbcontext scaffold "server=myserver;user=myuser;password=mypassword;database=mydb" Aumerial.EntityFrameworkCore --output-dir Data/Models --schema mydb
  • --output-dir : emplacement des entités générées (ex. Data/Models)
  • --schema : schéma à récupérer (ex. myschema)

Si aucune migration n'a encore été créée, générez une migration initiale vide :

dotnet ef migrations add InitialCreate

Videz les méthodes Up et Down dans le fichier généré pour éviter toute modification accidentelle des tables existantes :

protected override void Up(MigrationBuilder migrationBuilder)
{
    // Laissez vide
}

protected override void Down(MigrationBuilder migrationBuilder)
{
    // Laissez vide
}

Cela évite de supprimer ou recréer accidentellement des tables existantes lors de l'application des migrations.

Pour ajouter une nouvelle table, créez l'entité correspondante, ajoutez-la dans le DbContext, puis générez et appliquez une migration :

public class MyNewTable
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime CreatedAt { get; set; }
}
public DbSet<MyNewTable> MyNewTables { get; set; }
dotnet ef migrations add AddMyNewTable
dotnet ef database update

Et maintenant ?