Пошаговое руководство по самостоятельному отслеживанию сущностей

Важно!

Больше не рекомендуется использовать шаблон сущностей с самостоятельным отслеживанием. Он по-прежнему будет доступен только для поддержки существующих приложений. Если приложению необходимо работать с отключенными графами сущностей, рассмотрите другие варианты, такие как отслеживаемые сущности, которые технологически эквивалентны сущностям с самостоятельным отслеживанием, активно разрабатываемым сообществом. Или остановитесь на написании пользовательского кода с помощью API-интерфейсов отслеживания изменений низкого уровня.

В этом пошаговом руководстве демонстрируется сценарий, в котором служба Windows Communication Foundation (WCF) предоставляет операцию, которая возвращает граф сущностей. Затем клиентское приложение управляет этим графом и отправляет изменения в операцию службы, которая проверяет и сохраняет обновления базы данных с помощью Entity Framework.

Перед выполнением этого пошагового руководства убедитесь, что вы прочитали страницу сущностей самостоятельного отслеживания.

В этом пошаговом руководстве выполняются следующие действия.

  • Создает базу данных для доступа.
  • Создает библиотеку классов, содержащую модель.
  • Переключение на шаблон генератора сущностей самостоятельного отслеживания.
  • Перемещает классы сущностей в отдельный проект.
  • Создает службу WCF, которая предоставляет операции для запроса и сохранения сущностей.
  • Создает клиентские приложения (консоль и WPF), которые используют службу.

Мы будем использовать базу данных First в этом пошаговом руководстве, но те же методы применяются одинаково к модели First.

Предварительные требования

Для выполнения этого пошагового руководства потребуется последняя версия Visual Studio.

Создание базы данных

Сервер базы данных, установленный с Visual Studio, отличается в зависимости от установленной версии Visual Studio:

  • Если вы используете Visual Studio 2012, вы создадите базу данных LocalDB.
  • Если вы используете Visual Studio 2010, вы создадите базу данных SQL Express.

Давайте пойдем вперед и создадим базу данных.

  • Запустите Visual Studio
  • Представление —> Обозреватель сервера
  • Щелкните правой кнопкой мыши данные Подключение ions—> добавьте Подключение ion...
  • Если вы еще не подключились к базе данных из сервера Обозреватель, прежде чем выбрать Microsoft SQL Server в качестве источника данных
  • Подключение в LocalDB или SQL Express в зависимости от установленного экземпляра
  • Введите STESample в качестве имени базы данных
  • Нажмите кнопку "ОК ", и вам будет предложено создать новую базу данных, нажмите кнопку "Да"
  • Теперь новая база данных появится в сервере Обозреватель
  • Если вы используете Visual Studio 2012
    • Щелкните правой кнопкой мыши базу данных на сервере Обозреватель и выберите "Создать запрос"
    • Скопируйте следующий SQL в новый запрос, а затем щелкните правой кнопкой мыши запрос и выберите "Выполнить".
  • Если вы используете Visual Studio 2010
    • Выбор данных —> редактор Transact SQL —> создание Подключение запроса...
    • Введите .\SQLEXPRESS в качестве имени сервера и нажмите кнопку "ОК"
    • Выберите базу данных STESample в раскрывающемся списке в верхней части редактора запросов.
    • Скопируйте следующий SQL в новый запрос, а затем щелкните правой кнопкой мыши запрос и выберите "Выполнить 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)

Создание модели

Во-первых, нам нужен проект, в который будет помещена модель.

  • Файл —> создать проект> ...
  • Выберите Visual C# в левой области, а затем библиотеку классов
  • Введите STESample в качестве имени и нажмите кнопку "ОК"

Теперь мы создадим простую модель в ef Designer для доступа к нашей базе данных:

  • Проект —> добавление нового элемента...
  • Выберите данные из левой области, а затем ADO.NET модель данных сущности
  • Введите bloggingModel в качестве имени и нажмите кнопку "ОК"
  • Выберите " Создать из базы данных " и нажмите кнопку "Далее"
  • Введите сведения о подключении для базы данных, созданной в предыдущем разделе
  • Введите BloggingContext в качестве имени строка подключения и нажмите кнопку "Далее"
  • Установите флажок рядом с таблицами и нажмите кнопку "Готово"

Переключение на создание кода STE

Теперь необходимо отключить создание кода по умолчанию и переключиться на сущности самостоятельного отслеживания.

Если вы используете Visual Studio 2012

  • Разверните bloggingModel.edmx в Обозреватель решений и удалите BloggingModel.tt и BloggingModel.Context.ttэто приведет к отключению создания кода по умолчанию
  • Щелкните правой кнопкой мыши пустую область в области EF Designer и выберите " Добавить элемент создания кода".
  • Выберите "Online" в левой области и найдите генератор STE
  • Выберите шаблон STE Generator for C#, введите STETemplate в качестве имени и нажмите кнопку "Добавить".
  • Файлы STETemplate.tt и STETemplate.Context.tt добавляются в файл BloggingModel.edmx

Если вы используете Visual Studio 2010

  • Щелкните правой кнопкой мыши пустую область в области EF Designer и выберите " Добавить элемент создания кода".
  • Выберите код в левой области, а затем ADO.NET генератор сущностей самостоятельного отслеживания
  • Введите STETemplate в качестве имени и нажмите кнопку "Добавить"
  • Файлы STETemplate.tt и STETemplate.Context.tt добавляются непосредственно в проект

Перемещение типов сущностей в отдельный проект

Чтобы использовать сущности самостоятельного отслеживания, нашим клиентским приложением необходим доступ к классам сущностей, созданным из нашей модели. Так как мы не хотим предоставлять всю модель клиентскому приложению, мы переместим классы сущностей в отдельный проект.

Первым шагом является остановка создания классов сущностей в существующем проекте:

  • Щелкните правой кнопкой мыши STETemplate.tt в Обозреватель решений и выберите пункт "Свойства"
  • В окне свойств снимите флажок TextTemplatingFileGenerator из свойства CustomTool
  • Разверните STETemplate.tt в Обозреватель решений и удалите все файлы, вложенные в него

Далее мы добавим новый проект и создадим классы сущностей в нем

  • Файл —> добавление —> проект...

  • Выберите Visual C# в левой области, а затем библиотеку классов

  • Введите имя STESample.Entities и нажмите кнопку ОК

  • Проект —> добавление существующего элемента...

  • Перейдите в папку проекта STESample

  • Выберите, чтобы просмотреть все файлы (*.*)

  • Выберите файл STETemplate.tt

  • Щелкните стрелку раскрывающегося списка рядом с кнопкой "Добавить " и нажмите кнопку "Добавить как ссылку"

    Add Linked Template

Мы также собираемся убедиться, что классы сущностей создаются в том же пространстве имен, что и контекст. Это просто сокращает количество инструкций использования, которые необходимо добавить во всем приложении.

  • Щелкните правой кнопкой мыши связанный STETemplate.tt в Обозреватель решений и выберите "Свойства"
  • В окне "Свойства" для настраиваемого пространства имен средства задано значениеSTESample

Для компиляции кода, созданного шаблоном STE, потребуется ссылка на System.Runtime.Serialization . Эта библиотека необходима для атрибутов WCF DataContract и DataMember , используемых для сериализуемых типов сущностей.

  • Щелкните правой кнопкой мыши проект STESample.Entities в Обозреватель решений и выберите "Добавить ссылку...
    • В Visual Studio 2012 — проверка поле рядом с System.Runtime.Serialization и нажмите кнопку ОК
    • В Visual Studio 2010 выберите System.Runtime.Serialization и нажмите кнопку ОК

Наконец, проект с нашим контекстом в нем потребуется ссылка на типы сущностей.

  • Щелкните правой кнопкой мыши проект STESample в Обозреватель решений и выберите "Добавить ссылку...
    • В Visual Studio 2012 выберите решение в левой области, проверка поле рядом с STESample.Entities и нажмите кнопку "ОК"
    • В Visual Studio 2010 выберите вкладку "Проекты ", выберите STESample.Entities и нажмите кнопку "ОК"

Примечание.

Другим вариантом перемещения типов сущностей в отдельный проект является перемещение файла шаблона, а не связывание его из расположения по умолчанию. При этом необходимо обновить переменную inputFile в шаблоне, чтобы указать относительный путь к edmx-файлу (в этом примере это будет .). \BloggingModel.edmx).

Создание службы WCF

Теперь пришло время добавить службу WCF для предоставления наших данных, мы начнем с создания проекта.

  • Файл —> добавление —> проект...
  • Выберите Visual C# в левой области, а затем приложение службы WCF
  • Введите имя STESample.Service и нажмите кнопку ОК
  • Добавление ссылки на сборку System.Data.Entity
  • Добавление ссылки на проекты STESample и STESample.Entities

Необходимо скопировать ef строка подключения в этот проект, чтобы он был найден во время выполнения.

  • Откройте файл App.Config для проекта **STESample *** и скопируйте элемент connectionStrings
  • Вставьте элемент connectionStrings в качестве дочернегоэлемента конфигурации файла Web.Config в проекте STESample.Service

Теперь пришло время реализовать фактическую службу.

  • Откройте файл IService1.cs и замените содержимое следующим кодом.
    using System.Collections.Generic;
    using System.ServiceModel;

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

            [OperationContract]
            void UpdateBlog(Blog blog);
        }
    }
  • Откройте Service1.svc и замените содержимое следующим кодом.
    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.");
                    }
                }
            }        
        }
    }

Использование службы из консольного приложения

Создадим консольное приложение, использующее нашу службу.

  • Файл —> создать проект> ...
  • Выберите Visual C# в левой области, а затем консольное приложение
  • Введите STESample.ConsoleTest в качестве имени и нажмите кнопку "ОК"
  • Добавление ссылки на проект STESample.Entities

Нам нужна ссылка на службу WCF

  • Щелкните правой кнопкой мыши проект STESample.ConsoleTest в Обозреватель решений и выберите команду Добавить ссылку на службу...
  • Нажмите кнопку " Обнаружить"
  • Введите BloggingService в качестве пространства имен и нажмите кнопку "ОК"

Теперь можно написать код для использования службы.

  • Откройте Program.cs и замените содержимое следующим кодом.
    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);
                }
            }
        }
    }

Теперь вы можете запустить приложение и увидеть, как оно работает:

  • Щелкните правой кнопкой мыши проект STESample.ConsoleTest в Обозреватель решений и выберите "Отладка — Запуск нового экземпляра">

При выполнении приложения вы увидите следующие выходные данные.

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

Создадим приложение WPF, использующее нашу службу.

  • Файл —> создать проект> ...
  • Выберите Visual C# в левой области, а затем приложение WPF
  • Введите STESample.WPFTest в качестве имени и нажмите кнопку "ОК"
  • Добавление ссылки на проект STESample.Entities

Нам нужна ссылка на службу WCF

  • Щелкните правой кнопкой мыши проект STESample.WPFTest в Обозреватель решений и выберите команду Добавить ссылку на службу...
  • Нажмите кнопку " Обнаружить"
  • Введите BloggingService в качестве пространства имен и нажмите кнопку "ОК"

Теперь можно написать код для использования службы.

  • Откройте MainWindow.xaml и замените содержимое следующим кодом.
    <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 (MainWindow.xaml.cs) и замените содержимое следующим кодом.
    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();
                }
            }
        }
    }

Теперь вы можете запустить приложение и увидеть, как оно работает:

  • Щелкните правой кнопкой мыши проект STESample.WPFTest в Обозреватель решений и выберите "Отладка — запуск нового экземпляра">
  • Вы можете управлять данными с помощью экрана и сохранять их с помощью службы с помощью кнопки "Сохранить "

WPF Main window