Vous allez réaliser une petite application qui vous permettra de gérer une liste de bibliothèque ayant des livres à louer. Une petite BDD sera jointe au projet qu'on va essayer d'enrichir au maximum. L'idée est d'être en autonomie par Groupe de 2.
Le but du projet est de voir un maximum des choses que .NET peut vous apporter donc n'hésitez pas à demander au prof.
Pensez aussi à regarder les raccourcis utiles en bas de page.
Vous pouvez Fork ce repo Git, le cloner en local et travailler directement dedans.
- Solution
- La racine de votre application .NET qui permet d'avoir la vision globale des projet
- Manifesté sous la forme d'un fichier .sln, c'est ce fichier qu'il faut ouvrir pour ouvrir votre espace de travail
- Va contenir un ou plusieurs projet
- Projet :
- Bloc de construction de l'application
- Va générer une dll par projet
- Facilement extractable d'une solution à une autre
- Nuget :
- Beignet de poulet frit (existe aussi en version vegan)
- Gestionnaire de package
- Permet d'intégrer des dll à votre projet pour étendre les fonctionnalités de votre code (AutoMapper, ORM...)
- Commits réguliers, au moins à chaque étape
- Indentation
- En Anglais
- Privilégier les interfaces aux classes concrètes (normalement en suivant bien le projet ça devrait être facile)
- Nommage :
- "Singulier" pour un objet simple et "Pluriel" pour une liste
- PascalCase : Pour les noms de classe et interface...
- camelCase : Pour les variables
- _camelCase : Pour les variables déclaré au niveau de la classe
- Nom d'interface commençant par un I
- Nom de classe abstraite par un A
Créez un nouveau projet via Visual Studio 2022, puis sélectionner une application console nommée LibraryManager
.
Si vous n'avez pas d'explorateur de solution sur votre IDE, vous pouvez aussi l'ajouter via le menu Affichage > Explorateur de Solution
.
Pensez à ajouter via le gestionnaire de package Nuget sur votre projet : Microsoft.Extensions.Hosting
Et mettre en place son architecture de projets en ajoutant via VS des Librairies de classes :
BusinessLayer
: Couche métier; on va y mettre toute la logique métierServices
: Couche services intermédiaire; va permettre d'orchestrer les besoins et de relier d'autres couches entre ellesBusinessObjects
: Couche contenant vos objets métier (objets de base de données ou de travail)DataAccessLayer
: Couche permettant l'accès aux données; on y retrouvera notamment les repository
PS: Votre projet créer avec la solution fait office de couche d'entrée à l'application et configuration
Créer une méthode Main dans le Program.cs
grâce aux recommandations VS Alt + Entrée
à la l'intérieur du fichier, puis ajouter dans la classe Program la méthode :
private static IHost CreateHostBuilder(IConfigurationBuilder configuration)
{
return Host.CreateDefaultBuilder()
.ConfigureServices(services =>
{
// Configuration des services
})
.Build();
}
Maintenant passons à l'implémentation de notre architecture.
- Dans votre projet
BusinessObjects
, créez un dossierEntity
, puis dans ce dossier créez les objets correspondants aux tables du fichierLibraryInit.sql
Pour le type des types de livres, pensez à créer un enum
. Pas besoin de créer un fichier pour la table de Stock
.
Dans le cadre d'une relation OneToMany
(1..*) ou ManyToMany
(*), le Many se manifeste sous la forme d'une liste (Ex : IEnumerable<ClassA>
) et le One sous la forme d'un objet simple (Ex : ClassA
).
- Dans votre projet
DataAccessLayer
, créez un dossierRepository
, puis dans ce dossier une classeBookRepository
Vous y créerez les méthodes GetAll()
qui retournera un IEnumerable<Book>
et Get(int id)
qui retounera un Book
, vous pouvez return
des objets vides à ce stade.
PI : Vous aurez besoin d'ajouter des références d'un projet à un autre pour permettre d'utiliser vos entités à l'extérieur de leur projet respectif.
Répétez le même schéma pour chacune de vos entités.
-
Dans votre projet
BusinessLayer
, créez un dossierCatalog
, puis dans ce dossier une classeCatalogManager
qui contiendra les méthodesDisplayCatalog()
,DisplayCatalog(Type type)
etFindBook(int id)
qui utiliseront les Repository -
Dans votre projet
Services
, créez un dossierServices
, puis dans ce dossier une classeCatalogService
qui contiendra les méthodesShowCatalog()
,ShowCatalog(Type type)
etFindBook(int id)
qui utiliseront leCatalogManger
LINQ (prononcé line-cue) est langage d'interrogation pour vos tableaux en .NET. C'est un nuget à installer.
Celui vos permettra de filtrer vos tableaux et d'ajouter une granularité supplémentaire.
Il est possible d'utiliser LINQ via le langage correspondant (comme du SQL) ou d'utiliser des méthodes d'extensions sur vos listes (Ex : _voitures.Where(x => x.Price < 2000)
)
Dans ce contexte, on utilisera le langage LINQ comme dans la documentation Microsoft mais sachez que les deux sont possibles.
- Dans les
BusinessLayer
etServices
, ajoutez une méthode pour ne remonter que les livres de type "Fantasy" - Dans les
BusinessLayer
etServices
, ajoutez une méthode pour remonter le livre le mieux noté
Pour plus d'informations : LINQ - Microsoft
Il s'agit ici d'un concept extrêmement important lors du développement d'une application aujourd'hui. On ne développe non plus à partir de classe concrète mais à partir des interfaces afin de réduire le couplage de vos applications à l'implémentation.
L’injection de dépendances consiste, pour une classe, à déléguer la création de ses dépendances au code appelant qui va ensuite les injecter dans la classe correspondante. De ce fait, la création d’une instance de la dépendance est effectuée à l’extérieur de la classe dépendante et injectée dans la classe.
Par exemple, nous possède une classe concrète ApiACaller
que j'instancie à plusieurs endroits dans mon code.
Demain, on nous demande de la remplacer par un ApiBCaller
car la source de doit changer.
Plutôt que d'avoir à changer toutes références à notre classe concrète, nous allons mettre dans le constructeur notre interface qui vous donnera les fonctions disponibles.
Et à plus haut niveau, nous lui injecterons la classe concrète qui correspondera à cette interface.
A la compilation, la classe concrète correspondante est inconnu. Par contre, à l'exécution, cette classe est injecté à chaque qu'une référence à celle-ci est faite via l'interface.
De ce fait, lorsque que nous changerons l'implementation via une nouvelle classe, nous aurons juste besoin de changer la configuration correspondante.
public interface IApiCaller {
object Call();
}
public class ApiACaller : IApiCaller {
public object Call() {
return ResultA;
}
}
public class ApiBCaller : IApiCaller {
public object Call() {
return ResultB;
}
}
// Before
public Constructor() {
_caller = new ApiACaller();
}
// After
public Constructor(IApiCaller apiCaller) {
_caller = apiCaller;
}
En allant plus loin, on peut récupérer toutes vos classes de cette manière :
public ClassA(InterfaceB b){
_b = b;
}
public ClassB(InterfaceC c){
_c = c;
}
public ClassD(InterfaceA a, InterfaceB b) {
_a = a;
_b = b;
}
// Et ainsi de suite
On peut injecter un Singleton ! Renseignez vous sur la documentation pour connaître les cycles de vie des objets injecté.
Pour réaliser de l'injection de dépendance, extrayez une interface de vos classes concrètes ayant de la logique et instanciés ailleurs dans votre code (Ex : Services...)
Pour vos repository, on fera un peu différemment. Vous allez créer une seule interface IGenericRepository
qui prendra en paramètre un type générique. Aidez-vous de la documentation.
Pour récupérer votre classe correspondante dans le Main
et tester en reprenant l'exemple du IApiCaller
:
IApiCaller apiCaller = host.Services.GetRequiredService<IApiCaller>();
apiCaller.Call();
Pour plus d'informations :
Avec l'aide de la base de données SQLite fournit en annexe, vous allez implémenter l'ORM EntityFramework.
Le but ici est d'au lieu de renvoyer des listes vides au niveau de vos Repositories
, de renvoyer les infos qui sont stockées en base de données.
Dans votre DataAccessLayer
, créez un dossier Contexts
et un fichier LibraryContext
qui implémentera la classe DbContext
, servez-vous de la documentation pour remplir votre DbContext.
Pensez à l'injecter, pour une fois on utilisera une classe concrète. Vous aurez sûrement un autre Nuget à récupérer sur vos deux projets.
services.AddDbContext<LibraryContext>(options =>
options.UseSqlite("Data Source={path};"));
Dans vos respositories, utilisez le LibraryContext
injecté pour récupérer le contenu de la base.
Pour plus d'informations : EntityFramework - Microsoft
Pour consulter votre base de données, je vous conseille l'utilisation de DBeaver
Créez un dossier Tests
et puis un nouveau projet Services.Test
.
Créez une classe CatalogServiceTest
.
Implémentez un test unitaire sur chaque méthode de votre CatalogService
en pensant à Mock le retour de votre CatalogManager
pour bien tester unitairement votre méthode.
Pour plus d'informations : TU avec C# - Microsoft
On va maintenant mettre en place une API. Pour rappel, une API est une interface logiciel sur laquel vous pourrez vous connecter et récupérer des informations via requête HTTP. Elle vous renverra un résultat sous la forme d'un JSON.
Pour cela, vous allez créer un nouveau projet de type ASP.NET Core WebAPI
, sans authentification que vous allez appeler LibraryManager.Hosting
.
Une fois créée, vous allez mettre ce projet en tant que projet de démarrage.
Votre nouveau Program.cs
, va ressembler à ça :
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
/*
Les Middleware ajoutés avant le builder seront récupérer par l'application
*/
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Transformez votre fichier grâce aux recommandations VS Alt + Entrée
.
Ajoutez à votre builder les services de votre précédent Program.cs
.
Observez la classe créé :
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
Les éléments entre crochets sont appelés des attributs. Ils définissent des métadonnées qui sont lisible durant l'exécution.
Vous pouvez créé vos propres attributs, mais les attributs par défaut définissent des comportements :
[ApiController]
définit les comportements par défaut d'une API REST[Route("[controller]")]
définit la route pour accéder à un controller; le[controller]
désigne le nom de la classe en tant que endpoint[HttpGet(Name = "GetWeatherForecast")]
définit la méthode la méthode attendu
Donc pour accéder à cette API, nous utiliserons GET localhost:53000/WeatherForecast/GetWeatherForecast
. Il existe même des attributs pour l'authentification.
Après l'explication, place à la pratique.
Créez un fichier BookController
qui va commprendre les méthodes GET suivants :
- book
- book/{id}
- book/{type}
- book/topRatedBook
Pour tester votre API, installez Postman.
Pour plus d'informations : Tutoriel ASP.NET Core Web API- Microsoft
Améliorer votre bibliothèque :
- Créez de nouveaux endpoint pour accéder aux différentes tables.
- Ajoutez une table
Client
pour permettre à un client d'emprunter un livre et d'être black listed. - Ajoutez une notion de quantité à votre stock.
- Permettre à un client d'emprunter des livres, de les rendres.
- Recommandation VS :
Alt + Entrée
=> Hyper utile, n'hésitez pas à en abuser - Renommer un élément et ses références :
(Hold) CTRL + R + R
- Créer une propriété :
(Write) prop + Tab
- Créer un constructeur:
(Write) ctor + Tab
- Redirection sur la classe concernée :
F12
- Redirection sur la classe concrète concernée :
Ctrl + F12
- Afficher le contenu de l'élément dans un encart :
Alt + F12
- Lancer en Debug :
F5
- Faire continuer le programme :
F5
- Instruction suivante :
F10
- Instruction suivante dans la méthode :
F11
- Ajouter une référence à un projet :
Clic droit sur un Projet > Ajouter > Ajouter une référence à un projet
- Mail : erwann.fiolet@gmail.com
- Discord : byabyakar