自跟踪实体演练Self-Tracking Entities Walkthrough

重要

我们不再建议使用自跟踪实体模板。We no longer recommend using the self-tracking-entities template. 它将仅继续用于支持现有应用程序。It will only continue to be available to support existing applications. 如果应用程序需要使用断开连接的实体图,请考虑其他替代方案,例如可跟踪实体,它与自跟踪实体类似,社区在更积极地开发这种技术,或使用低级别更改跟踪 API 编写自定义代码。If your application requires working with disconnected graphs of entities, consider other alternatives such as Trackable Entities, which is a technology similar to Self-Tracking-Entities that is more actively developed by the community, or writing custom code using the low-level change tracking APIs.

本演练演示了 Windows Communication Foundation (WCF) 服务公开返回实体关系图的操作的情况。This walkthrough demonstrates the scenario in which a Windows Communication Foundation (WCF) service exposes an operation that returns an entity graph. 接下来,客户端应用程序操作该图并将修改提交给使用实体框架验证和保存数据库更新的服务操作。Next, a client application manipulates that graph and submits the modifications to a service operation that validates and saves the updates to a database using Entity Framework.

在完成本演练之前,请务必阅读 " 自跟踪实体 " 页。Before completing this walkthrough make sure you read the Self-Tracking Entities page.

此演练完成以下操作:This walkthrough completes the following actions:

  • 创建要访问的数据库。Creates a database to access.
  • 创建包含模型的类库。Creates a class library that contains the model.
  • 交换到 "自跟踪实体生成器" 模板。Swaps to the Self-Tracking Entity Generator template.
  • 将实体类移到单独的项目中。Moves the entity classes to a separate project.
  • 创建一个 WCF 服务,该服务公开用于查询和保存实体的操作。Creates a WCF service that exposes operations to query and save entities.
  • (控制台和 WPF) 创建使用该服务的客户端应用程序。Creates client applications (Console and WPF) that consume the service.

我们将在本演练中使用 Database First,但相同的技术同样适用于 Model First。We'll use Database First in this walkthrough but the same techniques apply equally to Model First.

先决条件Pre-Requisites

若要完成本演练,你将需要最新版本的 Visual Studio。To complete this walkthrough you will need a recent version of Visual Studio.

创建数据库Create a Database

随 Visual Studio 一起安装的数据库服务器因安装的 Visual Studio 版本而异:The database server that is installed with Visual Studio is different depending on the version of Visual Studio you have installed:

  • 如果使用的是 Visual Studio 2012,则将创建一个 LocalDB 数据库。If you are using Visual Studio 2012 then you'll be creating a LocalDB database.
  • 如果使用的是 Visual Studio 2010,则将创建 SQL Express 数据库。If you are using Visual Studio 2010 you'll be creating a SQL Express database.

接下来,生成数据库。Let's go ahead and generate the database.

  • 打开 Visual StudioOpen Visual Studio
  • 视图- > 服务器资源管理器View -> Server Explorer
  • 右键单击 "数据连接- > 添加连接 ... "Right click on Data Connections -> Add Connection…
  • 如果尚未从服务器资源管理器连接到数据库,则需要选择 Microsoft SQL Server 作为数据源If you haven’t connected to a database from Server Explorer before you’ll need to select Microsoft SQL Server as the data source
  • 连接到 LocalDB 或 SQL Express,具体取决于你安装的是哪个Connect to either LocalDB or SQL Express, depending on which one you have installed
  • 输入 STESample 作为数据库名称Enter STESample as the database name
  • 选择 "确定" ,系统会询问您是否要创建新数据库,请选择 "是"Select OK and you will be asked if you want to create a new database, select Yes
  • 新数据库现在将出现在服务器资源管理器The new database will now appear in Server Explorer
  • 如果使用的是 Visual Studio 2012If you are using Visual Studio 2012
    • 右键单击“服务器资源管理器”中的数据库,然后选择“新建查询”****Right-click on the database in Server Explorer and select New Query
    • 将以下 SQL 复制到新的查询中,然后右键单击该查询,然后选择 "执行"Copy the following SQL into the new query, then right-click on the query and select Execute
  • 如果使用的是 Visual Studio 2010If you are using Visual Studio 2010
    • 选择 数据 > Transact-sql SQL 编辑器- > 新建查询连接 ...Select Data -> Transact SQL Editor -> New Query Connection...
    • 输入 。 \SQLEXPRESS 作为服务器名称,然后单击 "确定"Enter .\SQLEXPRESS as the server name and click OK
    • 从 "查询编辑器" 顶部的下拉菜单中选择 " STESample " 数据库Select the STESample database from the drop down at the top of the query editor
    • 将以下 SQL 复制到新的查询中,然后右键单击该查询,然后选择 "执行 SQL "。Copy the following SQL into the new query, then right-click on the query and select Execute 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)

创建模型Create the Model

首先,我们需要一个项目来放置模型。First up, we need a project to put the model in.

  • 文件- > 新建- > 项目 .。。File -> New -> Project...
  • 从左窗格中选择 " **Visual C # ** " ,然后选择 "类库"Select Visual C# from the left pane and then Class Library
  • 输入 STESample 作为名称,然后单击 "确定"Enter STESample as the name and click OK

现在,我们将在 EF 设计器中创建一个简单的模型来访问数据库:Now we'll create a simple model in the EF Designer to access our database:

  • 项目- > 添加新项 .。。Project -> Add New Item...
  • 从左窗格中选择 " 数据 ",然后 ADO.NET 实体数据模型Select Data from the left pane and then ADO.NET Entity Data Model
  • 输入 BloggingModel 作为名称,然后单击 "确定"Enter BloggingModel as the name and click OK
  • 选择 "从数据库生成",然后单击 "下一步"Select Generate from database and click Next
  • 输入在上一部分中创建的数据库的连接信息Enter the connection information for the database that you created in the previous section
  • 输入 "bloggingcontext" 作为连接字符串的名称,然后单击 "下一步"Enter BloggingContext as the name for the connection string and click Next
  • 选中 "" 旁边的框,然后单击 "完成"Check the box next to Tables and click Finish

交换到粘贴代码生成Swap to STE Code Generation

现在,我们需要禁用默认代码生成并交换到自跟踪实体。Now we need to disable the default code generation and swap to Self-Tracking Entities.

如果使用的是 Visual Studio 2012If you are using Visual Studio 2012

  • 展开解决方案资源管理器中的BloggingModel ,删除BloggingModel.ttBloggingModel.Context.tt 这将禁用默认代码生成Expand BloggingModel.edmx in Solution Explorer and delete the BloggingModel.tt and BloggingModel.Context.tt This will disable the default code generation
  • 右键单击 EF 设计器图面上的空白区域,然后选择 "添加代码生成项 ... "Right-click an empty area on the EF Designer surface and select Add Code Generation Item...
  • 从左窗格中选择 " 联机 ",然后搜索 粘贴生成器Select Online from the left pane and search for STE Generator
  • 选择用于 C # 的粘贴生成器模板,输入STETemplate作为名称,然后单击 "添加"Select the STE Generator for C# template, enter STETemplate as the name and click Add
  • STETemplate.ttSTETemplate.Context.tt文件添加到了 BloggingModel 文件下The STETemplate.tt and STETemplate.Context.tt files are added nested under the BloggingModel.edmx file

如果使用的是 Visual Studio 2010If you are using Visual Studio 2010

  • 右键单击 EF 设计器图面上的空白区域,然后选择 "添加代码生成项 ... "Right-click an empty area on the EF Designer surface and select Add Code Generation Item...
  • 从左窗格中选择 " 代码 ",然后 ADO.NET 自跟踪实体生成器Select Code from the left pane and then ADO.NET Self-Tracking Entity Generator
  • 输入STETemplate作为名称,然后单击 "添加"Enter STETemplate as the name and click Add
  • STETemplate.ttSTETemplate.Context.tt文件会直接添加到你的项目The STETemplate.tt and STETemplate.Context.tt files are added directly to your project

将实体类型移到单独的项目中Move Entity Types into Separate Project

若要使用自跟踪实体,客户端应用程序需要访问模型中生成的实体类。To use Self-Tracking Entities our client application needs access to the entity classes generated from our model. 由于我们不想将整个模型公开给客户端应用程序,因此我们要将实体类移到单独的项目中。Because we don't want to expose the whole model to the client application we're going to move the entity classes into a separate project.

第一步是停止生成现有项目中的实体类:The first step is to stop generating entity classes in the existing project:

  • 解决方案资源管理器中右键单击 " STETemplate.tt ",然后选择 "属性"Right-click on STETemplate.tt in Solution Explorer and select Properties
  • 在 "属性" 窗口中,清除 " CustomTool " 属性中的TextTemplatingFileGeneratorIn the Properties window clear TextTemplatingFileGenerator from the CustomTool property
  • 解决方案资源管理器中展开 " STETemplate.tt ",并删除其下嵌套的所有文件Expand STETemplate.tt in Solution Explorer and delete all files nested under it

接下来,我们将添加一个新项目,并在其中生成实体类Next, we are going to add a new project and generate the entity classes in it

  • 文件- > 添加 > 项目 .。。File -> Add -> Project...

  • 从左窗格中选择 " **Visual C # ** " ,然后选择 "类库"Select Visual C# from the left pane and then Class Library

  • 输入 STESample 作为名称,然后单击 "确定"Enter STESample.Entities as the name and click OK

  • 项目- > 添加现有项 .。。Project -> Add Existing Item...

  • 导航到 STESample 项目文件夹Navigate to the STESample project folder

  • 选择 (查看 **所有文件 * 。 *) **Select to view All Files (*.*)

  • 选择 STETemplate.tt 文件Select the STETemplate.tt file

  • 单击 "添加" 按钮旁边的下拉箭头,然后选择 "添加为链接"。Click on the drop down arrow next to the Add button and select Add As Link

    添加链接模板

我们还将确保在上下文相同的命名空间中生成实体类。We're also going to make sure the entity classes get generated in the same namespace as the context. 这只是减少了我们需要在应用程序中添加的 using 语句的数量。This just reduces the number of using statements we need to add throughout our application.

  • 解决方案资源管理器中右键单击链接的STETemplate.tt ,然后选择 "属性"Right-click on the linked STETemplate.tt in Solution Explorer and select Properties
  • 在 " 属性 " 窗口中,将 自定义工具命名空间 设置为 STESampleIn the Properties window set Custom Tool Namespace to STESample

粘贴模板生成的代码将需要引用 system.exception 才能进行编译The code generated by the STE template will need a reference to System.Runtime.Serialization in order to compile. 在可序列化实体类型上使用的 WCF DataContractDataMember 特性需要此库。This library is needed for the WCF DataContract and DataMember attributes that are used on the serializable entity types.

  • 右键单击解决方案资源管理器中的STESample项目,然后选择 "添加引用 ... "Right click on the STESample.Entities project in Solution Explorer and select Add Reference...
    • 在 Visual Studio 2012 中,选中 " system.web " 旁边的框,然后单击 "确定"In Visual Studio 2012 - check the box next to System.Runtime.Serialization and click OK
    • 在 Visual Studio 2010 中,选择 " System.web " 并单击 "确定"In Visual Studio 2010 - select System.Runtime.Serialization and click OK

最后,具有中的上下文的项目将需要对实体类型的引用。Finally, the project with our context in it will need a reference to the entity types.

  • 解决方案资源管理器中右键单击 " STESample " 项目,然后选择 "添加引用 ... "Right click on the STESample project in Solution Explorer and select Add Reference...
    • 在 Visual Studio 2012 中,从左窗格中选择 " 解决方案 ",选中 " STESample " 旁边的框,然后单击 "确定"In Visual Studio 2012 - select Solution from the left pane, check the box next to STESample.Entities and click OK
    • 在 Visual Studio 2010 中,选择 " 项目 " 选项卡,选择 " STESample ",然后单击 "确定"In Visual Studio 2010 - select the Projects tab, select STESample.Entities and click OK

备注

将实体类型移动到单独的项目的另一种方法是移动模板文件,而不是将其链接到其默认位置。Another option for moving the entity types to a separate project is to move the template file, rather than linking it from its default location. 如果执行此操作,则需要更新模板中的 inputFile 变量,以提供此示例中的 edmx 文件 (相对路径,该路径为 。 \BloggingModel) 。If you do this, you will need to update the inputFile variable in the template to provide the relative path to the edmx file (in this example that would be ..\BloggingModel.edmx).

创建 WCF 服务Create a WCF Service

现在是时候添加 WCF 服务来公开数据,接下来我们将创建该项目。Now it's time to add a WCF Service to expose our data, we'll start by creating the project.

  • 文件- > 添加 > 项目 .。。File -> Add -> Project...
  • 从左窗格中选择 " **Visual C # ** ",然后选择 " WCF 服务应用程序"Select Visual C# from the left pane and then WCF Service Application
  • 输入 STESample 作为名称,然后单击 "确定"Enter STESample.Service as the name and click OK
  • 添加对 system.web 程序集的引用Add a reference to the System.Data.Entity assembly
  • 添加对 STESampleSTESample 项目的引用Add a reference to the STESample and STESample.Entities projects

需要将 EF 连接字符串复制到此项目中,以便在运行时找到它。We need to copy the EF connection string to this project so that it is found at runtime.

  • 打开 STESample 项目的App.Config文件,并复制connectionStrings元素Open the App.Config file for the **STESample **project and copy the connectionStrings element
  • connectionStrings元素粘贴为STESample项目中Web.Config文件的configuration元素的子元素Paste the connectionStrings element as a child element of the configuration element of the Web.Config file in the STESample.Service project

现在是时候实现实际服务了。Now it's time to implement the actual service.

  • 打开 IService1.cs 并将内容替换为以下代码Open IService1.cs and replace the contents with the following code
    using System.Collections.Generic;
    using System.ServiceModel;

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

            [OperationContract]
            void UpdateBlog(Blog blog);
        }
    }
  • 打开 Service1 并将内容替换为以下代码Open Service1.svc and replace the contents with the following code
    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.");
                    }
                }
            }        
        }
    }

从控制台应用程序使用服务Consume the Service from a Console Application

让我们创建一个使用我们的服务的控制台应用程序。Let's create a console application that uses our service.

  • 文件- > 新建- > 项目 .。。File -> New -> Project...
  • 从左窗格中选择 " **Visual C # ** ",然后选择 "**控制台应用程序**"Select Visual C# from the left pane and then Console Application
  • 输入 STESample 作为名称,然后单击 "确定"Enter STESample.ConsoleTest as the name and click OK
  • 添加对 STESample 项目的引用Add a reference to the STESample.Entities project

我们需要对 WCF 服务的服务引用We need a service reference to our WCF service

  • 解决方案资源管理器中右键单击 " ConsoleTest " 项目,然后选择 "添加服务引用 ...Right-click the STESample.ConsoleTest project in Solution Explorer and select Add Service Reference...
  • 单击 发现Click Discover
  • 输入 BloggingService 作为命名空间,然后单击 "确定"Enter BloggingService as the namespace and click OK

现在,我们可以编写一些代码来使用该服务。Now we can write some code to consume the service.

  • 打开 Program.cs 并将内容替换为以下代码。Open Program.cs and replace the contents with the following code.
    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);
                }
            }
        }
    }

现在可以运行应用程序来查看其实际运行情况。You can now run the application to see it in action.

  • 解决方案资源管理器中右键单击 " ConsoleTest " 项目,然后选择 "调试" -" > 启动新实例"Right-click the STESample.ConsoleTest project in Solution Explorer and select Debug -> Start new instance

当应用程序执行时,将看到以下输出。You'll see the following output when the application executes.

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

从 WPF 应用程序使用服务Consume the Service from a WPF Application

让我们创建一个使用我们的服务的 WPF 应用程序。Let's create a WPF application that uses our service.

  • 文件- > 新建- > 项目 .。。File -> New -> Project...
  • 从左窗格中选择 " **Visual C # ** ",然后选择 " WPF 应用程序"Select Visual C# from the left pane and then WPF Application
  • 输入 STESample 作为名称,然后单击 "确定"Enter STESample.WPFTest as the name and click OK
  • 添加对 STESample 项目的引用Add a reference to the STESample.Entities project

我们需要对 WCF 服务的服务引用We need a service reference to our WCF service

  • 解决方案资源管理器中右键单击 " WPFTest " 项目,然后选择 "添加服务引用 ...Right-click the STESample.WPFTest project in Solution Explorer and select Add Service Reference...
  • 单击 发现Click Discover
  • 输入 BloggingService 作为命名空间,然后单击 "确定"Enter BloggingService as the namespace and click OK

现在,我们可以编写一些代码来使用该服务。Now we can write some code to consume the service.

  • 打开 mainwindow.xaml 并将内容替换为以下代码。Open MainWindow.xaml and replace the contents with the following code.
    <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>
  • 打开 Mainwindow.xaml () MainWindow.xaml.cs 的隐藏代码,并将内容替换为以下代码Open the code behind for MainWindow (MainWindow.xaml.cs) and replace the contents with the following code
    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();
                }
            }
        }
    }

现在可以运行应用程序来查看其实际运行情况。You can now run the application to see it in action.

  • 解决方案资源管理器中右键单击 " WPFTest " 项目,然后选择 "调试" -" > 启动新实例"Right-click the STESample.WPFTest project in Solution Explorer and select Debug -> Start new instance
  • 你可以使用屏幕操作数据,并使用 " 保存 " 按钮通过服务保存数据You can manipulate the data using the screen and save it via the service using the Save button

WPF 主窗口