Pour vérifier la liste :
- des SDKS installés :
dotnet --list-sdks
- des runtimes installés :
dotnet --list-runtimes
Possible d'installer .NET depuis le site https://dot.net.
Symbole | Définition |
---|---|
| |
x ou y . |
[abc] |
Non obligatoire. |
/* Définition d'un namespace (espace de noms)
Permet d'organiser les classes.
On peut utiliser les "`.`" pour délimiter un namespace et hierarchiser le code.
Généralement, Societe.Produit.Project.DossierNiveau1.DossierNiveau2
Exemples :
- IdeaStudio.HotelLandon.DemoConsole
- IdeaStudio.HotelLandon.DemoConsole.Exceptions <- où on mettra les exceptions
- IdeaStudio.HotelLandon.DemoConsole.Interfaces <- où on mettra les interfaces
Physiquement, dans le dosser qui contient du projet, il y aura les répértoires suivants :
- C:\Repos\
- IdeaStudio.HotelLandon.DemoConsole
- Exceptions
- Interfaces
Plus d'informations : https://docs.microsoft.com/dotnet/csharp/fundamentals/types/namespaces
*/
namespace Solution.Project
{
/* Définition d'une classe
Portée/visibilité (voir section dédiée ci-dessous)
Modificateur (voir section dédiée ci-dessous)
Le mot clé `class`
Nom de la classe (ClassName)
Si héritage, ajouter ":" + le nom de la classe parente (on ne peut hériter que d'une seule classe, mais de plusieurs interfaces)
Par convention, elle doit être écrite en PascalCase
*/
[public|internal|protected|private] [abstract|static|sealed] class ClassName
{
/* Champ privé :
- Portée (visibilité par les autres classes)
- Peut être statique ou en lecture seule
- Type (HttpClient)
- nom du champ (camelCase)
*/
private [static] [readonly] HttpClient httpClient;
/* Propriété
- Portée
- Type
- Nom de la propriété
- Accesseur (leur portée peut être différente)
- On peut directement affecter une valeur par défaut
*/
[public|internal|protected|private] HttpClient HttpClient { get; [internal|protected|private] set; } = new HttpClient();
/* Constructeur
C'est une méthode sans nom
- Portée
- Nom de la classe
- Entre parenthèses, les éventuels paramètres
- Si héritage de constructeur, le mot clé `base` avec les paramètres du contructeur parent
*/
public ClassName() { }
public ClassName(Foo foo) { }
public ClassName(params Foo[] foos) { }
public ClassName(Type type) : base (type) { }
/* Méthode
- Portée
- Type de retour
- Entre parenthèses, les éventuels paramètres
- Si héritage d'une méthode parente (et qu'on souhaite l'utiliser), le mot clé `base`
*/
public void DoSomething() { }
public void DoSomething(Foo foo) { }
public void DoSomething(params Foo[] foos) { }
}
}
Liste exhaustive :
Mot-clé | Description |
---|---|
public |
Visible par toutes les classes, quelque soit l'assembly. |
internal |
Visible par toutes les classes du même assembly. Valeur par défaut pour les classes. |
protected |
Visible par la classe et les classes qui hérite de celle-ci. |
private |
Visible uniquement par la classe. Valeur par défaut pour les champs, propriétés et méthodes. |
Mot-clé | Description |
---|---|
abstract |
La classe ne peut pas être instanciée, elle peut comporter des champs/propriété ou méthodes abstraites. |
static |
La classe ne peut pas être instanciée, les méthodes publiques sont accessibles depuis l'objet (Exemple System.Console ou System.IO.File ). |
sealed |
La classe ne peut plus être parente d'une autre classe. |
Plus d'informations à propos du...
public HttpClient HttpClient { get; set; } [= new()]
Index | Description |
---|---|
0 | Portée |
1 | Type de la proriété |
2 | Nom de la propriété |
3 | Accesseurs, la portée peut être modifiée pour l'un ou l'autre (par exemple public HttpClient HttpClient { get; private set; } ) |
4 | Symbole = pour affecter une valeur par défaut |
5 | Valeur |
private [readonly] HttpClient _httpClient [= new()];
Index | Description |
---|---|
0 | Portée |
1 | Qualificatif readonly , on ne pourra affecter la valeur que lors de la construction d'une instance. |
2 | Type du champs |
3 | Nom du champs |
4 | Symbole = pour affecter une valeur par défaut |
5 | Valeur |
private void DoSomething(Foo foo1, Foo foo2) { ... }
Index | Description |
---|---|
0 | Portée |
1 | Type de retour (int |string |Customer |...) ou le mot clé void pour signifier que l'on n'a pas de type de retour. |
2 | Nom de la méthode. Par convention, c'est du PascalCase |
3 | Paramètres de la méthode. Par convention, la case est camelCase. |
Plus d'informations sur les méthodes : https://docs.microsoft.com/dotnet/csharp/methods
private Task DoSomethingAsync(Foo foo1, Foo foo2) { ... }
- Même fonctionnement et but que la méthode classique.
- Mot clé
async
(entre la portée et le type de retour). - Le type de retour qui change : il doit s'agir d'un objet de type
System.Threading.Tasks.Task<>
(type générique qui peut prendre un autre type).
Type synchrone | Type asynchrone |
---|---|
void |
Task |
int |
Task<int> |
string |
Task<string> |
double |
Task<double> |
... | ... |
Attention à ne pas abuser de l'asynchronisme. Cela peut être contreproductif.
Plus d'informations sur les tâches asynchrones : https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async/
- Permet d'avoir un nombre illimité de paramètres.
- Le type doit être un tableau.
- Il doit être le dernier paramètre
// signature dans la méthode
void DoSomething(params string[] array) { ... }
// utilisation
DoSomething("first", "second", "third", "fourth", "fifth", "sixth");
Plus d'informations sur le mot clé
params
: https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/params
Assigner une valeur à travers un opérateur :
Raccourci | Equivalent de... | Description |
---|---|---|
x++ | x = x + 1 | Additionne 1 à x |
x-- | x = x - 1 | Soustrait 1 à x |
x += y | x = x + y | Additionne y à x |
x -= y | x = x - y | Soustrait y à x |
Nom | Exemple | Description |
---|---|---|
for |
for(int i = 0; i < 10; i++) { /*Instructions*/ } |
Exécute des instructions en parcourant un tableau à l'aide d'une condition (exemple: i < capacité du tableau) |
foreach |
foreach(object item in list) { /*Instructions*/ } |
Exécute des instructions dans toute une liste (dont la capacité dynamique) |
while |
while(condition) { /*Instructions*/ } |
Exécute des instructions tant que la condition est vraie (la première itération ne sera pas forcément exécuté) |
do while |
do { /*Instructions*/ } while (condition) |
Exécute des instructions tant que la condition est vraie (une première itération dans la boucle sera effectué) |
Permet de décorer une classe pour lui donner un flag ou de nouveaux paramètres.
Déclaration :
// déclaration d'un attribut qui peut décorer
// une classe, une méthode, un assembly, un constructeur, un champ, une propriété, une interface, etc.
// On peut également utiliser [System.AttributeUsage(System.AttributeTargets.All)]
public class FooAttribute : System.Attribute
{
}
// déclaration d'un attribut qui peut décorer une classe (uniquement)
[System.AttributeUsage(System.AttributeTargets.Class)]
public class FooAttribute : System.Attribute
{
}
// déclaration d'un attribut qui peut décorer une méthode (uniquement)
[System.AttributeUsage(System.AttributeTargets.Method)]
public class FooAttribute : System.Attribute
{
}
// déclaration d'un attribut qui peut décorer une méthode (uniquement)
[System.AttributeUsage(System.AttributeTargets.Method)]
public class FooAttribute : System.Attribute
{
}
Utilisation :
[FooAttribute] // <- on utilise le constructeur de l'attribut
public class Toto
{
}
[Foo] // <- on utilise le constructeur de l'attribut
public class Toto
{
}
Plus d'informations à propos
- Des attributs : https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/attributes/
- L'énumérateur (
enum
)System.AttributeTargers
: https://docs.microsoft.com/dotnet/api/system.attributetargets
Cela permet de gérer un comportement inattendu. Il peut être personnalisable. Les exceptions héritent de la classe Exception
et, par convention, on suffixe avec Exception
.
Il en existe quelques unes dans l'assembly
System
DoSomething();
try
{
DoSomethingElse();
}
catch(FormatException ex)
{
Console.WriteLine("Format incorrect.");
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
FinishAndExecuteAnyway();
}
Une exception faite maison :
public class NoNumberException : Exception
{
}
Un paramètre générique est entouré de chevrons. Voir l'exemple en bas de cette section.
- Créer un projet
HotelLandon.Repository
. - Créer l'interface
IRepositoryBase
. Elle doit prendre un paramètre générique (TEntity par exemple). - Utiliser la classe
RepositoryBase<TEntity>
au lieu deHotelLandonContext
pour ajouter des clients. - Ajouter des chambres à l'aide d'une boucle.
/// <summary>
/// Définition d'une classe générique
/// avec des exemples de contraintes
/// </summary>
/// <remarks>
/// il peut ne pas y en avoir</remarks>
class Repository<TEntity>
// TEntity doit avoir un constructeur public sans paramètres
// where TEntity : new()
// TEntity doit être une classe (pas une interface par exemple)
// where TEntity : class
// TEntity doit hériter de EntityBase
where TEntity : EntityBase
{
}
// utilisation
class Program
{
static void Main()
{
// Room hérite de EntityBase, Repository
var repository = new Repository<Room>();
}
}
Les génériques peuvent s'utiliser avec des méthodes :
bool IsPositive<TEntity>(TEntity entity)
where TEntity : EntityBase
{
if (entity.Id >= 0)
{
return true;
}
else return false;
}
// utilisation :
// TEntity = Customer
Customer customer = new();
IsPositive(customer);
On peut ajouter des méthodes à des classes, même scellées. Ce sont des méthodes d'extention. Les méthodes d'extention doivent être écrites dans une classe statique.
public IsPositive<TEntity>(this TEntity entity)
where TEntity : EntityBase
{
...
}
// utilisation :
// TEntity = Customer
Customer customer = new();
bool isPositive = customer.IsPositive();
- Utiliser la commande
dotnet new sln --name HotelLandon
pour créer une solution Visual Studio nommée "HotelLandon" - Pour chaque projet, l'ajouter à la solution à l'aide de la commande
dotnet sln add [PATH-RELATIF-DU-PROJET]
(dotnet sln add HotelLandon.Data
). - Ouvrir la solution avec Visual Studio : tous les projets y sont référencés !
- Gratuit et Open Source
- Peut être utilisé en local ou sur un serveur distant
Il existe plusieurs logiciels de gestion de versions décentralisé en ligne permettant d'avoir un serveur Git distant :
- Github
- GitLab
- Azure DevOps
- Bitbucket
- ...
Il existe des clients lourds pour Windows, macOS et Linux dont :
- GitHub Desktop
- SourceTree
- ...
Les IDE intègrent généralement git.
Pour installer l'outil de ligne de commandes : https://git-scm.com.
Visual Studio et Visual Studio Code intègrent Git.
Etapes pour publier son code dans un repository Git :
(https://commons.wikimedia.org/wiki/File:Git_operations.svg)
Faire une copie locale du repository distant :
Etape | Commande | Description |
---|---|---|
1 | (aucune) | Choisir un dossier local et y ouvrir une instance d'un terminal. Par exemple dans le répertoire "C:\Repos\HotelLandon-2022-01 ". |
2 | git init |
Git créé un dossier caché nommé ".git " contenant toutes les informations nécessaires pour gérer l'historique et les différentes versions. |
3 | (aucune) | Ajouter des fichiers (par exemple readme.md , .gitignore , votre code, ...). Le fichier " readme.md " contient de la documentation qui est affichée dans une page "index" dans les logiciels en ligne. |
4 | git add fichier1 fichier2 ... ou git add -A (Pour ajouter tous les fichiers) |
Ajouter dans le prochain commit le(s) fichier(s). L'argument -A (ou --all ) ajoute au commit tous les fichiers ajoutés, modifiés ou supprimés. |
4 | git commit -m "Description de vos modifications" |
Prépare les fichiers ajoutés pour leur attribuer un nom de version. On peut réitérer l'opération plusieurs fois. Par exemple : git commit -m "Ajouter readme.md" git commit -m "Ajouter .gitignore de Visual Studio" git commit -m "Création du projet" |
5 | git branch -M main |
Créé la branche par défaut "main ".Il est courant de trouver " master " comme nom de branche par défaut. |
6 | git remote add origin [email protected]/a/b.git |
Ajoute l'adresse du repository (serveur) distant dans les paramètres du repository local. Où "[email protected]/a/b.git " est l'adresse internet du repository.Par exemple " [email protected]/andrestalavera/hotellandon-2022-01.git ". |
6 | git push -u origin main |
Git envoie tous les commits dans le serveur distant (origin). Le paramètre -u (ou --set-upstream ) ajoute une référence entre dans la branche locale "main " et la branche "main " présente dans le serveur d'origine (paramètre "origin "). |
- Tutoriel pour cloner un repository avec Visual Studio Code : https://code.visualstudio.com/docs/editor/versioncontrol#_cloning-a-repository (uniquement en anglais).
- Tutoriel pour cloner un repository avec Visual Studio 2019/2022 : https://docs.microsoft.com/visualstudio/version-control/git-clone-repository
TODO
La commande
git status
permet d'afficher le statut de chaque fichier :
- prêt à être ajouté au prochain commit
- qui n'est pas inclu dans le prochain commit
- non présent dans le repository distant (origin)
Application légère de réservation d'hôtel
- Ouvrir un terminal dans le dossier de votre choix, par exemple
C:\Repos
. On l'appelera le dossier racine. - Dans le terminal, à la racine, créer un projet librairie de classes à l'aide de .NET CLI grâce à la commande
dotnet new classlib --name HotelLandon.Models
- Créer 3 classes :
Customer
,Room
etReservation
Customer
contient les propriétés suivantes :FirstName
,LastName
,Birthdate
Room
contientNumber
,Floor
Reservation
:Customer
,Room
,Start
,End
- Créer un projet console avec la commande
dotnet new console --name HotelLandon.DemoConsole
- Référencer le projet
HotelLandon.Models
dans le nouveau projet grâce à la commandedotnet add reference HotelLandon.Models
- Ecrire un programme console capable d'instancier un
Customer
- Sérialiser et désérialiser en CSV grâce aux classes
System.IO.StreamWriter
etSystem.IO.StreamReader
- Possible d'utiliser la classe
System.IO.StreamWriter
. - La classe a plusieurs méthodes permettant d'écrire du texte dans la ressource choisie. Dans les exemples ci-dessous, la ressource est le fichier "
data.csv
". - Ne pas oublier que la classe
System.IO.StreamWriter
hérite de l'interfaceSystem.IDisposable
. Les ressources, en l'occurrence le fichier "data.csv
", reste ouvert et est bloqué par le processus. Aucune autre modification des ressources ne sera possible. 2 possibilités : utiliser la méthodeSystem.IO.StreamWriter.Dispose()
ou utiliser l'instructionusing
(recommandé). - Une nouvelle instance de la classe
System.IO.StreamWriter
remplacera le fichier. L'ancien contenu sera remplacé. - Pour ajouter du contenu dans un fichier existant, on peut utiliser la méthode statique
System.IO.File.AppendText
qui va créer une instance deSystem.IO.StreamWriter
.
using (StreamWriter writer = new StreamWriter("data.csv"))
{
// Ajoute une ligne
writer.WriteLine(customer.ToCsv());
// Ajoute à la fin de la ligne (sans saut de ligne)
writer.Write(customer.ToCsv());
}
using (StreamWriter writer = File.AppendText("data.csv"))
{
// Ajoute une ligne
writer.WriteLine(customer.ToCsv());
// Ajoute à la fin de la ligne (sans saut de ligne)
writer.Write(customer.ToCsv());
}
Plus d'informations
À propos la classe
System.IO.StreamWriter
: https://docs.microsoft.com/dotnet/api/system.io.streamwriterÀ propos de la méthode statique
System.IO.File.AppendText(string)
: https://docs.microsoft.com/dotnet/api/system.io.file.appendtextÀ propos de l'instruction
using
: https://docs.microsoft.com/dotnet/csharp/language-reference/keywords/using-statement
- Possible d'utiliser la classe
System.IO.StreamReader
- La classe
System.IO.StreamReader
a plusieurs méthodes permettant de lire du texte dans la ressources choisie. Dans l'exemple ci-dessous il s'agit du fichier "data.csv
". - Ne pas oublier que la classe System.IO.StreamWriter hérite de l'interface System.IDisposable.
- Il est possible de boucler (grâce ) et de lire ligne par ligne.
using (StreamReader reader = new StreamReader("data.csv"))
{
// Crée un entier pour indiquer l'index du numéro de ligne
int lineNumber = 0;
// Tant que ce n'est pas la fin du contenu du fichier
while (!reader.EndOfStream) // Alternative: while ((line = reader.ReadLine()) != null)
{
// On récupère la ligne
string line = reader.ReadLine();
// Si elle est nulle ou est vide (contient au moins un espace)
if (string.IsNullOrWhiteSpace(line))
{
// Affiche un message dans la console indiquant le numéro de ligne vide
Console.WriteLine($"La ligne {lineNumber} est vide.");
// permet d'ignorer la ligne du fichier
// et de passer à la suivante
// la prochaine instruction qui va s'exécuter sera :
// string line = reader.ReadLine();
continue;
}
Console.WriteLine(line);
// Incrémente le numéro de ligne
lineNumber++;
}
}
Contrairement au mot-clé break
, continue
ne va pas casser le cycle de la boucle while
.
Plus d'informations à propos...
Un exemple parmi tant d'autres possibilités, le faire soi-même avec une méthode :
public string ToCsv()
{
// avec string interpolation
return $"{LastName};{FirstName};{BirthDate}";
}
Avec de la réflexion (pouvant être une méthode d'extension générique) :
public string ToCsv<T>(IEnumerable<T> items) // version méthode d'extension : public static string ToCsv<T>(this IEnumerable<T> items)
where T : class
{
// Créé 2 variables de type `string`, nommées `output` et `delimiter`
string output = string.Empty, delimiter = ';';
// Récupère la liste des propriétés de la classe générique T
System.Reflection.PropertyInfo[] properties = typeof(T).GetProperties()
// Filtre les type que l'on souhaite parser au format CSV.
// La méthode `Where(...)` est issue de `System.Linq`
.Where(n =>
n.PropertyType == typeof(string)
|| n.PropertyType == typeof(bool)
|| n.PropertyType == typeof(char)
|| n.PropertyType == typeof(byte)
|| n.PropertyType == typeof(decimal)
|| n.PropertyType == typeof(int)
|| n.PropertyType == typeof(DateTime)
|| n.PropertyType == typeof(DateTime?));
// Création d'un `System.IO.StringWriter` en mémoire (on n'ouvre pas de fichier)
using (var sw = new StringWriter())
{
// Sélectionne les noms des colonnes
var header = properties
.Select(n => n.Name)
.Aggregate((a, b) => a + delimiter + b);
// On écrit la ligne en mémoire
sw.WriteLine(header);
// Pour chaque élément d'une liste
foreach (var item in items)
{
// On récupère les valeurs
var row = properties
.Select(n => n.GetValue(item, null))
.Select(n => n == null ? “null” : n.ToString())
.Aggregate((a, b) => a + delimiter + b);
// On écrit la ligne en mémoire
sw.WriteLine(row);
}
// On récupère le contenu écrit en mémoire
output = sw.ToString();
}
// On renvoie le contenu CSV
return output;
}
// Admettons que la liste aie plusieurs objets de type `Customer`
IEnumerable<Customer> customers;
// appel (méthode classique)
string csv = ToCsv(customer);
// appel (méthode d'extension)
string csv = customers.ToCsv();
public Customer ToCustomer(string line)
{
return new Customer()
{
// mapper les propriétés avec les indes des 'colonnes'
LastName = line[0],
FirstName = line[1],
BirthDate = DateTime.Parse(line[2])
}
}
Il existe des packages NuGet permettant de mapper automatiquement.
Pour installer un package NuGet avec dotnet-cli :
dotnet add package {NOM_PACKAGE}
(En l'occurrence pour le packageNewtonsoft.Json
:dotnet add package Newtonsoft.Json
).Par défaut, la commande installe la dernière version disponible dans www.nuget.org. Pour installer une version particulière, vous pouvez ajouter l'argument
--version X.X.X.X
(où "X.X.X.X
" est la version spécifiée)
using Newtonsoft.Json;
// ...
public string ToJson(Customer customer)
{
return JsonConvert.SerializeObject(customer);
}
using Newtonsoft.Json;
public Customer ToCustomer(string json)
{
return JsonConvert.DeserializeObject<Customer>(json);
}
Vous pouvez exécuter l'application à l'aide de la commande
dotnet run
(=dotnet restore
,dotnet build
, exécution).
Installer des outils pour .NET :
dotnet tool install [NAME]
- A la racine, créer un projet librairie de classes
HotelLandon.Data
- Installer les packages
Microsoft.EntityFrameworkCore
,Microsoft.EntityFrameworkCore.SqlServer
etMicrosoft.EntityFrameworkCore.Design
- Créer la classe
HotelLandonContext
qui hérite deMicrosoft.EntityFrameworkCore.DbContext
et qui contient 3 propriétés :
DbSet<Room> Rooms
DbSet<Customer> Customers
DbSet<Reservation> Reservations
- Dans le projet
HotelLandon.Models
, ajouter une classe abstraiteEntityBase
qui contient une seule propriétéint Id
. - Faire hériter les classes
Customer
,Room
etReservation
de la nouvelle classeEntityBase
. - Installer l'outil
dotnet-ef
grâce à la commandedotnet tool install
au niveau global (argument--global
). - Ajouter les propriétés de navigation représentant les clés étrangères :
- Dans la classe
Customer
,HashSet<Reservation> Reservations
- Dans la classe
Room
,HashSet<Reservation> Reservations
- Dans la classe
Reservation
,int CustomerId
etint RoomId
- Créer une migration grâce à la commande
dotnet ef migrations add NOM_MIGRATION
(par exempleInitial
).
La migration va créer un delta entre la base de données et le modèle défini dans la classeHotelLandonContext
. Si la base de données n'existe pas, elle sera créée par la première migration. - Appliquer les migrations dans la base de données (si la base de données n'existe pas, elle sera créée) grâce à la commande
dotnet ef database update
.
[Ajouter un shéma]
- A la racine, créer un projet console (
HotelLandon.DemoEfCore
) qui va me permettre d'interagir avec la base de données : on doit pouvoir créer des clients. - Ajouter des chambres (avec une boucle pour initialiser la liste des chambres disponibles).
- A la racine, créer un projet Web API à l'aide de la commande
dotnet new webapi --name HotelLandon.Api
. - Exécuter et explorer.
- Ajouter les références
HotelLandon.Data
,HotelLandon.Models
,HotelLandon.Repository
dans le projetHotelLandon.Api
. - Créer un contrôleur avec les contraintes suivantes :
- Classe abstraite (mot-clé
abstract
). - Hérite de
Microsoft.AspNetCore.Mvc.ControllerBase
. - Prends 2 paramètres génériques :
TRepository
qui doit hériter deIRepositoryBase<TEntity>
etTEntity
qui doit hériter deEntityBase
. - Décorée par les attributs
Microsoft.AspNetCore.Mvc.ApiController
etMicrosoft.AspNetCore.Mvc.Route
(ce dernier doit prendre en paramètre un template, par exemple[controller]
pour que les routes correspondent au nom du contrôleur). - Ayant les méthodes
GetAll
,GetById(int)
,Create
,Update
etDelete
.
Chacune devra retourner un objet de typeActionResult<T>
,T
étant soit un énumérable deTEntity
, soit unTEntity
, soit unbool
.
Chaque méthode devra être décorée d'un attribut associé à son verbe HTTP (get, post, put, delete).
[Route("[controller]")]
[ApiController]
public abstract class GenericController<TRepository, TEntity> : ControllerBase
where TRepository : IRepositoryBase<TEntity>
where TEntity : EntityBase
{
}
- A la racine, créer un projet web mvc nommé
HotelLandon.MvcRazor
. - Exécuter et explorer.
- Ajouter les références
HotelLandon.Data
,HotelLandon.Models
,HotelLandon.Repository
dans le projetHotelLandon.MvcRazor
. - Créer un contrôleur avec les contraintes suivates :
- Classe abstraite
- Hérite de
Microsoft.AspNetcore.Mvc.Controller
(qui gère les vues en plus de sa classe mère). - Prends 2 paramètres génériques :
TRepository
qui doit hériter deIRepositoryBase<TEntity>
etTEntity
qui doit hériter deEntityBase
. - Pas de décoration
- Ayant les méthodes
Index()
,Details(int)
,Create(int)
,Create(int, TEntity)
,Edit(int)
,Edit(int,TEntity)
,Delete(int)
,DeleteConfirmed
qui devront retourner unIActionResult
(ou sa version asynchrone, si nécessaire).
Les méthodesEdit(int,TEntity)
etCreate(int,TEntity)
devront être marquées comme étant virtuelles. Les autres méthodes peuvent l'être également.
- Créer une classe concrète pour les clients,
CustomersController
, qui devra hériter de votre classe abstraiteGenericController<IRepositoryBase<Customer>, Customer>
. - Surcharger les méthodes
Edit(int,Customer)
etCreate(int,Customer)
afin d'ajouter au paramètre de typeCustomer
l'attribut[Bind("Id","FirstName,LastName,BirthDate")]
- Dans le dossire
Views
du projet ; Créer un dossier ayant comme nom, le nom de vôtre contrôleur sans son suffixe.
Exemple : Pour le contrôleurCustomerController
, le dossier devra porter le nom "Customers
". - Pour chaque méthode publique retourant un objet de type
IActionResult
, créer une vue razor (extension ".cshtml
") du nom de la méthode. Ecrire un code HTML simple permettant simplement de réaliser ce pourquoi la page a été définie.
Exemples :
Pour la méthodeIndex()
, créer un fichier nommé "Index.cshtml
". Il faudra afficher la liste de tous les clients.
Pour la méthodeCreate()
, créer un fichier nommé "Create.cshtml
". Il faudra afficher un formulaire permettant de créer un client.
https://fr.wikipedia.org/wiki/Singleton_(patron_de_conception)#C#
Il existe plusieurs implémentations possibles :
- Dependency Injection (Injection de dépendences)
- Service Locator
- Factory ...
Il permet d'ajouter des services (dépendence) dans un dictionnaire (en mémoire).
On peut les utiliser en ajoutant des paramètres soit dans le contructeur (le plus commun), soit à travers une méthode. On l'injecte.
Exemple :
// Startup.cs :
Configure(IServiceCollection services)
{
// méthode d'extension fournie par un package tiers
services.AddWeatherService();
}
// Autre classe (contrôleur par exemple) :
public class WeatherController : ControllerBase
{
private readonly IWeatherService _weatherService;
public WeatherController(IWeatherService weatherService)
{
// on affecte le champs
_weatherService = weatherService;
}
public Weather Get(id)
{
// on consomme le service
return _weatherService.Get(id);
}
}
Ici, WeatherController dépends de IWeatherService.
- LinkedIn Learning (cours vidéo avec présentations - payant, 20€/mois~)
- Microsoft Learn (cours écrit avec TP - gratuit)
- Documentation .NET
- Documentation EFCore