Vous êtes sur la page 1sur 9

Module au choix : Programmation .NET/C# 2ATEL A.U.

2020/2021

TP : EF core avec des projets séparés


Part 1 – Mise en place de la solution:
1. Créer une solution nommé “MyFinance” et y ajouter 3projets :
- MyFinance.BL (Class Library) : correspond à la couche métier où vous placer les objets
métier correspondant à tous les objets spécifiques que vous allez manipuler
- MyFinance.DAL (Class Library) : correspond à la couche accès aux données (DbContext
and Migrations)
- MyFinance.Web (ASP.NET Core Web Application — MVC)

2. Ajouter les dépendances nécessaires pour chaque projet


- Le projet DAL fait référence au projet BL

- Le projet Web fait référence aux projets BL et DAL

Page 1 sur 9
Module au choix : Programmation .NET/C# 2ATEL A.U. 2020/2021

Part 2 – Le projet BL (Business Layer):


Etape 1 :
- Ajouter les classes schématisées dans le diagramme (Classes déjà implémentées)

Part 3 – Le projet DAL (Data Access Layer):


1. Ajouter les Packages NuGet avec le gestionnaire de packages NuGet
- « Microsoft.EntityFrameworkCore.SqlServer » et
- « Microsoft.EntityFrameworkCore.Tools » (pour la génération de fichier de migrations)

Page 2 sur 9
Module au choix : Programmation .NET/C# 2ATEL A.U. 2020/2021

NB :
- Il faut choisir la bonne version à installer
- Ie projet DAL doit être le projet de démarrage

2. Définir le DbContext
- Ajouter la classe ApplicationDbContext
public class ApplicationDbContext: DbContext
{

public DbSet<Category> Category { get; set; }


public DbSet<Product> Product { get; set; }
public DbSet<Provider> Provider { get; set; }
public DbSet<Biological> Biological { get; set; }
public DbSet<Chemical> Chemical { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
optionsBuilder.UseSqlServer(@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=MyFinanceDB; Integrated
Security=True;");
}

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
// One to Many
modelBuilder.Entity<Category>()
.HasMany(c => c.Products)
.WithOne(e => e.Category)
.OnDelete(DeleteBehavior.Cascade);

// Many to many
modelBuilder.Entity<ProductProvider>()
.HasKey(bc => new { bc.ProductId, bc.ProviderId });

modelBuilder.Entity<ProductProvider>()
.HasOne(bc => bc.Product)
.WithMany(b => b.ProductProviders)
.HasForeignKey(bc => bc.ProductId);

modelBuilder.Entity<ProductProvider>()
.HasOne(bc => bc.Provider)
.WithMany(c => c.ProductProviders)
.HasForeignKey(bc => bc.ProviderId);
}
}

Page 3 sur 9
Module au choix : Programmation .NET/C# 2ATEL A.U. 2020/2021

- Créer la base de données :


NuGet Package Manager Console → Update-Database

Partie 4 – Le Projet Web

1. Seeding la base de données:


- Ajouter La chaine de connexion sql dans appsettings.json du projet MyFinane.Web
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyFinanceDB;Trusted_Connection=True;
MultipleActiveResultSets=true"
}

- Modifier la classe MyFinanceDbContext

public MyFinanceDbContext(DbContextOptions<MyFinanceDbContext> options) : base(options)


{
}

/* protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
optionsBuilder.UseSqlServer(@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=MyFinanceDB;
Integrated Security=True;");
}*/

- Dans « Startup.cs ». Enregistrer le dbContext dans les services en ajout le code suivant :

public void ConfigureServices(IServiceCollection services)


{
services.AddDbContext<MyFinanceDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Page 4 sur 9
Module au choix : Programmation .NET/C# 2ATEL A.U. 2020/2021

2. Ajouter une classe qui implémente l’interface IDesignTimeDbContextFactory. Cette fabrique va


être utilisée par les outils EF pour créer une instance de votre DBContext. Le code de ce fichier est
le suivant :

using MyFinance.DAL;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System.IO;

namespace MyFinance.Web
{
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyFinanceDbContext>
{
public MyFinanceDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(@Directory.GetCurrentDirectory() + "/../MyFinance.Web/appsettings.json")
.Build();

var builder = new DbContextOptionsBuilder<MyFinanceDbContext>();


var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
return new MyFinanceDbContext(builder.Options);
}
}
}
- Ajouter la classe
public class SeedData
{
public static void Initialize(MyFinanceDbContext context)
{
if (!context.Category.Any())
{
var cats = new List<Category>()
{
new Category { Name = "Alimentaire" },
new Category { Name = "Cosmétique" },
new Category { Name = "Informatique" },
};
context.Category.AddRange(cats);
context.SaveChanges();
}

}
}

- Modifier la méthode Main du Program.cs


public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();

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


{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<MyFinanceDbContext>();
SeedData.Initialize(context);
}
catch (Exception)
{
Console.WriteLine("An error occurred while seeding the database.");
}
}
host.Run();
}
Page 5 sur 9
Module au choix : Programmation .NET/C# 2ATEL A.U. 2020/2021

- Exécuter l’application MyFinance.Web


- Actualiser la base de données

3. Les images
- Modifier la classe ¨Product en ajoutant l’annotation suivante :
[DataType(DataType.ImageUrl)]
[Display(Name = "Image")]
public string ImageName { get; set; }

- Dans Views/Products/Index.cshtml, ajoutez le code suivant :


<td>
@if (item.ImageName != null)
{
<img src="@Url.Content("~")/Images/@item.ImageName" style="width:100px; height:50px;" />
}
</td>

Partie 5 : Personnaliser les pages de « Product »


Quand une entité Produit est créée, elle doit avoir une relation avec une catégorie existant. Pour
faciliter cela, le code du modèle généré automatiquement inclut des méthodes de contrôleur, et des
vues Create et Edit qui incluent une liste déroulante pour sélectionner la catégorie. La liste déroulante
définit la propriété de clé étrangère Product.CategoryId, qui est tout ce dont Entity Framework a besoin
pour charger la propriété de navigation Category avec l’entité Category appropriée. Vous utilisez le code
du modèle généré automatiquement, mais que vous modifiez un peu pour ajouter la gestion des erreurs
et trier la liste déroulante.
1. Ajouter un contrôleur :

2. Dans ProductsController.cs, supprimez les quatre méthodes Create et Edit, et remplacez-les par
le code suivant :

Page 6 sur 9
Module au choix : Programmation .NET/C# 2ATEL A.U. 2020/2021

public IActionResult Create()


{
CategorysDropDownList();
return View();
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ProductID,Description,Name, Price, Quantity, ImageName,
CategoryD")] Product product)
{
if (ModelState.IsValid)
{
_context.Add(product);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
CategorysDropDownList(product.CategoryId);
return View(product);
}

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


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

var product = await _context.Product


.AsNoTracking()
.FirstOrDefaultAsync(m => m.ProductId == id);
if (product == null)
{
return NotFound();
}
CategorysDropDownList(product.CategoryId);
return View(product);
}

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}

var productToUpdate = await _context.Product


.FirstOrDefaultAsync(c => c.ProductId == id);

if (await TryUpdateModelAsync<Product>(productToUpdate,
"", c => c.Name, c => c.Description, c => c.Price, c => c.Quantity, c => c.CategoryId, c
=> c.ImageName, c => c.DateProd))
{
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction(nameof(Index));
}
CategorysDropDownList(productToUpdate.CategoryId);
return View(productToUpdate);
}

Page 7 sur 9
Module au choix : Programmation .NET/C# 2ATEL A.U. 2020/2021

Après la méthode HttpPost Edit, créez une méthode qui charge les informations des catégories pour la
liste déroulante.
private void CategorysDropDownList(object selectedCategory = null)
{
var categorysQuery = from d in _context.Category
orderby d.Name
select d;
ViewBag.CategoryId = new SelectList(categorysQuery.AsNoTracking(), "CategoryId", "Name",
selectedCategory);
}

La méthode CategorysDropDownList obtient une liste de toutes les catégories triées par nom, crée une
collection SelectList pour une liste déroulante et passe la collection à la vue dans ViewBag. La méthode
accepte le paramètre facultatif selectedCategory qui permet au code appelant de spécifier l’élément
sélectionné lors de l’affichage de la liste déroulante. La vue passe le nom « CategoryId » pour le tag
helper <select> : le helper peut alors rechercher dans l’objet ViewBag une SelectList nommée «
CategoryId ».
La méthode HttpGet Create appelle la méthode CategorysDropDownList sans définir l’élément
sélectionné, car pour un nouveau cours, le département n’est pas encore établi.
La méthode HttpGet Edit définit l’élément sélectionné, en fonction de l’ID de la catégorie qui est déjà
affectée au Produit à modifier

Les méthodes HttpPost pour Create et pour Edit incluent également du code qui définit l’élément
sélectionné quand elles réaffichent la page après une erreur. Ceci garantit que quand la page est
réaffichée pour montrer le message d’erreur, la catégorie qui a été sélectionnée le reste.

3. Ajouter .AsNoTracking aux méthodes Details et Delete

Pour optimiser les performances des pages Details et Delete pour les produits, ajoutez des appels
de AsNoTracking dans les méthodes Details et HttpGet Delete.

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


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

var product = await _context.Product


.Include(p => p.Category)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ProductId == id);
if (product == null)
{
return NotFound();
}

return View(product);
}

public async Task<IActionResult> Details(int? id)


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

var product = await _context.Product


.Include(p => p.Category)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ProductId == id);

Page 8 sur 9
Module au choix : Programmation .NET/C# 2ATEL A.U. 2020/2021

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

return View(product);
}

4. Modifier les vues des cours


Dans Views/Products/Create.cshtml, ajoutez une option « Select Category » à la liste
déroulante Category, changez la légende de CategoryId en Category et ajoutez un message de
validation.
<div class="form-group">
<label asp-for="Category" class="control-label"></label>
<select asp-for="CategoryId" class="form-control" asp-items="ViewBag.CategoryId">
<option value="">-- Select Category --</option>
</select>
<span asp-validation-for="CategoryId" class="text-danger" />
</div>

Dans Views/Products/Edit.cshtml, faites les mêmes modifications pour le champ Category que ce que vous
venez de faire dans Create.cshtml.
Également dans Views/Products/Edit.cshtml, ajoutez un champ de numéro de produit avant le
champ Name. Comme le numéro de produit est la clé primaire, il est affiché mais ne peut pas être
modifié.
<div class="form-group">
<label asp-for="ProductId" class="control-label"></label>
<div>@Html.DisplayFor(model => model.ProductId)</div>
</div>

Il existe déjà un champ masqué (<input type="hidden">) pour le numéro de produit dans la vue
Edit. L’ajout d’un tag helper <label> n’élimine la nécessité d’avoir le champ masqué, car cela n’a pas
comme effet que le numéro du produit est inclut dans les données envoyées quand l’utilisateur clique
sur Save dans la page Edit.

Dans Views/Products/Delete.cshtml, ajoutez un champ pour le numéro de produit en haut et changez


l’ID de category en nom de Category.
<dt>
@Html.DisplayNameFor(model => model.ProductId)
</dt>
<dd>
@Html.DisplayFor(model => model.ProductId)
</dd>

<dt>
@Html.DisplayNameFor(model => model.Category)
</dt>
<dd>
@Html.DisplayFor(model => model.Category.Name)
</dd>

Dans Views/Products/Details.cshtml, faites la même modification que celle que vous venez de faire
pour Delete.cshtml.
5. Testez les pages des produits

Page 9 sur 9

Vous aimerez peut-être aussi