Udostępnij za pośrednictwem


Przewodnik po jednostkach samodzielnego śledzenia

Ważne

Nie zalecamy już używania szablonu jednostek samośledzących. Będzie on nadal dostępny tylko na potrzeby obsługi istniejących aplikacji. Jeśli Twoja aplikacja wymaga pracy z odłączonymi grafami jednostek, rozważ inne alternatywy, takie jak jednostki z możliwością śledzenia (technologia podobna do jednostek samośledzących, która jest aktywniej rozwijana przez społeczność) lub napisanie niestandardowego kodu przy użyciu interfejsów API śledzenia zmian niskiego poziomu.

W tym przewodniku przedstawiono scenariusz, w którym usługa Windows Communication Foundation (WCF) uwidacznia operację zwracającą graf jednostki. Następnie aplikacja kliencka manipuluje tym grafem i przesyła modyfikacje operacji usługi, która weryfikuje i zapisuje aktualizacje w bazie danych przy użyciu programu Entity Framework.

Przed ukończeniem tego przewodnika upewnij się, że przeczytasz stronę Jednostki samodzielnego śledzenia.

Ten przewodnik wykonuje następujące czynności:

  • Tworzy bazę danych w celu uzyskania dostępu.
  • Tworzy bibliotekę klas zawierającą model.
  • Zamienia się na szablon Generator jednostek samodzielnego śledzenia.
  • Przenosi klasy jednostek do oddzielnego projektu.
  • Tworzy usługę WCF, która uwidacznia operacje w celu wykonywania zapytań i zapisywania jednostek.
  • Tworzy aplikacje klienckie (Konsola i WPF), które korzystają z usługi.

W tym przewodniku użyjemy funkcji Database First, ale te same techniki mają jednakowe zastosowanie do modelu First.

Wymagania wstępne

Aby ukończyć ten przewodnik, potrzebujesz najnowszej wersji programu Visual Studio.

Tworzenie bazy danych

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 2012, utworzysz bazę danych LocalDB.
  • Jeśli używasz programu Visual Studio 2010, utworzysz bazę danych SQL Express.

Wygenerujmy bazę danych.

  • Otwórz program Visual Studio.
  • Widok —> Eksplorator serwera
  • Kliknij prawym przyciskiem myszy pozycję Połączenie ions danych —> Dodaj Połączenie ion...
  • Jeśli nie nawiązaliśmy połączenia z bazą danych z Eksploratora serwera przed wybraniem programu Microsoft SQL Server jako źródła danych
  • Połączenie do lokalnej bazy danych lub programu SQL Express, w zależności od zainstalowanego
  • Wprowadź steSample jako nazwę bazy danych
  • Wybierz przycisk OK i zostanie wyświetlony monit o utworzenie nowej bazy danych, wybierz pozycję Tak
  • Nowa baza danych będzie teraz wyświetlana w Eksploratorze serwera
  • Jeśli używasz programu Visual Studio 2012
    • Kliknij prawym przyciskiem myszy bazę danych w Eksploratorze serwera 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
  • Jeśli używasz programu Visual Studio 2010
    • Wybierz pozycję Dane —> Edytor Transact SQL —> nowe zapytanie Połączenie ion...
    • Wprowadź ciąg .\SQLEXPRESS jako nazwę serwera, a następnie kliknij przycisk OK
    • Wybierz bazę danych STESample z listy rozwijanej w górnej części edytora zapytań
    • Skopiuj następujący kod SQL do nowego zapytania, a następnie kliknij prawym przyciskiem myszy zapytanie i wybierz polecenie Wykonaj sql
    CREATE TABLE [dbo].[Blogs] (
        [BlogId] INT IDENTITY (1, 1) NOT NULL,
        [Name] NVARCHAR (200) NULL,
        [Url]  NVARCHAR (200) NULL,
        CONSTRAINT [PK_dbo.Blogs] PRIMARY KEY CLUSTERED ([BlogId] ASC)
    );

    CREATE TABLE [dbo].[Posts] (
        [PostId] INT IDENTITY (1, 1) NOT NULL,
        [Title] NVARCHAR (200) NULL,
        [Content] NTEXT NULL,
        [BlogId] INT NOT NULL,
        CONSTRAINT [PK_dbo.Posts] PRIMARY KEY CLUSTERED ([PostId] ASC),
        CONSTRAINT [FK_dbo.Posts_dbo.Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [dbo].[Blogs] ([BlogId]) ON DELETE CASCADE
    );

    SET IDENTITY_INSERT [dbo].[Blogs] ON
    INSERT INTO [dbo].[Blogs] ([BlogId], [Name], [Url]) VALUES (1, N'ADO.NET Blog', N'blogs.msdn.com/adonet')
    SET IDENTITY_INSERT [dbo].[Blogs] OFF
    INSERT INTO [dbo].[Posts] ([Title], [Content], [BlogId]) VALUES (N'Intro to EF', N'Interesting stuff...', 1)
    INSERT INTO [dbo].[Posts] ([Title], [Content], [BlogId]) VALUES (N'What is New', N'More interesting stuff...', 1)

Tworzenie modelu

Najpierw potrzebujemy projektu, aby umieścić model.

  • Plik — Nowy —>> Projekt...
  • Wybierz pozycję Visual C# w okienku po lewej stronie, a następnie pozycję Biblioteka klas
  • Wprowadź nazwę STESample i kliknij przycisk OK.

Teraz utworzymy prosty model w Projektant EF, aby uzyskać dostęp do naszej bazy danych:

  • Projekt —> dodaj nowy element...
  • Wybierz pozycję Dane w okienku po lewej stronie, a następnie ADO.NET model danych jednostki
  • Wprowadź ciąg BloggingModel jako nazwę i kliknij przycisk OK.
  • Wybierz pozycję Generuj z bazy danych i kliknij przycisk Dalej
  • Wprowadź informacje o połączeniu dla bazy danych utworzonej w poprzedniej sekcji
  • Wprowadź wartość BloggingContext jako nazwę parametry połączenia, a następnie kliknij przycisk Dalej
  • Zaznacz pole wyboru obok pozycji Tabele i kliknij przycisk Zakończ

Zamiana na generowanie kodu STE

Teraz musimy wyłączyć domyślną generację kodu i zamienić na jednostki samodzielnego śledzenia.

Jeśli używasz programu Visual Studio 2012

  • Rozwiń węzeł BloggingModel.edmx w Eksplorator rozwiązań i usuń BloggingModel.tt i BloggingModel.Context.ttSpowoduje to wyłączenie domyślnego generowania kodu
  • Kliknij prawym przyciskiem myszy pusty obszar na powierzchni Projektant EF i wybierz pozycję Dodaj element generowania kodu...
  • Wybierz pozycję Online w okienku po lewej stronie i wyszukaj pozycję STE Generator
  • Wybierz szablon STE Generator dla języka C#, wprowadź nazwę STETemplate i kliknij przycisk Dodaj
  • Pliki STETemplate.tt i STETemplate.Context.tt są dodawane zagnieżdżone w pliku BloggingModel.edmx

Jeśli używasz programu Visual Studio 2010

  • Kliknij prawym przyciskiem myszy pusty obszar na powierzchni Projektant EF i wybierz pozycję Dodaj element generowania kodu...
  • Wybierz pozycję Kod w okienku po lewej stronie, a następnie ADO.NET Generator jednostek samodzielnego śledzenia
  • Wprowadź nazwę STETemplate i kliknij przycisk Dodaj.
  • Pliki STETemplate.tt i STETemplate.Context.tt są dodawane bezpośrednio do projektu

Przenoszenie typów jednostek do oddzielnego projektu

Aby korzystać z jednostek samodzielnego śledzenia, nasza aplikacja kliencka musi mieć dostęp do klas jednostek wygenerowanych na podstawie naszego modelu. Ponieważ nie chcemy uwidaczniać całego modelu w aplikacji klienckiej, przeniesiemy klasy jednostek do oddzielnego projektu.

Pierwszym krokiem jest zatrzymanie generowania klas jednostek w istniejącym projekcie:

  • Kliknij prawym przyciskiem myszy STETemplate.tt w Eksplorator rozwiązań i wybierz polecenie Właściwości
  • W oknie Właściwości wyczyść pole TextTemplatingFileGenerator z właściwości CustomTool
  • Rozwiń STETemplate.tt w Eksplorator rozwiązań i usuń wszystkie pliki zagnieżdżone pod nim

Następnie dodamy nowy projekt i wygenerujemy w nim klasy jednostek

  • Plik —> Dodaj —> Projekt...

  • Wybierz pozycję Visual C# w okienku po lewej stronie, a następnie pozycję Biblioteka klas

  • Wprowadź nazwę STESample.Entities i kliknij przycisk OK.

  • Projekt —> dodaj istniejący element...

  • Przejdź do folderu projektu STESample

  • Wybierz, aby wyświetlić wszystkie pliki (*.*)

  • Wybierz plik STETemplate.tt

  • Kliknij strzałkę listy rozwijanej obok przycisku Dodaj i wybierz pozycję Dodaj jako łącze

    Add Linked Template

Upewnimy się również, że klasy jednostek są generowane w tej samej przestrzeni nazw co kontekst. Zmniejsza to tylko liczbę instrukcji użycia, które musimy dodać w całej aplikacji.

  • Kliknij prawym przyciskiem myszy połączoną STETemplate.tt w Eksplorator rozwiązań i wybierz polecenie Właściwości
  • W oknie Właściwości ustaw niestandardową przestrzeń nazw narzędzi na STESample

Kod wygenerowany przez szablon STE będzie wymagał odwołania do pliku System.Runtime.Serialization w celu skompilowania. Ta biblioteka jest wymagana dla atrybutów DataContract i DataMember programu WCF, które są używane w typach jednostek możliwych do serializacji.

  • Kliknij prawym przyciskiem myszy projekt STESample.Entities w Eksplorator rozwiązań i wybierz pozycję Dodaj odwołanie...
    • W programie Visual Studio 2012 — zaznacz pole wyboru obok pozycji System.Runtime.Serialization i kliknij przycisk OK
    • W programie Visual Studio 2010 — wybierz pozycję System.Runtime.Serialization i kliknij przycisk OK

Na koniec projekt z naszym kontekstem będzie potrzebował odwołania do typów jednostek.

  • Kliknij prawym przyciskiem myszy projekt STESample w Eksplorator rozwiązań i wybierz pozycję Dodaj odwołanie...
    • W programie Visual Studio 2012 — wybierz pozycję Rozwiązanie w okienku po lewej stronie, zaznacz pole wyboru obok pozycji STESample.Entities i kliknij przycisk OK
    • W programie Visual Studio 2010 — wybierz kartę Projekty , wybierz pozycję STESample.Entities i kliknij przycisk OK

Uwaga

Inną opcją przenoszenia typów jednostek do oddzielnego projektu jest przeniesienie pliku szablonu zamiast łączenia go z jego lokalizacji domyślnej. Jeśli to zrobisz, musisz zaktualizować zmienną inputFile w szablonie, aby podać ścieżkę względną do pliku edmx (w tym przykładzie będzie to .. \BloggingModel.edmx).

Tworzenie usługi WCF

Teraz nadszedł czas, aby dodać usługę WCF, aby uwidocznić nasze dane, zaczniemy od utworzenia projektu.

  • Plik —> Dodaj —> Projekt...
  • Wybierz pozycję Visual C# w okienku po lewej stronie, a następnie pozycję Aplikacja usługi WCF
  • Wprowadź nazwę STESample.Service i kliknij przycisk OK.
  • Dodawanie odwołania do zestawu System.Data.Entity
  • Dodawanie odwołania do projektów STESample i STESample.Entities

Musimy skopiować parametry połączenia EF do tego projektu, aby można było go znaleźć w czasie wykonywania.

  • Otwórz plik App.Config dla projektu **STESample **i skopiuj element connectionStrings
  • Wklej element connectionStrings jako element podrzędny elementu konfiguracji pliku Web.Config w projekcie STESample.Service

Teraz nadszedł czas na zaimplementowanie rzeczywistej usługi.

  • Otwórz plik IService1.cs i zastąp zawartość następującym kodem
    using System.Collections.Generic;
    using System.ServiceModel;

    namespace STESample.Service
    {
        [ServiceContract]
        public interface IService1
        {
            [OperationContract]
            List<Blog> GetBlogs();

            [OperationContract]
            void UpdateBlog(Blog blog);
        }
    }
  • Otwórz plik Service1.svc i zastąp zawartość następującym kodem
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;

    namespace STESample.Service
    {
        public class Service1 : IService1
        {
            /// <summary>
            /// Gets all the Blogs and related Posts.
            /// </summary>
            public List<Blog> GetBlogs()
            {
                using (BloggingContext context = new BloggingContext())
                {
                    return context.Blogs.Include("Posts").ToList();
                }
            }

            /// <summary>
            /// Updates Blog and its related Posts.
            /// </summary>
            public void UpdateBlog(Blog blog)
            {
                using (BloggingContext context = new BloggingContext())
                {
                    try
                    {
                        // TODO: Perform validation on the updated order before applying the changes.

                        // The ApplyChanges method examines the change tracking information
                        // contained in the graph of self-tracking entities to infer the set of operations
                        // that need to be performed to reflect the changes in the database.
                        context.Blogs.ApplyChanges(blog);
                        context.SaveChanges();

                    }
                    catch (UpdateException)
                    {
                        // To avoid propagating exception messages that contain sensitive data to the client tier
                        // calls to ApplyChanges and SaveChanges should be wrapped in exception handling code.
                        throw new InvalidOperationException("Failed to update. Try your request again.");
                    }
                }
            }        
        }
    }

Korzystanie z usługi z poziomu aplikacji konsolowej

Utwórzmy aplikację konsolową korzystającą z naszej usługi.

  • Plik — Nowy —>> Projekt...
  • Wybierz pozycję Visual C# w okienku po lewej stronie, a następnie pozycję Aplikacja konsolowa
  • Wprowadź nazwę STESample.ConsoleTest i kliknij przycisk OK.
  • Dodawanie odwołania do projektu STESample.Entities

Potrzebujemy odwołania do usługi w naszej usłudze WCF

  • Kliknij prawym przyciskiem myszy projekt STESample.ConsoleTest w Eksplorator rozwiązań i wybierz polecenie Dodaj odwołanie do usługi...
  • Kliknij pozycję Odnajdź
  • Wprowadź ciąg BloggingService jako przestrzeń nazw, a następnie kliknij przycisk OK.

Teraz możemy napisać kod umożliwiający korzystanie z usługi.

  • Otwórz plik Program.cs i zastąp zawartość następującym kodem.
    using STESample.ConsoleTest.BloggingService;
    using System;
    using System.Linq;

    namespace STESample.ConsoleTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Print out the data before we change anything
                Console.WriteLine("Initial Data:");
                DisplayBlogsAndPosts();

                // Add a new Blog and some Posts
                AddBlogAndPost();
                Console.WriteLine("After Adding:");
                DisplayBlogsAndPosts();

                // Modify the Blog and one of its Posts
                UpdateBlogAndPost();
                Console.WriteLine("After Update:");
                DisplayBlogsAndPosts();

                // Delete the Blog and its Posts
                DeleteBlogAndPost();
                Console.WriteLine("After Delete:");
                DisplayBlogsAndPosts();

                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }

            static void DisplayBlogsAndPosts()
            {
                using (var service = new Service1Client())
                {
                    // Get all Blogs (and Posts) from the service
                    // and print them to the console
                    var blogs = service.GetBlogs();
                    foreach (var blog in blogs)
                    {
                        Console.WriteLine(blog.Name);
                        foreach (var post in blog.Posts)
                        {
                            Console.WriteLine(" - {0}", post.Title);
                        }
                    }
                }

                Console.WriteLine();
                Console.WriteLine();
            }

            static void AddBlogAndPost()
            {
                using (var service = new Service1Client())
                {
                    // Create a new Blog with a couple of Posts
                    var newBlog = new Blog
                    {
                        Name = "The New Blog",
                        Posts =
                        {
                            new Post { Title = "Welcome to the new blog"},
                            new Post { Title = "What's new on the new blog"}
                        }
                    };

                    // Save the changes using the service
                    service.UpdateBlog(newBlog);
                }
            }

            static void UpdateBlogAndPost()
            {
                using (var service = new Service1Client())
                {
                    // Get all the Blogs
                    var blogs = service.GetBlogs();

                    // Use LINQ to Objects to find The New Blog
                    var blog = blogs.First(b => b.Name == "The New Blog");

                    // Update the Blogs name
                    blog.Name = "The Not-So-New Blog";

                    // Update one of the related posts
                    blog.Posts.First().Content = "Some interesting content...";

                    // Save the changes using the service
                    service.UpdateBlog(blog);
                }
            }

            static void DeleteBlogAndPost()
            {
                using (var service = new Service1Client())
                {
                    // Get all the Blogs
                    var blogs = service.GetBlogs();

                    // Use LINQ to Objects to find The Not-So-New Blog
                    var blog = blogs.First(b => b.Name == "The Not-So-New Blog");

                    // Mark all related Posts for deletion
                    // We need to call ToList because each Post will be removed from the
                    // Posts collection when we call MarkAsDeleted
                    foreach (var post in blog.Posts.ToList())
                    {
                        post.MarkAsDeleted();
                    }

                    // Mark the Blog for deletion
                    blog.MarkAsDeleted();

                    // Save the changes using the service
                    service.UpdateBlog(blog);
                }
            }
        }
    }

Teraz możesz uruchomić aplikację, aby zobaczyć ją w działaniu.

  • Kliknij prawym przyciskiem myszy projekt STESample.ConsoleTest w Eksplorator rozwiązań i wybierz polecenie Debuguj —> Uruchom nowe wystąpienie

Po wykonaniu aplikacji zostaną wyświetlone następujące dane wyjściowe.

Initial Data:
ADO.NET Blog
- Intro to EF
- What is New

After Adding:
ADO.NET Blog
- Intro to EF
- What is New
The New Blog
- Welcome to the new blog
- What's new on the new blog

After Update:
ADO.NET Blog
- Intro to EF
- What is New
The Not-So-New Blog
- Welcome to the new blog
- What's new on the new blog

After Delete:
ADO.NET Blog
- Intro to EF
- What is New

Press any key to exit...

Korzystanie z usługi z poziomu aplikacji WPF

Utwórzmy aplikację WPF korzystającą z naszej usługi.

  • Plik — Nowy —>> Projekt...
  • Wybierz pozycję Visual C# w okienku po lewej stronie, a następnie pozycję Aplikacja WPF
  • Wprowadź nazwę STESample.WPFTest i kliknij przycisk OK.
  • Dodawanie odwołania do projektu STESample.Entities

Potrzebujemy odwołania do usługi w naszej usłudze WCF

  • Kliknij prawym przyciskiem myszy projekt STESample.WPFTest w Eksplorator rozwiązań i wybierz polecenie Dodaj odwołanie do usługi...
  • Kliknij pozycję Odnajdź
  • Wprowadź ciąg BloggingService jako przestrzeń nazw, a następnie kliknij przycisk OK.

Teraz możemy napisać kod umożliwiający korzystanie z usługi.

  • Otwórz plik MainWindow.xaml i zastąp zawartość następującym kodem.
    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:STESample="clr-namespace:STESample;assembly=STESample.Entities"
        mc:Ignorable="d" x:Class="STESample.WPFTest.MainWindow"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">

        <Window.Resources>
            <CollectionViewSource
                x:Key="blogViewSource"
                d:DesignSource="{d:DesignInstance {x:Type STESample:Blog}, CreateList=True}"/>
            <CollectionViewSource
                x:Key="blogPostsViewSource"
                Source="{Binding Posts, Source={StaticResource blogViewSource}}"/>
        </Window.Resources>

        <Grid DataContext="{StaticResource blogViewSource}">
            <DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True"
                      ItemsSource="{Binding}" Margin="10,10,10,179">
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding BlogId}" Header="Id" Width="Auto" IsReadOnly="True" />
                    <DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="Auto"/>
                    <DataGridTextColumn Binding="{Binding Url}" Header="Url" Width="Auto"/>
                </DataGrid.Columns>
            </DataGrid>
            <DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True"
                      ItemsSource="{Binding Source={StaticResource blogPostsViewSource}}" Margin="10,145,10,38">
                <DataGrid.Columns>
                    <DataGridTextColumn Binding="{Binding PostId}" Header="Id" Width="Auto"  IsReadOnly="True"/>
                    <DataGridTextColumn Binding="{Binding Title}" Header="Title" Width="Auto"/>
                    <DataGridTextColumn Binding="{Binding Content}" Header="Content" Width="Auto"/>
                </DataGrid.Columns>
            </DataGrid>
            <Button Width="68" Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom"
                    Margin="0,0,10,10" Click="buttonSave_Click">Save</Button>
        </Grid>
    </Window>
  • Otwórz kod za elementem MainWindow (MainWindow.xaml.cs) i zastąp zawartość następującym kodem
    using STESample.WPFTest.BloggingService;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;

    namespace STESample.WPFTest
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }

            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                using (var service = new Service1Client())
                {
                    // Find the view source for Blogs and populate it with all Blogs (and related Posts)
                    // from the Service. The default editing functionality of WPF will allow the objects
                    // to be manipulated on the screen.
                    var blogsViewSource = (CollectionViewSource)this.FindResource("blogViewSource");
                    blogsViewSource.Source = service.GetBlogs().ToList();
                }
            }

            private void buttonSave_Click(object sender, RoutedEventArgs e)
            {
                using (var service = new Service1Client())
                {
                    // Get the blogs that are bound to the screen
                    var blogsViewSource = (CollectionViewSource)this.FindResource("blogViewSource");
                    var blogs = (List<Blog>)blogsViewSource.Source;

                    // Save all Blogs and related Posts
                    foreach (var blog in blogs)
                    {
                        service.UpdateBlog(blog);
                    }

                    // Re-query for data to get database-generated keys etc.
                    blogsViewSource.Source = service.GetBlogs().ToList();
                }
            }
        }
    }

Teraz możesz uruchomić aplikację, aby zobaczyć ją w działaniu.

  • Kliknij prawym przyciskiem myszy projekt STESample.WPFTest w Eksplorator rozwiązań i wybierz polecenie Debuguj —> Uruchom nowe wystąpienie
  • Możesz manipulować danymi przy użyciu ekranu i zapisywać je za pośrednictwem usługi przy użyciu przycisku Zapisz

WPF Main window