CRUD ASP.NET Core MVC avec MYSQL
Apprenez les opérations CRUD sur ASP.NET Core MVC avec MySQL. Tutorial complet avec Entity Framework Core, Data Seed et déploiement en production.

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. À 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 environnement 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 :
Création du projet
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.
Installation des packages
Installons les packages nécessaires pour notre projet :
Création du modèle Contact
Créons notre modèle Contact dans le dossier Models :
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; }Configuration de la base de données
DataContext
Créons notre DataContext :
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Contact>();
}Seed Data
Créons aussi notre classe Seed pour générer des données de test :
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();
}
}Configuration de la connexion MySQL
Dans le fichier appsettings.json, ajoutons notre chaîne de connexion :
"ConnectionStrings": {
"DefaultConnection": "server=localhost; port=3306; database=crud_mvc; user=root;"
},Dans Startup.cs, configurons Entity Framework avec MySQL :
string mySqlConnectionStr = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<DataContext>(opt => opt.UseMySql(mySqlConnectionStr, ServerVersion.AutoDetect(mySqlConnectionStr)));Configuration du Program.cs
Modifions Program.cs pour initialiser la base de données :
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();
}Migration de la base de données
Créons notre première migration :
Contrôleur Contact
Créons notre contrôleur avec toutes les opérations 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");
}
}Vues Razor
Layout principal
Modifions le layout principal pour inclure Bootstrap Icons :
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css"><!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">
© 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>Configuration de la route par défaut
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Contact}/{action=Index}/{id?}");
});Vue Create.cshtml
@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" />
}
}Vue Edit.cshtml
@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" />
}
}
Test de l'application
Déploiement en production
Configuration pour la production
Pour le déploiement en production, nous devons modifier certaines configurations :
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?}");
});
}Configuration appsettings.json pour production
"ConnectionStrings": {
"DefaultConnection": "server=***host***; port=3306; database=*******; user=******; password=*******;"
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://testlinode.com:5002"
},
"Https": {
"Url": "https://testlinode.com:5000"
}
}
},Configuration Nginx
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;
}
}Service systemd
Configuration du service pour maintenir l'application en vie :
[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.targetTest en production
Testons notre application déployée sur https://testlinode.com
Pour plus de détails sur le déploiement, consultez l'article : Comment déployer .net 5 api sur linux
Conclusion
Nous avons créé une application CRUD complète avec ASP.NET Core MVC et MySQL incluant :
✅ Modèle Contact avec validations ✅ Entity Framework Core pour l'accès aux données ✅ CRUD complet (Create, Read, Update, Delete) ✅ Interface Bootstrap responsive ✅ Data Seed pour les données de test ✅ Migration automatique de la base de données ✅ Déploiement en production avec Nginx et SSL
Cette application constitue une base solide pour des projets plus complexes utilisant ASP.NET Core MVC.
Le code complet est disponible sur GitLab.
#AspNetCore #MVC #CRUD #MySQL #EntityFramework #DotNet #CSharp #VisualStudio