Créer une application Blazor Server CRUD avec EF Core 8 et DB2 for i (IBM i)

Introduction

Ce tutoriel montre comment créer une application Blazor Server connectée à une base DB2 for i via l'extension Entity Framework Core de NTi, compatible avec .NET 8, .NET 9 et .NET 10.

L’objectif est de mettre en place un CRUD complet pour gérer des produits, catégories et commandes depuis une application web .NET moderne.

Cette approche permet d’utiliser EF Core sur IBM i pour simplifier l’accès aux données et accélérer le développement d’applications métiers.

Le projet présenté dans ce tutoriel utilise .NET 8.


Étape 1 - Créer et configurer le projet

Créez le projet depuis la ligne de commande :

dotnet new blazorserver -n myApp --framework net8.0
cd myApp

Ajoutez les packages nécessaires :

dotnet add package Aumerial.Data.NTi
dotnet add package Aumerial.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Design

Ajoutez la chaîne de connexion dans appsettings.json en spécifiant le schéma par défaut dans lequel toutes les entités créées seront placées :

{
    "ConnectionStrings": {
        "DefaultConnection": "server=Server;user=User;password=Pwd;database=Db"
    }
}

Étape 2 - Définir les entités

Créez un dossier Models et ajoutez les classes suivantes :

  • Category.cs

Une catégorie peut contenir plusieurs produits:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Product> Products { get; set; }
}
  • Product.cs

Un produit appartient à une catégorie et peut être lié à plusieurs commandes :

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public int StockQuantity { get; set; }
    public decimal Weight { get; set; } 
    public bool IsAvailable { get; set; }
    public int CategoryId { get; set; }
    public Category Category { get; set; }
    public ICollection<Order> Orders { get; set; } = new List<Order>();
}
  • Order.cs

Une commande peut contenir plusieurs produits :

public class Order
{
    public int Id { get; set; }
    public DateTime OrderDate { get; set; }
    public DateTime? DeliveryDate { get; set; }
    public decimal TotalAmount { get; set; } 
    public ICollection<Product> Products { get; set; }
}

Étape 3 - Configurer le DbContext

Ajoutez une classe AppDbContext héritant de DbContext pour gérer les entités et leurs relations:

using Microsoft.EntityFrameworkCore;

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

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

     protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
       
    }
}

Étape 4 - Configurer Program.cs

Configurez le DbContext dans Program.cs et enregistrez-le comme service via l'injection de dépendances pour vos composants Blazor.

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNTi(connectionString));

💡 AddDbContext est recommandé pour la plupart des applications Blazor Server. Le DbContext est instancié avec une durée de vie Scoped, recréé à chaque requête utilisateur. Utilisez AddDbContextFactory pour les tâches en arrière-plan ou les traitements multi-threads.


Étape 5 - Créer et gérer les migrations

Générez une migration initiale pour créer les tables dans la base de données. Cette commande va créer un fichier dans le dossier Migrations contenant les instructions SQL pour créer vos tables :

dotnet ef migrations add InitialCreate

Appliquez ensuite la migration :

dotnet ef database update

Visuel entités crées sur DB2 for i via ACS

Pour ajouter ou modifier une table, créez ou modifiez l'entité concernée, ajoutez-la dans AppDbContext si nécessaire, puis générez une nouvelle migration :

dotnet ef migrations add MyNewMigration
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 MigrationName

Remplacez MigrationName par le nom de la migration vers laquelle vous souhaitez revenir


Étape 6 - Ajouter un jeu de données initial

Ajoutez un jeu de données dans Program.cs après l'enregistrement des services :

using (var scope = app.Services.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService();

    var categories = new List
    {
        new Category { Name = "Electronics" },
        new Category { Name = "Books" },
        new Category { Name = "Home Appliances" },
        new Category { Name = "Fashion" },
        new Category { Name = "Toys" }
    };
    context.Categories.AddRange(categories);

    var products = new List
    {
        new Product { Name = "Smartphone", Price = 500, StockQuantity = 10, Category = categories[0], IsAvailable = true },
        new Product { Name = "Laptop", Price = 1200, StockQuantity = 5, Category = categories[0], IsAvailable = true },
        new Product { Name = "Washing Machine", Price = 300, StockQuantity = 8, Category = categories[2], IsAvailable = true },
        new Product { Name = "T-Shirt", Price = 20, StockQuantity = 50, Category = categories[3], IsAvailable = true },
        new Product { Name = "Children's Book", Price = 15, StockQuantity = 100, Category = categories[1], IsAvailable = true },
        new Product { Name = "Toy Car", Price = 30, StockQuantity = 20, Category = categories[4], IsAvailable = true },
        new Product { Name = "Microwave Oven", Price = 250, StockQuantity = 6, Category = categories[2], IsAvailable = true },
        new Product { Name = "Jeans", Price = 40, StockQuantity = 30, Category = categories[3], IsAvailable = true }
    };
    context.Products.AddRange(products);

    var orders = new List
    {
        new Order
        {
            OrderDate = DateTime.Now.AddDays(-10),
            DeliveryDate = DateTime.Now.AddDays(-7),
            TotalAmount = 750,
            Products = new List { products[0], products[1], products[3] }
        },
        new Order
        {
            OrderDate = DateTime.Now.AddDays(-5),
            DeliveryDate = DateTime.Now.AddDays(-3),
            TotalAmount = 600,
            Products = new List { products[4], products[5], products[6] }
        },
        new Order
        {
            OrderDate = DateTime.Now.AddDays(-2),
            DeliveryDate = null,
            TotalAmount = 290,
            Products = new List { products[2], products[7] }
        }
    };
    context.Orders.AddRange(orders);

    context.SaveChanges();
}

Étape 7 - Générer les pages CRUD

Visual Studio peut générer automatiquement les composants Razor CRUD pour chacune de vos entités en quelques clics.

  1. Clic droit sur le dossier Pages de votre projet Blazor Server
  2. Sélectionnez Ajouter > Nouvel élément généré automatiquement > Composants Razor avec Entity Framework (CRUD)
  3. Configurez les options :
    • Classe de Modèle : sélectionnez l'entité souhaitée (ex. Product)
    • Classe de DbContext : sélectionnez AppDbContext

Visual Studio génère automatiquement un ensemble de composants Razor CRUD dans un dossier dédié (ex. ProductPages) :

  • Index.razor - liste des enregistrements
  • Create.razor - formulaire d'ajout
  • Edit.razor - formulaire de modification
  • Details.razor - affichage du détail d'un enregistrement
  • Delete.razor - confirmation et suppression

Répétez l'opération pour chaque entité : Category, Order.


Étape 8 - Ajouter un champ image (BLOB)

Ajoutez un champ Image de type byte[] à l'entité Product :

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public int StockQuantity { get; set; }
    public decimal Weight { get; set; }
    public bool IsAvailable { get; set; }
    public int CategoryId { get; set; }
    public Category Category { get; set; }

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

Générez et appliquez la migration :

dotnet ef migrations add AddProductImage
dotnet ef database update

Modifiez ensuite les composants EditProduct et CreateProduct pour ajouter le champ d'upload :

<div class="mb-3">
    <label for="image" class="form-label">Image:</label>
    <InputFile id="image" OnChange="UploadFile" class="form-control" />
    @if (Product?.Image != null && Product.Image.Length > 0)
    {
        <p>Image actuelle :</p>
        <img src="data:image/jpeg;base64,@Convert.ToBase64String(Product.Image)" 
             style="max-width: 200px; max-height: 200px;" />
    }
    else
    {
        <p>Aucune image disponible</p>
    }
</div>

Et la méthode pour gérer l'upload:

    private async Task UploadFile(InputFileChangeEventArgs e)
    {
        var file = e.File;

        if (file != null)
        {
            using var memoryStream = new MemoryStream();
            await file.OpenReadStream().CopyToAsync(memoryStream);
            Product.Image = memoryStream.ToArray();
        }
    }

Illustration CRUD BLAZOR page produit

Et maintenant ?