Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Important
Ten dokument jest prawidłowy tylko dla platformy WPF na platformie .NET Framework
W tym dokumencie opisano powiązanie danych dla platformy WPF na platformie .NET Framework. W przypadku nowych projektów platformy .NET zalecamy użycie programu EF Core zamiast programu Entity Framework 6. Dokumentacja dotycząca powiązania danych w programie EF Core znajduje się tutaj: Wprowadzenie do platformy WPF.
W tym przewodniku krok po kroku pokazano, jak powiązać typy POCO z kontrolkami WPF w formularzu "master-detail". Aplikacja używa interfejsów API programu Entity Framework do wypełniania obiektów danymi z bazy danych, śledzenia zmian i utrwalania danych w bazie danych.
Model definiuje dwa typy uczestniczące w relacji jeden-do-wielu: Kategoria (główny\macierzysty) i Produkt (zależny\szczegółowy). Następnie narzędzia programu Visual Studio służą do powiązania typów zdefiniowanych w modelu z kontrolkami WPF. Struktura powiązania danych WPF umożliwia nawigację między powiązanymi obiektami: wybieranie wierszy w widoku głównym powoduje, że widok szczegółów zostanie zaktualizowany przy użyciu odpowiednich danych podrzędnych.
Zrzuty ekranu i listy kodu w tym przewodniku pochodzą z programu Visual Studio 2013, ale możesz ukończyć ten przewodnik za pomocą programu Visual Studio 2012 lub Visual Studio 2010.
Użyj opcji "Object" do tworzenia źródeł danych WPF
W poprzedniej wersji programu Entity Framework zalecamy użycie opcji Baza danych podczas tworzenia nowego źródła danych na podstawie modelu utworzonego za pomocą projektanta EF. Wynika to z faktu, że projektant wygeneruje kontekst pochodzący z klasy ObjectContext i jednostek, które pochodzą z obiektu EntityObject. Użycie opcji Baza danych ułatwi napisanie najlepszego kodu do interakcji z tą powierzchnią interfejsu API.
Projektanci ef dla programów Visual Studio 2012 i Visual Studio 2013 generują kontekst pochodzący z klasy jednostek DbContext wraz z prostymi klasami jednostek POCO. W programie Visual Studio 2010 zalecamy zamianę na szablon generowania kodu, który używa elementu DbContext zgodnie z opisem w dalszej części tego przewodnika.
W przypadku korzystania z powierzchni interfejsu API DbContext należy użyć opcji Obiekt podczas tworzenia nowego źródła danych, jak pokazano w tym przewodniku.
W razie potrzeby możesz przywrócić generowanie kodu opartego na obiekcie ObjectContext dla modeli utworzonych za pomocą projektanta EF.
Pre-Requisites
Aby ukończyć ten przewodnik, musisz mieć zainstalowany program Visual Studio 2013, Visual Studio 2012 lub Visual Studio 2010.
Jeśli używasz programu Visual Studio 2010, musisz również zainstalować pakiet NuGet. Aby uzyskać więcej informacji, zobacz Instalowanie pakietu NuGet.
Tworzenie aplikacji
- Otwórz program Visual Studio.
- Plik — Nowy —>> Projekt....
- Wybierz Windows w panelu po lewej stronie i WPFApplication w panelu po prawej stronie
- Wprowadź WPFwithEFSample jako nazwę
- Wybierz OK
Instalowanie pakietu NuGet programu Entity Framework
- W Eksploratorze rozwiązań kliknij prawym przyciskiem myszy projekt WinFormswithEFSample
- Wybierz pozycję Zarządzaj pakietami NuGet...
- W oknie dialogowym Zarządzanie pakietami NuGet wybierz kartę Online i wybierz pakiet EntityFramework
- Kliknij pozycję Zainstaluj
Note
Oprócz zestawu EntityFramework dodano również odwołanie do elementu System.ComponentModel.DataAnnotations. Jeśli projekt ma odwołanie do elementu System.Data.Entity, zostanie on usunięty po zainstalowaniu pakietu EntityFramework. Zestaw System.Data.Entity nie jest już używany dla aplikacji platformy Entity Framework 6.
Definiowanie modelu
W tym przewodniku można wybrać wdrożenie modelu przy użyciu funkcji Code First lub EF Designer. Wykonaj jedną z dwóch poniższych sekcji.
Opcja 1. Definiowanie modelu przy użyciu funkcji Code First
W tej sekcji przedstawiono sposób tworzenia modelu i skojarzonej z nią bazy danych przy użyciu funkcji Code First. Przejdź do następnej sekcji (Opcja 2: Definiowanie modelu przy użyciu funkcji Database First), jeśli wolisz użyć usługi Database First do odtworzenia modelu z bazy danych przy użyciu projektanta EF
W przypadku korzystania z programowania Code First zwykle zaczynasz od pisania klas programu .NET Framework, które definiują model koncepcyjny (domena).
- Dodaj nową klasę do wpFwithEFSample:
- Kliknij prawym przyciskiem myszy nazwę projektu
- Wybierz pozycję Dodaj, a następnie pozycję Nowy element
- Wybierz Klasę i wprowadź Product jako nazwę klasy.
- Zastąp definicję klasy Product następującym kodem:
namespace WPFwithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }
}
}
- Dodaj klasę Category z następującą definicją:
using System.Collections.ObjectModel;
namespace WPFwithEFSample
{
public class Category
{
public Category()
{
this.Products = new ObservableCollection<Product>();
}
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual ObservableCollection<Product> Products { get; private set; }
}
}
Właściwość Products w klasie Category i właściwości Category w klasie Product to właściwości nawigacji. W programie Entity Framework właściwości nawigacji zapewniają sposób nawigowania po relacji między dwoma typami jednostek.
Oprócz definiowania jednostek należy zdefiniować klasę dziedziczącą po DbContext i eksponującą właściwości DbSet<TEntity>. Właściwości DbSet<TEntity> poinformują kontekst o typach, które mają zostać uwzględnione w modelu.
Wystąpienie typu pochodnego DbContext zarządza obiektami encji podczas działania, co obejmuje wypełnianie obiektów danymi z bazy danych, śledzenie zmian i zapisywanie danych w niej.
- Dodaj nową klasę ProductContext do projektu z następującą definicją:
using System.Data.Entity;
namespace WPFwithEFSample
{
public class ProductContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
}
Skompiluj projekt.
Opcja 2. Definiowanie modelu przy użyciu funkcji Database First
W tej sekcji pokazano, jak używać Database First do odtwarzania modelu z bazy danych przy użyciu projektanta EF. Jeśli poprzednia sekcja została ukończona (Opcja 1: Definiowanie modelu przy użyciu funkcji Code First), pomiń tę sekcję i przejdź prosto do sekcji Ładowanie z opóźnieniem .
Tworzenie istniejącej bazy danych
Zazwyczaj, gdy korzystasz z istniejącej bazy danych, jest ona już utworzona, ale w tym przewodniku musimy utworzyć bazę danych, aby można było z niej skorzystać.
Serwer bazy danych zainstalowany w programie Visual Studio różni się w zależności od zainstalowanej wersji programu Visual Studio:
- Jeśli używasz programu Visual Studio 2010, utworzysz bazę danych SQL Express.
- Jeśli używasz programu Visual Studio 2012, utworzysz bazę danych LocalDB .
Wygenerujmy bazę danych.
Widok —> Eksplorator serwera
Kliknij prawym przyciskiem myszy pozycję Połączenia danych —> Dodaj połączenie...
Jeśli wcześniej nie nawiązałeś połączenia z bazą danych przy użyciu Eksploratora serwera, będziesz musiał wybrać Microsoft SQL Server jako źródło danych.
Połącz się z bazą danych LocalDB lub SQL Express, w zależności od zainstalowanej bazy danych, a następnie wprowadź ciąg Products jako nazwę bazy danych
Wybierz przycisk OK i zostanie wyświetlony monit o utworzenie nowej bazy danych, wybierz pozycję Tak
Nowa baza danych pojawi się teraz w Eksploratorze serwera, kliknij ją prawym przyciskiem myszy i wybierz pozycję Nowe zapytanie
Skopiuj następujący kod SQL do nowego zapytania, a następnie kliknij prawym przyciskiem myszy zapytanie i wybierz polecenie Wykonaj
CREATE TABLE [dbo].[Categories] (
[CategoryId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
)
CREATE TABLE [dbo].[Products] (
[ProductId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[CategoryId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
)
CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE
Model inżynierii odwrotnej
Użyjemy projektanta platformy Entity Framework, który zostanie uwzględniony w programie Visual Studio, aby utworzyć nasz model.
Projekt —> dodaj nowy element...
Wybierz pozycję Dane z menu po lewej stronie, a następnie Model danych jednostki ADO.NET
Wprowadź productModel jako nazwę i kliknij przycisk OK
Spowoduje to uruchomienie Kreatora modelu danych jednostki
Wybierz pozycję Generuj z bazy danych i kliknij przycisk Dalej
Wybierz połączenie z bazą danych utworzoną w pierwszej sekcji, wprowadź productContext jako nazwę parametrów połączenia, a następnie kliknij przycisk Dalej.
Kliknij pole wyboru obok pozycji "Tabele", aby zaimportować wszystkie tabele, a następnie kliknij przycisk Zakończ.
Po zakończeniu procesu inżynierii wstecznej nowy model zostanie dodany do projektu i otwarty, do obejrzenia go w Projektancie Entity Framework. Plik App.config został również dodany do projektu ze szczegółami połączenia dla bazy danych.
Dodatkowe kroki w programie Visual Studio 2010
Jeśli pracujesz w programie Visual Studio 2010, musisz zaktualizować projektanta ef do używania generowania kodu EF6.
- Kliknij prawym przyciskiem myszy puste miejsce modelu w projektancie EF i wybierz polecenie Dodaj element generowania kodu...
- Wybierz pozycję Szablony online z menu po lewej stronie i wyszukaj pozycję DbContext
- Wybierz generator EF 6.x DbContext dla języka C#, wprowadź ciąg ProductsModel jako nazwę, a następnie kliknij przycisk Dodaj
Aktualizowanie generowania kodu na potrzeby powiązania danych
Program EF generuje kod z modelu przy użyciu szablonów T4. Szablony dostarczane z programem Visual Studio lub pobrane z galerii programu Visual Studio są przeznaczone do użytku ogólnego przeznaczenia. Oznacza to, że jednostki wygenerowane na podstawie tych szablonów mają proste właściwości ICollection<T> . Jednak w przypadku tworzenia powiązania danych przy użyciu WPF pożądane jest użycie funkcji ObservableCollection dla właściwości kolekcji, dzięki czemu WPF może śledzić zmiany wprowadzone w kolekcjach. W tym celu zmodyfikujemy szablony tak, aby używały funkcji ObservableCollection.
Otwórz Eksploratora rozwiązań i znajdź plik ProductModel.edmx
Znajdź plik ProductModel.tt , który zostanie zagnieżdżony w pliku ProductModel.edmx
Kliknij dwukrotnie plik ProductModel.tt, aby otworzyć go w edytorze programu Visual Studio
Znajdź i zamień dwa wystąpienia elementu "ICollection" na "ObservableCollection". Znajdują się one w przybliżeniu w liniach 296 i 484.
Znajdź i zastąp pierwsze wystąpienie elementu "HashSet" ciągiem "ObservableCollection". To zdarzenie znajduje się w przybliżeniu na 50. wierszu. Nie zamieniaj drugiego wystąpienia elementu HashSet znalezionego w dalszej części kodu.
Znajdź i zastąp jedyne wystąpienie elementu "System.Collections.Generic" ciągiem "System.Collections.ObjectModel". Znajduje się on w przybliżeniu w wierszu 424.
Zapisz plik ProductModel.tt. Powinno to spowodować ponowne wygenerowanie kodu jednostek. Jeśli kod nie zostanie wygenerowany automatycznie, kliknij prawym przyciskiem myszy ProductModel.tt i wybierz polecenie "Uruchom narzędzie niestandardowe".
Jeśli teraz otworzysz plik Category.cs (który jest zagnieżdżony w ProductModel.tt), zobaczysz, że kolekcja Products ma typ ObservableCollection<Product>.
Skompiluj projekt.
Późne ładowanie
Właściwość Products w klasie Category i właściwości Category w klasie Product to właściwości nawigacji. W programie Entity Framework właściwości nawigacji zapewniają sposób nawigowania po relacji między dwoma typami jednostek.
Program EF umożliwia automatyczne ładowanie powiązanych jednostek z bazy danych przy pierwszym uzyskiwaniu dostępu do właściwości nawigacji. W przypadku tego typu ładowania (nazywanego ładowaniem leniwym) należy pamiętać, że przy pierwszym uzyskiwaniu dostępu do każdej właściwości nawigacji zostanie wykonane oddzielne zapytanie względem bazy danych, jeśli zawartość nie znajduje się jeszcze w kontekście.
W przypadku korzystania z typów jednostek POCO program EF osiąga leniwe ładowanie, tworząc wystąpienia pochodnych typów proxy podczas wykonywania, a następnie nadpisując właściwości wirtualne w klasach, aby dodać punkt zaczepienia ładowania. Aby uzyskać leniwe ładowanie powiązanych obiektów, właściwości nawigacyjne muszą być zadeklarowane jako publiczne i wirtualne (Overridable w Visual Basic), a klasa nie może być zapieczętowana (NotOverridable w Visual Basic). Kiedy używamy podejścia Database First, właściwości nawigacyjne są automatycznie oznaczane jako wirtualne, aby umożliwić leniwe ładowanie. W sekcji Code First zdecydowaliśmy się na wirtualne właściwości nawigacyjne z tego samego powodu.
Wiązanie obiektu z kontrolkami
Dodaj klasy zdefiniowane w modelu jako źródła danych dla tej aplikacji WPF.
Kliknij dwukrotnie plik MainWindow.xaml w Eksploratorze rozwiązań, aby otworzyć formularz główny
Z menu głównego wybierz pozycję Projekt —> Dodaj nowe źródło danych... (w programie Visual Studio 2010 musisz wybrać pozycję Dane —> Dodaj nowe źródło danych...)
W obszarze Wybierz typ źródła danychwindow wybierz pozycję Obiekt , a następnie kliknij przycisk Dalej
W oknie dialogowym Wybieranie obiektów danych rozwiń WPFwithEFSample dwukrotnie i wybierz Kategoria
Nie ma potrzeby wybierania źródła danych Produkt, ponieważ przejdziemy do niego za pośrednictwem właściwości Product w źródle danych Category
Kliknij przycisk Zakończ.
Okno Źródła danych jest otwierane obok okna MainWindow.xaml Jeśli okno Źródła danych nie jest wyświetlane, wybierz pozycję Widok —> Inne źródła danych systemu Windows —> Źródła danych
Naciśnij ikonę pinezki, aby okno Źródła danych nie ukrywało się automatycznie. Jeśli okno było już widoczne, może być konieczne naciśnięcie przycisku odświeżania.
Wybierz źródło danych kategorii i przeciągnij je na formularz.
Podczas przeciągnięcia tego źródła wystąpiły następujące zdarzenia:
- Zasób categoryViewSource i kontrolka categoryDataGrid zostały dodane do języka XAML
- Właściwość DataContext elementu nadrzędnej siatki została ustawiona na "{StaticResource categoryViewSource }". Zasób categoryViewSource służy jako źródło powiązania elementu zewnętrznej\nadrzędnej siatki. Elementy wewnętrznej siatki dziedziczą następnie wartość DataContext z nadrzędnej siatki (właściwość ItemsSource kategoriiDataGrid jest ustawiona na "{Binding}")
<Window.Resources>
<CollectionViewSource x:Key="categoryViewSource"
d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource categoryViewSource}">
<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding}" Margin="13,13,43,191"
RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="categoryIdColumn" Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}"
Header="Name" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Dodawanie siatki szczegółów
Teraz, gdy mamy siatkę do wyświetlenia kategorii, dodajmy siatkę szczegółów w celu wyświetlenia skojarzonych produktów.
- Wybierz właściwość Products z obszaru Źródło danych Kategoria i przeciągnij ją na formularz.
- Zasób categoryProductsViewSource i siatka productDataGrid są dodawane do języka XAML
- Ścieżka powiązania dla tego zasobu jest ustawiona na „Products”
- Struktura powiązania danych WPF zapewnia, że tylko produkty powiązane z wybraną kategorią są wyświetlane w elemencie productDataGrid
- Z przybornika przeciągnij element Button na formę. Ustaw właściwość Name na przyciskZapisz i właściwość Content na Zapisz.
Formularz powinien wyglądać podobnie do następującego:
Dodawanie kodu obsługującego interakcję z danymi
Nadszedł czas, aby dodać niektóre programy obsługi zdarzeń do okna głównego.
W oknie XAML kliknij <element Okno , co powoduje wybranie głównego okna
W oknie Właściwości wybierz pozycję Zdarzenia w prawym górnym rogu, a następnie kliknij dwukrotnie pole tekstowe po prawej stronie załadowanej etykiety
Dodaj również zdarzenie Kliknij dla przycisku Zapisz, dwukrotnie klikając przycisk Zapisz w projektancie.
Spowoduje to wyświetlenie kodu formularza. Teraz zmodyfikujemy kod, aby używać obiektu ProductContext do wykonywania dostępu do danych. Zaktualizuj kod dla systemu MainWindow, jak pokazano poniżej.
Kod deklaruje długotrwałe wystąpienie elementu ProductContext. Obiekt ProductContext służy do wykonywania zapytań i zapisywania danych w bazie danych. Metoda Dispose() w wystąpieniu ProductContext jest następnie wywoływana z zastąpionej metody OnClosing . Komentarze kodu zawierają szczegółowe informacje o kodzie.
using System.Data.Entity;
using System.Linq;
using System.Windows;
namespace WPFwithEFSample
{
public partial class MainWindow : Window
{
private ProductContext _context = new ProductContext();
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
System.Windows.Data.CollectionViewSource categoryViewSource =
((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource")));
// Load is an extension method on IQueryable,
// defined in the System.Data.Entity namespace.
// This method enumerates the results of the query,
// similar to ToList but without creating a list.
// When used with Linq to Entities this method
// creates entity objects and adds them to the context.
_context.Categories.Load();
// After the data is loaded call the DbSet<T>.Local property
// to use the DbSet<T> as a binding source.
categoryViewSource.Source = _context.Categories.Local;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
// When you delete an object from the related entities collection
// (in this case Products), the Entity Framework doesn’t mark
// these child entities as deleted.
// Instead, it removes the relationship between the parent and the child
// by setting the parent reference to null.
// So we manually have to delete the products
// that have a Category reference set to null.
// The following code uses LINQ to Objects
// against the Local collection of Products.
// The ToList call is required because otherwise the collection will be modified
// by the Remove call while it is being enumerated.
// In most other situations you can use LINQ to Objects directly
// against the Local property without using ToList first.
foreach (var product in _context.Products.Local.ToList())
{
if (product.Category == null)
{
_context.Products.Remove(product);
}
}
_context.SaveChanges();
// Refresh the grids so the database generated values show up.
this.categoryDataGrid.Items.Refresh();
this.productsDataGrid.Items.Refresh();
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
this._context.Dispose();
}
}
}
Testowanie aplikacji WPF
Skompiluj i uruchom aplikację. Jeśli użyto funkcji Code First, zobaczysz, że zostanie utworzona baza danych WPFwithEFSample.ProductContext .
Wprowadź nazwę kategorii w górnej siatce i nazwy produktów w dolnej siatce Nie wprowadzaj niczego w kolumnach identyfikatorów, ponieważ klucz podstawowy jest generowany przez bazę danych
Naciśnij przycisk Zapisz , aby zapisać dane w bazie danych
Po wywołaniu metody SaveChanges() elementu DbContext identyfikatory są wypełniane wartościami wygenerowanymi przez bazę danych. Ponieważ wywołaliśmy Refresh() po SaveChanges(), kontrolki DataGrid również zostały zaktualizowane z nowymi wartościami.
Dodatkowe zasoby
Aby dowiedzieć się więcej na temat powiązania danych z kolekcjami przy użyciu platformy WPF, zobacz ten temat w dokumentacji platformy WPF.