Návod k entitě s vlastním sledováním

Důležité

Šablonu entit s vlastním sledováním už nedoporučujeme používat. Bude nadále k dispozici jen pro podporu existujících aplikací. Pokud vaše aplikace vyžaduje práci s odpojenými grafy entit, zvažte jiné alternativy, jako jsou sledovatelné entity, což je technologie podobná entitám s vlastním sledováním, která je aktivněji vyvíjena komunitou, nebo vytvoření vlastního kódu pro sledování změn pomocí nízkoúrovňových rozhraní API.

Tento názorný postup ukazuje scénář, ve kterém služba WCF (Windows Communication Foundation) zveřejňuje operaci, která vrací graf entit. Dále klientská aplikace manipuluje s tímto grafem a odesílá změny do operace služby, která ověřuje a ukládá aktualizace do databáze pomocí Entity Frameworku.

Před dokončením tohoto návodu se ujistěte, že jste si přečetli stránku Entity samoobslužného sledování.

Tento názorný postup provede následující akce:

  • Vytvoří databázi pro přístup.
  • Vytvoří knihovnu tříd, která obsahuje model.
  • Prohodí se do šablony Generátor entit pro samosledování.
  • Přesune třídy entit do samostatného projektu.
  • Vytvoří službu WCF, která zveřejňuje operace pro dotazování a ukládání entit.
  • Vytvoří klientské aplikace (konzolu a WPF), které službu využívají.

V tomto názorném postupu použijeme Database First, ale stejné techniky platí stejně jako u modelu First.

Předpoklady

K dokončení tohoto názorného postupu budete potřebovat nejnovější verzi sady Visual Studio.

Vytvoření databáze

Databázový server nainstalovaný se sadou Visual Studio se liší v závislosti na nainstalované verzi sady Visual Studio:

  • Pokud používáte Visual Studio 2012, budete vytvářet databázi LocalDB.
  • Pokud používáte Visual Studio 2010, budete vytvářet databázi SQL Express.

Pojďme pokračovat a vygenerovat databázi.

  • Otevřete sadu Visual Studio.
  • Zobrazení –> Průzkumník serveru
  • Klikněte pravým tlačítkem na Připojení iony dat –> přidat Připojení ion...
  • Pokud jste se ještě nepřipojili k databázi z Průzkumníka serveru, musíte jako zdroj dat vybrat Microsoft SQL Server .
  • Připojení k LocalDB nebo SQL Expressu v závislosti na tom, který z nich jste nainstalovali
  • Jako název databáze zadejte STESample .
  • Vyberte OK a zobrazí se dotaz, jestli chcete vytvořit novou databázi, vyberte Ano.
  • Nová databáze se teď zobrazí v Průzkumníku serveru.
  • Pokud používáte Visual Studio 2012
    • Klikněte pravým tlačítkem myši na databázi v Průzkumníku serveru a vyberte Nový dotaz.
    • Zkopírujte následující SQL do nového dotazu, klikněte pravým tlačítkem myši na dotaz a vyberte Spustit.
  • Pokud používáte Visual Studio 2010
    • Výběr dat –> Editor Transact SQL –> Nový dotaz Připojení ion...
    • Jako název serveru zadejte .\SQLEXPRESS a klikněte na OK.
    • V rozevíracím seznamu v horní části editoru dotazů vyberte databázi STESample.
    • Zkopírujte následující SQL do nového dotazu, klikněte pravým tlačítkem myši na dotaz a vyberte Spustit 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)

Vytvoření modelu

Nejprve potřebujeme projekt pro vložení modelu.

  • Soubor -> Nový -> Projekt...
  • V levém podokně vyberte Visual C# a pak knihovnu tříd.
  • Jako název zadejte STESample a klikněte na OK.

Teď v EF Designeru vytvoříme jednoduchý model pro přístup k naší databázi:

  • Projekt –> přidat novou položku...
  • V levém podokně vyberte Data a pak ADO.NET Entity Data Model.
  • Jako název zadejte BloggingModel a klikněte na OK.
  • Vyberte Vygenerovat z databáze a klikněte na Další.
  • Zadejte informace o připojení pro databázi, kterou jste vytvořili v předchozí části.
  • Jako název připojovací řetězec zadejte BloggingContext a klikněte na Další.
  • Zaškrtněte políčko vedle tabulek a klikněte na Dokončit.

Přepnutí na generování kódu STE

Teď musíme zakázat výchozí generování kódu a prohodit entity samoobslužného sledování.

Pokud používáte Visual Studio 2012

  • Rozbalte BloggingModel.edmx v Průzkumník řešení a odstraňte BloggingModel.tt a BloggingModel.Context.ttTím se zakáže výchozí generování kódu.
  • Pravým tlačítkem myši klikněte na prázdnou oblast na ploše EF Designeru a vyberte Přidat položku generování kódu...
  • V levém podokně vyberte Online a vyhledejte generátor STE.
  • Vyberte šablonu Generátor STE pro C#, jako název zadejte STETemplate a klikněte na Přidat.
  • Soubory STETemplate.tt a STETemplate.Context.tt se přidají do souboru BloggingModel.edmx.

Pokud používáte Visual Studio 2010

  • Pravým tlačítkem myši klikněte na prázdnou oblast na ploše EF Designeru a vyberte Přidat položku generování kódu...
  • V levém podokně vyberte Kód a pak ADO.NET Generátor entit pro samoobslužné sledování.
  • Jako název zadejte STETemplate a klikněte na Přidat.
  • Soubory STETemplate.tt a STETemplate.Context.tt se přidají přímo do projektu.

Přesun typů entit do samostatného projektu

Abychom mohli používat entity samoobslužného sledování, potřebuje klientská aplikace přístup ke třídám entit vygenerovaným z našeho modelu. Protože nechceme vystavit celý model klientské aplikaci, přesuneme třídy entit do samostatného projektu.

Prvním krokem je zastavení generování tříd entit v existujícím projektu:

  • Klikněte pravým tlačítkem na STETemplate.tt v Průzkumník řešení a vyberte Vlastnosti.
  • V okně Properties vymazat TextTemplatingFileGenerator z CustomTool vlastnost
  • Rozbalte STETemplate.tt v Průzkumník řešení a odstraňte všechny soubory vnořené pod ním.

Dále přidáme nový projekt a vygenerujeme v něm třídy entit.

  • Soubor -> Přidat -> Projekt...

  • V levém podokně vyberte Visual C# a pak knihovnu tříd.

  • Jako název zadejte STESample.Entities a klikněte na OK.

  • Projekt –> přidat existující položku...

  • Přejděte do složky projektu STESample .

  • Vyberte, pokud chcete zobrazit všechny soubory (*.*)

  • Výběr souboru STETemplate.tt

  • Klikněte na šipku rozevíracího seznamu vedle tlačítka Přidat a vyberte Přidat jako odkaz.

    Add Linked Template

Také zajistíme, aby se třídy entit vygenerovaly ve stejném oboru názvů jako kontext. Tím se sníží počet příkazů using, které potřebujeme přidat v celé aplikaci.

  • Klikněte pravým tlačítkem myši na propojené STETemplate.tt v Průzkumník řešení a vyberte Vlastnosti.
  • V okně Vlastnosti nastavte vlastní obor názvů nástrojů na STESample.

Kód vygenerovaný šablonou STE bude k kompilaci potřebovat odkaz na System.Runtime.Serialization . Tato knihovna je potřebná pro atributy WCF DataContract a DataMember , které se používají u serializovatelných typů entit.

  • Klikněte pravým tlačítkem na projekt STESample.Entities v Průzkumník řešení a vyberte Přidat odkaz...
    • V sadě Visual Studio 2012 – zaškrtněte políčko vedle system.Runtime.Serialization a klepněte na tlačítko OK
    • V sadě Visual Studio 2010 – vyberte System.Runtime.Serialization a klepněte na tlačítko OK

Nakonec bude projekt s naším kontextem potřebovat odkaz na typy entit.

  • Klikněte pravým tlačítkem na projekt STESample v Průzkumník řešení a vyberte Přidat odkaz...
    • V sadě Visual Studio 2012 – v levém podokně vyberte Řešení , zaškrtněte políčko vedle položky STESample.Entities a klikněte na OK.
    • V sadě Visual Studio 2010 – vyberte kartu Projekty , vyberte STESample.Entities a klikněte na OK.

Poznámka

Další možností pro přesun typů entit do samostatného projektu je přesunout soubor šablony místo propojení z výchozího umístění. Pokud to uděláte, budete muset aktualizovat proměnnou inputFile v šabloně tak, aby poskytovala relativní cestu k souboru edmx (v tomto příkladu by to bylo .. \BloggingModel.edmx).

Vytvoření služby WCF

Teď je čas přidat službu WCF, která zveřejní naše data, začneme vytvořením projektu.

  • Soubor -> Přidat -> Projekt...
  • V levém podokně vyberte Visual C# a pak aplikaci služby WCF.
  • Jako název zadejte STESample.Service a klikněte na OK.
  • Přidání odkazu na sestavení System.Data.Entity
  • Přidání odkazu na projekty STESample a STESample.Entities

Potřebujeme zkopírovat ef připojovací řetězec do tohoto projektu, aby byl nalezen za běhu.

  • Otevřete soubor App.Config pro projekt **STESample **a zkopírujte element connectionStrings.
  • Vložte element connectionStrings jako podřízený prvek konfiguračního prvku souboru Web.Config v projektu STESample.Service.

Teď je čas implementovat skutečnou službu.

  • Otevřete soubor IService1.cs a nahraďte obsah následujícím kódem.
    using System.Collections.Generic;
    using System.ServiceModel;

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

            [OperationContract]
            void UpdateBlog(Blog blog);
        }
    }
  • Otevřete Service1.svc a nahraďte obsah následujícím kódem.
    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.");
                    }
                }
            }        
        }
    }

Využívání služby z konzolové aplikace

Pojďme vytvořit konzolovou aplikaci, která používá naši službu.

  • Soubor -> Nový -> Projekt...
  • V levém podokně vyberte Visual C# a pak konzolovou aplikaci.
  • Jako název zadejte STESample.ConsoleTest a klikněte na OK.
  • Přidání odkazu na projekt STESample.Entities

Potřebujeme odkaz na službu WCF.

  • Klikněte pravým tlačítkem myši na projekt STESample.ConsoleTest v Průzkumník řešení a vyberte Přidat odkaz na službu...
  • Klikněte na Zjistit.
  • Jako obor názvů zadejte BloggingService a klikněte na OK.

Teď můžeme napsat nějaký kód, který bude službu využívat.

  • Otevřete Program.cs a nahraďte obsah následujícím kódem.
    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);
                }
            }
        }
    }

Teď můžete aplikaci spustit a zobrazit ji v akci.

  • Klikněte pravým tlačítkem myši na projekt STESample.ConsoleTest v Průzkumník řešení a vyberte Ladit –> Spustit novou instanci.

Po spuštění aplikace se zobrazí následující výstup.

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...

Využívání služby z aplikace WPF

Pojďme vytvořit aplikaci WPF, která používá naši službu.

  • Soubor -> Nový -> Projekt...
  • V levém podokně vyberte Visual C# a pak aplikaci WPF.
  • Jako název zadejte STESample.WPFTest a klikněte na OK.
  • Přidání odkazu na projekt STESample.Entities

Potřebujeme odkaz na službu WCF.

  • Klikněte pravým tlačítkem na projekt STESample.WPFTest v Průzkumník řešení a vyberte Přidat odkaz na službu...
  • Klikněte na Zjistit.
  • Jako obor názvů zadejte BloggingService a klikněte na OK.

Teď můžeme napsat nějaký kód, který bude službu využívat.

  • Otevřete MainWindow.xaml a nahraďte obsah následujícím kódem.
    <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>
  • Otevřete kód pro MainWindow (MainWindow.xaml.cs) a nahraďte obsah následujícím kódem.
    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();
                }
            }
        }
    }

Teď můžete aplikaci spustit a zobrazit ji v akci.

  • Klikněte pravým tlačítkem myši na projekt STESample.WPFTest v Průzkumník řešení a vyberte Ladit –> Spustit novou instanci.
  • Data můžete manipulovat pomocí obrazovky a uložit je přes službu pomocí tlačítka Uložit .

WPF Main window