Dans cet article, nous allons apprendre les opérations CRUD (Create Read Update Delete) sur ASP.NET Core MVC (V 5.0). Nous utiliserons un Data Seed pour générer des fakes data et aussi nous utiliserons Entity Framework Core pour interagir avec la base de données MYSQL.A la fin du tutorial le code sera disponible sur mon repo gitlab : https://gitlab.com/eroamba/crud-dotnet-core-mvc-avec-mysql

Avant de continuer, je vais vous donner une brève introduction à .NET 5.0 MVC.

Attention avant de commencer ne pas confondre ASP.NET MVC et ASP.NET Core Mvc il y a une différence et je vous explique

D’abord Asp.net Core est une nouvelle version d'Asp.net publiée par Microsoft.  C’est un framework open-source et peut être exécuté sur différent système comme Windows, Mac ou Linux.  Il a été lancé à l'origine en tant qu'ASP.NET 5, mais il a ensuite été renommé ASP.NET Core et porte toujours le même nom.

Donc ASP.NET CORE est multi plateforme (Windows, Linux, MAC) par contre ASP.NET était que pour environemment windows .

Pour plus d’information voici un lien complet : ici

ASP.NET Core MVC est un framework qui permet de concevoir des applications web en utilisant le design pattern MVC(Model-View-Controller)

‌    

Pour ce tutoriel on utilisera une table Contact avec les différents champs suivants :

‌                                                              

Pour débuter nous allons créer notre Solution (projet) avec Visual studio 2019 pour faire plus simple. Mais je montrerai aussi dans un autre tutoriel comment créer une solution .net core mvc avec Visual Studio Code qui est différent de Visual studio.

‌                                                              

Choisissons notre type de projet qui ASP.NET Core (Model-View-Controller)

‌                                                              

Suivons les étapes et choisissons un nom pour notre projet CRUD MVC

Super notre solution est créée avec les différents éléments du projet.

‌                                                              

Avant de commencer nous lançons notre projet

‌                                                              

Pour les personnes ayant activé https, ce message s’affichera pour la première fois.

Il faudra valider oui deux fois suivant.

‌                                                              

Voici notre page par défaut du projet

‌                                                              

Je vous explique l’architecture de base d’un projet ASP.Net Core mvc

· Dependencies: il contient tous les packages NuGet installés. Nous pouvons gérer les packages NuGet en cliquant dessus avec le bouton droit de la souris.

· Properties: Il contient le fichier launchSettings.json qui a des profils Visual Studio, iis et un paramètre de débogage.

· wwwroot folder: Il s'agit du dossier racine Web de l'application principale asp.net où nous pouvons mettre tous les fichiers statiques tels que javascript , css , images.

· Controllers:  il contient toutes les classes de contrôleurs que nous créons dans notre application asp.net core mvc.

· Models: nous pouvons mettre tous les modèles ou afficher les classes de modèles dans ce dossier.

· Views: nous pouvons ajouter des vues pour certaines méthodes d'action dans le dossier de vue. Il y aura un dossier séparé pour chaque vue que nous créons dans le dossier Vues.

· appsettings.json: c'est le fichier de configuration de l'application qui est utilisé pour stocker les paramètres de configuration, c'est-à-dire les chaînes de connexion de la base de données, les variables globales, etc.

· Program.cs : Initialement, l'application principale asp.net démarre en tant qu'application console. Dans la méthode Main , il appelle la méthode CreateWebHostBuilder()  qui configure le paramètre principal asp.net et le lance en tant qu'application principale asp.net.

· Startup.cs:   il contient les méthodes ConfigureServices() et Configure. Comme son nom l'indique, la méthode ConfigureServices() configure tous les services qui seront utilisés par l'application. La méthode Configure prend en charge tous les pipelines de traitement des demandes.

Avant de débuter le code je vous propose d’installer nos différents package.‌‌Pour ce tuto j’ai déjà listé tous les packages avec les versions que nous aurons à utiliser dans le but de faciliter le tutoriel.

Microsoft.EntityFrameworkCore 5.0.12

Microsoft.AspNetCore.Mvc.NewtonsoftJson 5.0.12

Microsoft.EntityFrameworkCore.Design 5.0.12

Microsoft.EntityFrameworkCore.Tools 5.0.12

Microsoft.VisualStudio.Web.CodeGeneration.Design 5.0.2

MySql.Data.EntityFrameworkCore 8.0.22

MySql.EntityFrameworkCore 5.0.8

Pomelo.EntityFrameworkCore.MySql 5.0.3

‌                                                              

Listons nos packages dans la barre de recherche et choisissons les versions

‌                                                              

Dans notre fichier model créons une class Contact et ajoutons le code suivant.

‌                                                              

public int Id { get; set; }
[Required(ErrorMessage = "Le nom est requis.")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Le prénom est requis.")]
public string LastName { get; set; }
[Required(ErrorMessage = "Le numéro de téléphone est requis.")]

public string Phone { get; set; }

public string Email { get; set; }

‌                                  

Créons notre DataContext

La classe DataContext est une classe qui agit comme un conduit entre notre base de données et les classes d’entité. La classe DataContext contient les informations de chaîne de connexion et les méthodes de connexion à une base de données et de manipulation des données dans la base de données.

‌                                                              

Ajoutons ce code à notre class

public DataContext(DbContextOptions options) : base(options)
        {

        }
        public DbSet<Contact> Contacts { get; set; }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            builder.Entity<Contact>();

        }
}

Ajoutons aussi un Seed Data à notre projet.

L'idée générale d'une Data Seed est d'initialiser les données test dans la base de données. Ces données sont souvent des données de test, mais peuvent également être des données de référence telles que type de genre connus ou type d’élément déjà connu à l’avance, etc.

Ajoutons le code suivant dans notre class Seed.

public static async Task SeedData(DataContext context)
        {

            var contacts = new List<Contact>{
                new Contact{
                     Id=1,
                     FirstName="Roamba",
                     LastName="Windlassida Epaphras",
                     Phone="+225 0103601135",
                     Email="eroamba@yahoo.fr",
                },
                new Contact{
                     Id=2,
                     FirstName="Djama",
                     LastName="Lemec",
                     Phone="+225 0779157835",
                     Email="djama@gmail.com",
                },

            };

            foreach (var item in contacts )
            {
                 context.Contacts.Add(item);
            }

             await context.SaveChangesAsync();
        } 
        }

‌      

Ici nous utilisons un async Task qui représente généralement une tâche asynchrone

‌                                                              

Ajoutons les éléments de configuration pour notre base de données.

Dans notre fichier appsetings.json

"ConnectionStrings": {
    "DefaultConnection": "server=localhost; port=3306; database=crud_mvc; user=root;"
  },

‌          

Ajoutons notre connectionString à la configuration de notre projet dans le Startup de notre projet.

string mySqlConnectionStr = Configuration.GetConnectionString("DefaultConnection");
            services.AddDbContext<DataContext>(opt => opt.UseMySql(mySqlConnectionStr, ServerVersion.AutoDetect(mySqlConnectionStr)));

‌                                          

N’oublions pas aussi la configuration de notre Program pour permettre la génération de notre seed (données test) lors du lancement du projet.

public static async Task Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            using var scope = host.Services.CreateScope();

            var services = scope.ServiceProvider;
            try
            {
                var context = services.GetRequiredService<DataContext>();

                await context.Database.MigrateAsync();
                await Seed.SeedData(context);
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred during migration");

            }

            await host.RunAsync();

        }

‌              

Activons notre service ou port mysql.

Pour ce tutorial j’utilise xampp pour la base donnée mysql.

‌                                                              

Créons une migration :la migration permet de générer les tables de la base de données à partir du model de class

Add-Migration InitialCreate

Update-Database

‌                                                              

‌                                                              

Relançons notre projet et vérifions notre base de données mysql.

‌                                                              

Nous constatons notre fake data existe maintenant dans notre base.

‌                                                              

Nous allons débuter par la lecture de nos données. CRUD

Créons notre controller contact et ajoutons le code suivant pour gérer notre CRUD.

‌                                                              

public class ContactController : Controller
    {
        private readonly ILogger<ContactController> _logger;
        private readonly DataContext _db;

        public ContactController(ILogger<ContactController> logger, DataContext db)
        {
            _db = db;
            _logger = logger;

        }

        public async Task<IActionResult> Index()
        {

            var result = await _db.Contacts.ToListAsync();

            return View(result);
        }


        public IActionResult Create()
        {

            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create(Contact contact)
        {
            if (ModelState.IsValid)
            {
                _db.Contacts.Add(contact);
                await _db.SaveChangesAsync();
                TempData["success"] = "Création effectuée avec succès";
                return RedirectToAction("Index");
            }
            return View(contact);
        }

        public async Task<IActionResult> Edit(int? id)
        {

            if (id == null || id == 0)
            {
                return NotFound();
            }

            var contact = await _db.Contacts.FindAsync(id);

            if (contact == null)
            {
                return NotFound();
            }


            return View(contact);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(Contact contact, int id)
        {
            if (id != contact.Id)
            {
                return NotFound();
            }
            if (ModelState.IsValid)
            {
                _db.Contacts.Update(contact);
                await _db.SaveChangesAsync();
                TempData["success"] = "Mise à jour effectuée avec succès";
                return RedirectToAction("Index");
            }
            return View(contact);
        }

        public async Task<IActionResult> Delete(int? id)
        {

            if (id == null || id == 0)
            {
                return NotFound();
            }

            var contact = await _db.Contacts.FindAsync(id);

            if (contact == null)
            {
                return NotFound();
            }

            _db.Contacts.Remove(contact);
            await _db.SaveChangesAsync();
            TempData["success"] = "Suppresson effectuée avec succès";
            return RedirectToAction("Index");



        }
    }

‌                  

Maintenant créons notre VIEW pour afficher nos données.

Sélectionnons le nom de notre fonction Index faisons click droit. Et ajoutons une correspondant à la fonction.

‌                                                              

‌                                                              

‌                                                              

Attribuons le nom par défaut qui est index.cshtml

Ajoutons aussi le CDN bootstrap icon dans notre _layout.cshtml principal

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">

‌          

Modifions notre fichier _layout.cshtml

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - CRUD_MVC</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">

    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Contact" asp-action="Index">Contact 2.0</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2022 - eroambahub.com - <a asp-area="" asp-controller="Contact" asp-action="Index">CRUD ASP.NET Core</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

‌ Modifions notre MapControllerRoute dans le fichier startup.cs    

app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Contact}/{action=Index}/{id?}");
            });

       

Relançons notre projet

‌                                                              ‌

Créons notre formulaire pour faire notre Create ou ajout de contact

Nous procédons de la même manière

Nous sélectionnons notre create dans le controller et nous ajoutons une page razor create

Comme le code suivant :

@model Contact;
@{
    ViewData["Title"] = "Create";
}

<form method="post">
    <div class="form-group">
        <label asp-for="FirstName">Nom</label>
        <input asp-for="FirstName" type="text" class="form-control" placeholder="Veuillez saisir votre nom">
        <span asp-validation-for="FirstName" class="form-text text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="LastName">Prénoms</label>
        <input asp-for="LastName" type="text" class="form-control" placeholder="Veuillez saisir votre nom">
        <span asp-validation-for="LastName" class="form-text text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Phone">Téléphone</label>
        <input asp-for="Phone" type="text" class="form-control" placeholder="Veuillez saisir votre numéro de téléphone">
        <span asp-validation-for="Phone" class="form-text text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Email">Email address</label>
        <input asp-for="Email" type="email" class="form-control" placeholder="Veuillez saisir votre numéro Email">
        <span asp-validation-for="Email" class="form-text text-danger"></span>
    </div>
    <button type="submit" class="btn btn-primary" value="Create">Ajouter</button>
</form>

@section Scripts{

    @{
        <partial name="_ValidationScriptsPartial" />
    }

}

‌                  ‌

Nous pouvons modifier maintenant un contact avec Update

Nous utilisons Edit dans notre controller pour créer notre view et nous ajoutons le code suivant

@model Contact;
@{
    ViewData["Title"] = "Edit";
}

<form method="post">
    <div class="form-group">
        <label asp-for="FirstName">Nom</label>
        <input asp-for="FirstName" type="text" class="form-control" placeholder="Veuillez saisir votre nom">
        <span asp-validation-for="FirstName" class="form-text text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="LastName">Prénoms</label>
        <input asp-for="LastName" type="text" class="form-control" placeholder="Veuillez saisir votre nom">
        <span asp-validation-for="LastName" class="form-text text-danger"></span>
    </div>
    <div class="form-group">
        <label asp-for="Phone">Téléphone</label>
        <input asp-for="Phone" type="text" class="form-control" placeholder="Veuillez saisir votre numéro de téléphone">
        <small asp-validation-for="Phone" class="form-text text-danger"></small>
    </div>
    <div class="form-group">
        <label asp-for="Email">Email address</label>
        <input asp-for="Email" type="email" class="form-control" placeholder="Veuillez saisir votre numéro de téléphone">
        <small asp-validation-for="Email" class="form-text text-danger"></small>
    </div>
    <button type="submit" class="btn btn-primary" value="Create">Modifier</button>
</form>

@section Scripts{

    @{
        <partial name="_ValidationScriptsPartial" />
    }

}

‌                  

Pour effectuer notre Delete nous allons utiliser ici un modal de confirmation qui est déjà dans notre fichier index de la VIEW Contact.

‌                                                              

‌                                                              

Notre CRUD marche super bien.

Code source disponible sur mon repo en ligne gitlab : https://gitlab.com/eroamba/crud-dotnet-core-mvc-avec-mysql

Pour ce tutorial vous aurez les éléments de connectionString en local sur mon repo mais normalement l’éléments doit être ignoré par gitignore car les informations d’une base données son confidentiel.

Nous déploierons ensemble notre projet sur une machine Ubuntu avec une base de données mysql en ligne

Avant de mettre notre projet en ligne effectuons quelque configuration.

Modifions notre Program.cs .ET ajoutons le nom de domaine de serveur linode en ligne :

https://testlinode.com

Remplaçons notre ancien IHostBuilder par celui ci

‌                   public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { //webBuilder.UseStartup<Startup>(); webBuilder.UseStartup<Startup>().UseUrls("https://testlinode.com:5000;http://testlinode.com:5002").UseKestrel(); });              

Dans notre fichier Startup.cs remplaçons fonction Configure par :

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

          
            app.UseStaticFiles();

            app.UseRouting();

            app.UseForwardedHeaders(new ForwardedHeadersOptions
            {
                ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            });

            app.UseAuthorization();


            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Contact}/{action=Index}/{id?}");
            });
        }

Remplaçons aussi les éléments de connexion à la base de données et ajoutons les différents endpoints pour kestrel. Dans notre fichier

"ConnectionStrings": {
    "DefaultConnection": "server=***host***; port=3306; database=*******; user=******; password=*******;"
  },
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://testlinode.com:5002"
      },
      "Https": {
        "Url": "https://testlinode.com:5000"
      }
    }
  },

‌                              

Buildons le projet

eroam@DESKTOP-UGTVO1F MINGW64 ~/source/repos/CRUD MVC (main)$ dotnet publish -c production

‌Utilisons Filezilla pour déployer notre projet en ligne

Créons un dossier sur notre machine Ubuntu crudmvc

Pour ce qu’ils veulent savoir comment configurer nginx dans les détails vous pouvez suivre mon article suivant : Comment déployer .net 5 api sur linux Ubuntu 20.04 (Nginx) -Linode (Partie1)

Configurons nginx pour permettre notre déploiement

epa@localhost:~/chretien$ sudo vim /etc/nginx/sites-available/default

server {
listen 80;
server_name testlinode.com;
return 301 https://$host$request_uri;
}
server {
listen 443;
server_name testlinode.com;
ssl_certificate /etc/letsencrypt/live/www.testlinode.com/fullchain.pem;
ssl_certificate_key       /etc/letsencrypt/live/www.testlinode.com/privkey.pem;
ssl on;
ssl_session_cache  builtin:1000 shared:SSL:10m;
ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
gzip on;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_proxied any;
gzip_buffers 16 8k;
gzip_disable “MSIE [1-6].(?!.*SV1)”;
location / {
proxy_pass            https://178.79.168.173:5000;
proxy_http_version 1.1;
proxy_set_header   Upgrade $http_upgrade;
proxy_set_header   Connection keep-alive;
proxy_set_header   Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header   X-Forwarded-Proto $scheme;
}
}

epa@localhost:~$ sudo systemctl restart nginx

epa@localhost:~/crudmvc$ dotnet CRUD\ MVC.dll

Notre projet est bien disponible en ligne

Créons un service pour lancer notre projet en arrière-plan

epa@localhost:~/crudmvc$ sudo vim /etc/systemd/system/crud-mvc.service

[Unit]
Description=Example .NET Web API App running on Ubuntu
[Service]
WorkingDirectory=/home/epa/crudmvc
ExecStart=/usr/bin/dotnet /home/epa/crudmvc/'CRUD MVC.dll'
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
SyslogIdentifier=CRUDMVC
User=root
Environment=ASPNETCORE_ENVIRONMENT=Production
[Install]
WantedBy=multi-user.target

epa@localhost:~$ sudo systemctl enable crud-mvc.service

epa@localhost:~$ sudo systemctl start crud-mvc.service