Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In dit stapsgewijze overzicht ziet u hoe u een eenvoudige Windows Forms-toepassing (WinForms) bouwt die wordt ondersteund door een SQLite-database. De toepassing maakt gebruik van Entity Framework Core (EF Core) om gegevens uit de database te laden, wijzigingen in die gegevens bij te houden en deze wijzigingen weer in de database te behouden.
De schermafbeeldingen en codevermeldingen in dit overzicht worden genomen vanuit Visual Studio 2022 17.3.0.
Aanbeveling
U kunt het voorbeeld van dit artikel bekijken op GitHub.
Vereiste voorwaarden
U moet Visual Studio 2022 17.3 of hoger hebben geïnstalleerd met de .NET-desktopworkload geselecteerd om deze handleiding te voltooien. Zie Visual Studio installeren voor meer informatie over het installeren van de nieuwste versie van Visual Studio.
De toepassing maken
Visual Studio openen
Kies nieuw project maken in het startvenster.
Kies Windows Forms-app en klik vervolgens op Volgende.
Geef in het volgende scherm een naam op voor het project, bijvoorbeeld GetStartedWinForms, en kies Volgende.
Kies in het volgende scherm de .NET-versie die u wilt gebruiken. Deze handleiding is gemaakt met .NET 7, maar zal ook werken met latere versies.
Kies Maken.
De EF Core NuGet-pakketten installeren
Klik met de rechtermuisknop op de oplossing en kies NuGet-pakketten beheren voor oplossing...
Kies het tabblad Bladeren en zoek naar Microsoft.EntityFrameworkCore.Sqlite.
Selecteer het pakket Microsoft.EntityFrameworkCore.Sqlite .
Controleer het project GetStartedWinForms in het rechterdeelvenster.
Kies de nieuwste versie. Als u een voorlopige versie wilt gebruiken, moet u ervoor zorgen dat het selectievakje Voorlopige versie opnemen is ingeschakeld.
Klik op Installeren
Opmerking
Microsoft.EntityFrameworkCore.Sqlite is het pakket 'databaseprovider' voor het gebruik van EF Core met een SQLite-database. Vergelijkbare pakketten zijn beschikbaar voor andere databasesystemen. Als u een databaseproviderpakket installeert, worden automatisch alle afhankelijkheden opgenomen die nodig zijn voor het gebruik van EF Core met dat databasesysteem. Dit omvat het basispakket Microsoft.EntityFrameworkCore .
Een model definiëren
In dit scenario implementeren we een model met behulp van Code First. Dit betekent dat EF Core de databasetabellen en het schema maakt op basis van de C#-klassen die u definieert. Zie Databaseschema's beheren om te zien hoe u in plaats daarvan een bestaande database gebruikt.
Klik met de rechtermuisknop op project en kies Toevoegen en vervolgens Klasse... om een nieuwe klasse toe te voegen.
Gebruik de bestandsnaam
Product.cs
en vervang de code voor de klasse door:using System.ComponentModel; namespace GetStartedWinForms; public class Product { public int ProductId { get; set; } public string? Name { get; set; } public int CategoryId { get; set; } public virtual Category Category { get; set; } = null!; }
Herhaal om
Category.cs
te maken met de volgende code:using Microsoft.EntityFrameworkCore.ChangeTracking; namespace GetStartedWinForms; public class Category { public int CategoryId { get; set; } public string? Name { get; set; } public virtual ObservableCollectionListSource<Product> Products { get; } = new(); }
De Products
eigenschap van de Category
klasse en de Category
eigenschap in de Product
klasse worden 'navigatie' genoemd. In EF Core definiëren navigatie een relatie tussen twee entiteitstypen. In dit geval verwijst de Product.Category
navigatie naar de categorie waartoe een bepaald product behoort. Op dezelfde manier bevat de Category.Products
verzamelingsnavigatie alle producten voor een bepaalde categorie.
Aanbeveling
Wanneer u Windows Forms gebruikt, kan de ObservableCollectionListSource
, die IListSource
implementeert, worden gebruikt voor verzamelingsnavigaties. Dit is niet nodig, maar verbetert wel de tweerichtingsbindingservaring voor gegevens.
DbContext definiëren
In EF Core wordt een klasse die is afgeleid van DbContext
, gebruikt om entiteitstypen in een model te configureren en te fungeren als een sessie voor interactie met de database. In het eenvoudigste geval, een DbContext
klasse:
- Bevat
DbSet
eigenschappen voor elk entiteitstype in het model. - Overschrijft de
OnConfiguring
-methode om de databaseprovider en verbindingstekst die moet worden gebruikt te configureren. Zie Een DbContext configureren voor meer informatie.
In dit geval overschrijft de DbContext-klasse ook de OnModelCreating
methode om enkele voorbeeldgegevens voor de toepassing op te geven.
Voeg een nieuwe ProductsContext.cs
klasse toe aan het project met de volgende code:
using Microsoft.EntityFrameworkCore;
namespace GetStartedWinForms;
public class ProductsContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite("Data Source=products.db");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>().HasData(
new Category { CategoryId = 1, Name = "Cheese" },
new Category { CategoryId = 2, Name = "Meat" },
new Category { CategoryId = 3, Name = "Fish" },
new Category { CategoryId = 4, Name = "Bread" });
modelBuilder.Entity<Product>().HasData(
new Product { ProductId = 1, CategoryId = 1, Name = "Cheddar" },
new Product { ProductId = 2, CategoryId = 1, Name = "Brie" },
new Product { ProductId = 3, CategoryId = 1, Name = "Stilton" },
new Product { ProductId = 4, CategoryId = 1, Name = "Cheshire" },
new Product { ProductId = 5, CategoryId = 1, Name = "Swiss" },
new Product { ProductId = 6, CategoryId = 1, Name = "Gruyere" },
new Product { ProductId = 7, CategoryId = 1, Name = "Colby" },
new Product { ProductId = 8, CategoryId = 1, Name = "Mozzela" },
new Product { ProductId = 9, CategoryId = 1, Name = "Ricotta" },
new Product { ProductId = 10, CategoryId = 1, Name = "Parmesan" },
new Product { ProductId = 11, CategoryId = 2, Name = "Ham" },
new Product { ProductId = 12, CategoryId = 2, Name = "Beef" },
new Product { ProductId = 13, CategoryId = 2, Name = "Chicken" },
new Product { ProductId = 14, CategoryId = 2, Name = "Turkey" },
new Product { ProductId = 15, CategoryId = 2, Name = "Prosciutto" },
new Product { ProductId = 16, CategoryId = 2, Name = "Bacon" },
new Product { ProductId = 17, CategoryId = 2, Name = "Mutton" },
new Product { ProductId = 18, CategoryId = 2, Name = "Pastrami" },
new Product { ProductId = 19, CategoryId = 2, Name = "Hazlet" },
new Product { ProductId = 20, CategoryId = 2, Name = "Salami" },
new Product { ProductId = 21, CategoryId = 3, Name = "Salmon" },
new Product { ProductId = 22, CategoryId = 3, Name = "Tuna" },
new Product { ProductId = 23, CategoryId = 3, Name = "Mackerel" },
new Product { ProductId = 24, CategoryId = 4, Name = "Rye" },
new Product { ProductId = 25, CategoryId = 4, Name = "Wheat" },
new Product { ProductId = 26, CategoryId = 4, Name = "Brioche" },
new Product { ProductId = 27, CategoryId = 4, Name = "Naan" },
new Product { ProductId = 28, CategoryId = 4, Name = "Focaccia" },
new Product { ProductId = 29, CategoryId = 4, Name = "Malted" },
new Product { ProductId = 30, CategoryId = 4, Name = "Sourdough" },
new Product { ProductId = 31, CategoryId = 4, Name = "Corn" },
new Product { ProductId = 32, CategoryId = 4, Name = "White" },
new Product { ProductId = 33, CategoryId = 4, Name = "Soda" });
}
}
Zorg ervoor dat u de oplossing op dit moment bouwt.
Besturingselementen toevoegen aan het formulier
In de toepassing wordt een lijst met categorieën en een lijst met producten weergegeven. Wanneer een categorie in de eerste lijst is geselecteerd, wordt de tweede lijst gewijzigd om producten voor die categorie weer te geven. Deze lijsten kunnen worden gewijzigd om producten en categorieën toe te voegen, te verwijderen of te bewerken. Deze wijzigingen kunnen worden opgeslagen in de SQLite-database door op een knop Opslaan te klikken.
Wijzig de naam van het hoofdformulier in
Form1
MainForm
.En wijzig de titel in 'Producten en categorieën'.
Voeg met behulp van de Werkset twee
DataGridView
besturingselementen toe, gerangschikt naast elkaar.In de eigenschappen voor de eerste
DataGridView
, wijzig de naam indataGridViewCategories
.Wijzig in de eigenschappen de
DataGridView
van de tweede naardataGridViewProducts
.Gebruik ook de Toolbox om een
Button
controle toe te voegen.Geef de knop
buttonSave
een naam en geef deze de tekst 'Opslaan'. Het formulier ziet er ongeveer als volgt uit:
Gegevensbinding
De volgende stap bestaat uit het verbinden van de Product
en Category
typen van het model met de DataGridView
besturingselementen. Hiermee worden de gegevens die door EF Core worden geladen, gekoppeld aan de besturingselementen, zodat de entiteiten die door EF Core worden bijgehouden, gesynchroniseerd blijven met de entiteiten die in de besturingselementen worden weergegeven.
Klik op de Designer-actieglyph op de eerste
DataGridView
. Dit is de kleine knop in de rechterbovenhoek van het besturingselement.Hiermee opent u de actielijst, waaruit de vervolgkeuzelijst voor de gekozen gegevensbron kan worden geopend. We hebben nog geen gegevensbron gemaakt, dus ga naar de onderkant en kies Nieuwe objectgegevensbron toevoegen....
Kies Categorie om een objectgegevensbron voor categorieën te maken en klik op OK.
Aanbeveling
Als hier geen gegevensbrontypen worden weergegeven, moet u ervoor zorgen dat
Product.cs
enCategory.cs
ProductsContext.cs
zijn toegevoegd aan het project en dat de oplossing is gebouwd.Nu bevat de vervolgkeuzelijst Gegevensbron kiezen de objectgegevensbron die we zojuist hebben gemaakt. Vouw Andere gegevensbronnen uit, vervolgens Projectgegevensbronnen en kies Categorie.
De tweede
DataGridView
is gebonden aan producten. Maar in plaats van een binding met het type op het hoogste niveauProduct
, wordt het in plaats daarvan gebonden aan deProducts
navigatie vanuit deCategory
binding van de eersteDataGridView
. Dit betekent dat wanneer een categorie in de eerste weergave is geselecteerd, de producten voor die categorie automatisch worden gebruikt in de tweede weergave.Kies met behulp van het Designer Actiepictogram op de tweede
DataGridView
, Gegevensbron kiezen, vouw vervolgens hetcategoryBindingSource
uit en kiesProducts
.
Configureren wat wordt weergegeven
Standaard wordt er een kolom gemaakt in de DataGridView
voor elke eigenschap van de afhankelijke typen. De waarden voor elk van deze eigenschappen kunnen ook door de gebruiker worden bewerkt. Sommige waarden, zoals de primaire-sleutelwaarden, zijn echter conceptueel alleen-lezen en moeten dus niet worden bewerkt. Bovendien zijn sommige eigenschappen, zoals de CategoryId
buitenlandse sleutel-eigenschap en de Category
navigatie, niet nuttig voor de gebruiker en moeten dus worden verborgen.
Aanbeveling
Het is gebruikelijk om primaire sleuteleigenschappen in een echte toepassing te verbergen. Ze blijven hier zichtbaar om te zien wat EF Core achter de schermen doet.
Klik met de rechtermuisknop op de eerste
DataGridView
en kies Kolommen bewerken....Maak de kolom
CategoryId
, die de primaire sleutel vertegenwoordigt, alleen-lezen en klik op OK.Klik met de rechtermuisknop op de tweede
DataGridView
en kies Kolommen bewerken.... Maak deProductId
kolom alleen-lezen en verwijder deCategoryId
enCategory
kolommen en klik op OK.
Verbinding maken met EF Core
De toepassing heeft nu een kleine hoeveelheid code nodig om EF Core te verbinden met de gegevensgebonden besturingselementen.
Open de
MainForm
code door met de rechtermuisknop op het bestand te klikken en Code weergeven te kiezen.Voeg een privéveld toe om de
DbContext
voor de sessie te bewaren en voeg overschrijvingen toe voor deOnLoad
enOnClosing
methoden. De code moet er als volgt uitzien:
using Microsoft.EntityFrameworkCore;
using System.ComponentModel;
namespace GetStartedWinForms
{
public partial class MainForm : Form
{
private ProductsContext? dbContext;
public MainForm()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.dbContext = new ProductsContext();
// Uncomment the line below to start fresh with a new database.
// this.dbContext.Database.EnsureDeleted();
this.dbContext.Database.EnsureCreated();
this.dbContext.Categories.Load();
this.categoryBindingSource.DataSource = dbContext.Categories.Local.ToBindingList();
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
this.dbContext?.Dispose();
this.dbContext = null;
}
}
}
De OnLoad
methode wordt aangeroepen wanneer het formulier wordt geladen. Op dit moment
- Een instantie van de
ProductsContext
wordt gemaakt om producten en categorieën te laden en wijzigingen bij te houden die door de toepassing worden weergegeven. -
EnsureCreated
wordt aangeroepenDbContext
om de SQLite-database te maken als deze nog niet bestaat. Dit is een snelle manier om een database te maken bij het maken van prototypen of het testen van toepassingen. Als het model echter wordt gewijzigd, moet de database worden verwijderd, zodat deze opnieuw kan worden gemaakt. (DeEnsureDeleted
regel kan ongecommentarieerd zijn om de database eenvoudig te verwijderen en opnieuw te maken wanneer de toepassing wordt uitgevoerd.) U kunt in plaats daarvan EF Core-migraties gebruiken om het databaseschema te wijzigen en bij te werken zonder gegevens te verliezen. -
EnsureCreated
vult ook de nieuwe database met de gegevens die in deProductsContext.OnModelCreating
methode zijn gedefinieerd. - De
Load
extensiemethode wordt gebruikt om alle categorieën uit de database in deDbContext
database te laden. Deze entiteiten worden nu bijgehouden door deDbContext
entiteit , waarmee wijzigingen worden gedetecteerd die zijn aangebracht wanneer de categorieën door de gebruiker worden bewerkt. - De
categoryBindingSource.DataSource
eigenschap wordt geïnitialiseerd voor de categorieën die worden bijgehouden door deDbContext
. Dit wordt gedaan door deLocal.ToBindingList()
Categories
eigenschap aan te roepenDbSet
.Local
biedt toegang tot een lokale weergave van de bijgehouden categorieën, waarbij gebeurtenissen zijn gekoppeld om ervoor te zorgen dat de lokale gegevens gesynchroniseerd blijven met de weergegeven gegevens en omgekeerd.ToBindingList()
maakt deze gegevens beschikbaar als eenIBindingList
, die wordt begrepen door windows Forms-gegevensbinding.
De OnClosing
methode wordt aangeroepen wanneer het formulier wordt gesloten. Op dit moment wordt de DbContext
database verwijderd, waardoor alle databasebronnen worden vrijgemaakt en het dbContext
veld is ingesteld op null, zodat het niet opnieuw kan worden gebruikt.
Productenweergave vullen
Als de toepassing op dit moment wordt gestart, ziet deze er ongeveer als volgt uit:
U ziet dat de categorieën uit de database zijn geladen, maar de tabel producten leeg blijft. De knop Opslaan werkt ook niet.
Als u de tabel producten wilt vullen, moet EF Core producten laden uit de database voor de geselecteerde categorie. U kunt dit als volgt bereiken:
Selecteer in de ontwerpfunctie voor het hoofdformulier de
DataGridView
optie voor categorieën.Kies in de Eigenschappen van de
DataGridView
de gebeurtenissen (de bliksemknop) en dubbelklik op de SelectionChanged gebeurtenis.Hiermee maakt u stub in de hoofdformuliercode om een gebeurtenis te activeren wanneer de categorieselectie wordt gewijzigd.
Vul de code voor de gebeurtenis in:
private void dataGridViewCategories_SelectionChanged(object sender, EventArgs e)
{
if (this.dbContext != null)
{
var category = (Category)this.dataGridViewCategories.CurrentRow.DataBoundItem;
if (category != null)
{
this.dbContext.Entry(category).Collection(e => e.Products).Load();
}
}
}
In deze code, als er een actieve (niet-null) DbContext
-sessie is, verkrijgen we het Category
exemplaar dat is gebonden aan de momenteel geselecteerde rij van de DataViewGrid
. (Dit kan zijn null
als de laatste rij in de weergave is geselecteerd, die wordt gebruikt om nieuwe categorieën te maken.) Als er een geselecteerde categorie is, wordt de DbContext
instructie gegeven om de producten te laden die aan die categorie zijn gekoppeld. Dit wordt gedaan door:
- Een
EntityEntry
verkrijgen voor hetCategory
exemplaar (dbContext.Entry(category)
) - EF Core laten weten dat we willen werken met de
Products
verzamelingnavigatie van dieCategory
(.Collection(e => e.Products)
) - En ten slotte ef Core vertellen dat we die verzameling producten uit de database willen laden (
.Load();
)
Aanbeveling
Wanneer Load
wordt aangeroepen, zal EF Core alleen toegang hebben tot de database om de producten te laden als ze nog niet zijn ingeladen.
Als de toepassing nu opnieuw wordt uitgevoerd, moet deze de juiste producten laden wanneer een categorie wordt geselecteerd:
Wijzigingen opslaan
Ten slotte kan de knop Opslaan worden verbonden met EF Core, zodat eventuele wijzigingen in de producten en categorieën worden opgeslagen in de database.
Selecteer in de ontwerpfunctie voor het hoofdformulier de knop Opslaan .
Kies in de eigenschappen voor de
Button
, selecteer de events (de bliksemknop) en dubbelklik op het event Click.Vul de code voor de gebeurtenis in:
private void buttonSave_Click(object sender, EventArgs e)
{
this.dbContext!.SaveChanges();
this.dataGridViewCategories.Refresh();
this.dataGridViewProducts.Refresh();
}
SaveChanges
wordt aangeroepen op de DbContext
door deze code, waarmee alle gemaakte wijzigingen in de SQLite-database worden opgeslagen. Als er geen wijzigingen zijn aangebracht, is dit een no-open wordt er geen database-aanroep uitgevoerd. Nadat er is opgeslagen, worden de DataGridView
besturingselementen vernieuwd. Dit komt doordat EF Core gegenereerde primaire-sleutelwaarden leest voor nieuwe producten en categorieën uit de database. Het aanroepen van Refresh
werkt de weergave bij met deze gegenereerde waarden.
De laatste toepassing
Dit is de volledige code voor het hoofdformulier:
using Microsoft.EntityFrameworkCore;
using System.ComponentModel;
namespace GetStartedWinForms
{
public partial class MainForm : Form
{
private ProductsContext? dbContext;
public MainForm()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.dbContext = new ProductsContext();
// Uncomment the line below to start fresh with a new database.
// this.dbContext.Database.EnsureDeleted();
this.dbContext.Database.EnsureCreated();
this.dbContext.Categories.Load();
this.categoryBindingSource.DataSource = dbContext.Categories.Local.ToBindingList();
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
this.dbContext?.Dispose();
this.dbContext = null;
}
private void dataGridViewCategories_SelectionChanged(object sender, EventArgs e)
{
if (this.dbContext != null)
{
var category = (Category)this.dataGridViewCategories.CurrentRow.DataBoundItem;
if (category != null)
{
this.dbContext.Entry(category).Collection(e => e.Products).Load();
}
}
}
private void buttonSave_Click(object sender, EventArgs e)
{
this.dbContext!.SaveChanges();
this.dataGridViewCategories.Refresh();
this.dataGridViewProducts.Refresh();
}
}
}
De toepassing kan nu worden uitgevoerd en producten en categorieën kunnen worden toegevoegd, verwijderd en bewerkt. Als op de knop Opslaan wordt geklikt voordat u de toepassing sluit, worden alle aangebrachte wijzigingen opgeslagen in de database en opnieuw geladen wanneer de toepassing opnieuw wordt gestart. Als niet op Opslaan wordt geklikt, gaan eventuele wijzigingen verloren wanneer de toepassing opnieuw wordt gestart.
Aanbeveling
Aan een DataViewControl
kan een nieuwe categorie of product worden toegevoegd door gebruik te maken van de lege rij onderaan het besturingselement. U kunt een rij verwijderen door deze te selecteren en op de Del-toets te drukken.
Voordat u opslaat
Na het opslaan
U ziet dat de primaire-sleutelwaarden voor de toegevoegde categorie en producten worden ingevuld wanneer op Opslaan wordt geklikt.