Procedura dettagliata sulle entità di rilevamento automatico

Importante

Non è più consigliabile usare il modello di entità con rilevamento automatico. Continuerà a essere disponibile solo per supportare le applicazioni esistenti. Se l'applicazione richiede l'uso con grafici di entità disconnesse, prendere in considerazione altre alternative, come ad esempio Trackable Entities, che è una tecnologia simile alle entità con rilevamento automatico ma viene sviluppata in modo più attivo dalla community, oppure la scrittura di codice personalizzato usando le API di rilevamento delle modifiche di basso livello.

Questa procedura dettagliata illustra lo scenario in cui un servizio Windows Communication Foundation (WCF) espone un'operazione che restituisce un entità graph. Successivamente, un'applicazione client modifica il grafico e invia le modifiche a un'operazione del servizio che convalida e salva gli aggiornamenti in un database usando Entity Framework.

Prima di completare questa procedura dettagliata, assicurarsi di leggere la pagina Entità di rilevamento automatico.

In questa procedura dettagliata verranno completate le seguenti azioni:

  • Crea un database a cui accedere.
  • Crea una libreria di classi che contiene il modello.
  • Esegue lo scambio con il modello generatore di entità di rilevamento automatico.
  • Sposta le classi di entità in un progetto separato.
  • Crea un servizio WCF che espone le operazioni per eseguire query e salvare entità.
  • Crea applicazioni client (Console e WPF) che utilizzano il servizio.

In questa procedura dettagliata si userà Database First, ma le stesse tecniche si applicano allo stesso modo a Model First.

Prerequisiti

Per completare questa procedura dettagliata, è necessaria una versione recente di Visual Studio.

Creare un database

Il server di database installato con Visual Studio è diverso a seconda della versione di Visual Studio installata:

  • Se si usa Visual Studio 2012, si creerà un database Local DB.
  • Se si usa Visual Studio 2010, si creerà un database SQL Express.

Procedere e generare il database.

  • Aprire Visual Studio.
  • Visualizzazione -> Esplora server
  • Fare clic con il pulsante destro del mouse su Data Connessione ions -> Aggiungi Connessione ion...
  • Se non si è connessi a un database da Esplora server prima di dover selezionare Microsoft SQL Server come origine dati
  • Connessione a Local DB o SQL Express, a seconda di quale è stato installato
  • Immettere STESample come nome del database
  • Selezionare OK e verrà chiesto se si vuole creare un nuovo database, selezionare
  • Il nuovo database verrà ora visualizzato in Esplora server
  • Se si usa Visual Studio 2012
    • Fare clic con il pulsante destro del mouse sul database in Esplora server e scegliere Nuova query
    • Copiare il codice SQL seguente nella nuova query, quindi fare clic con il pulsante destro del mouse sulla query e selezionare Esegui
  • Se si usa Visual Studio 2010
    • Selezionare Dati -> Editor TRANSACT SQL -> Nuova Connessione query...
    • Immettere .\SQLEXPRESS come nome del server e fare clic su OK
    • Selezionare il database STESample dall'elenco a discesa nella parte superiore dell'editor di query
    • Copiare il codice SQL seguente nella nuova query, quindi fare clic con il pulsante destro del mouse sulla query e selezionare Esegui 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)

Creare il modello

Prima di tutto, è necessario un progetto in cui inserire il modello.

  • File -> Nuovo -> Progetto...
  • Selezionare Visual C# nel riquadro sinistro e quindi libreria di classi
  • Immettere STESample come nome e fare clic su OK

A questo punto si creerà un modello semplice in Entity Framework Designer per accedere al database:

  • Progetto -> Aggiungi nuovo elemento...
  • Selezionare Dati nel riquadro sinistro e quindi ADO.NET Entity Data Model
  • Immettere BloggingModel come nome e fare clic su OK
  • Selezionare Genera dal database e fare clic su Avanti
  • Immettere le informazioni di connessione per il database creato nella sezione precedente
  • Immettere BloggingContext come nome per il stringa di connessione e fare clic su Avanti
  • Selezionare la casella accanto a Tabelle e fare clic su Fine

Passare alla generazione di codice STE

È ora necessario disabilitare la generazione di codice predefinita e passare a Entità di rilevamento automatico.

Se si usa Visual Studio 2012

  • Espandere BloggingModel.edmx in Esplora soluzioni ed eliminare il BloggingModel.tt e BloggingModel.Context.ttQuesta operazione disabiliterà la generazione di codice predefinita
  • Fare clic con il pulsante destro del mouse su un'area vuota nell'area di Progettazione EF e selezionare Aggiungi elemento generazione codice...
  • Selezionare Online nel riquadro sinistro e cercare STE Generator
  • Selezionare il modello STE Generator per C# , immettere STETemplate come nome e fare clic su Aggiungi
  • I file STETemplate.tt e STETemplate.Context.tt vengono aggiunti annidati nel file BloggingModel.edmx

Se si usa Visual Studio 2010

  • Fare clic con il pulsante destro del mouse su un'area vuota nell'area di Progettazione EF e selezionare Aggiungi elemento generazione codice...
  • Selezionare Codice nel riquadro sinistro e quindi ADO.NET Generatore di entità di rilevamento automatico
  • Immettere STETemplate come nome e fare clic su Aggiungi
  • I file STETemplate.tt e STETemplate.Context.tt vengono aggiunti direttamente al progetto

Spostare i tipi di entità in un progetto separato

Per usare le entità di rilevamento automatico, l'applicazione client deve accedere alle classi di entità generate dal modello. Poiché non si vuole esporre l'intero modello all'applicazione client, le classi di entità verranno spostate in un progetto separato.

Il primo passaggio consiste nell'interrompere la generazione di classi di entità nel progetto esistente:

  • Fare clic con il pulsante destro del mouse su STETemplate.tt in Esplora soluzioni e scegliere Proprietà
  • Nella finestra Proprietà deselezionare TextTemplatingFileGenerator dalla proprietà CustomTool
  • Espandere STETemplate.tt in Esplora soluzioni ed eliminare tutti i file annidati sotto di esso

Successivamente, si aggiungerà un nuovo progetto e si genereranno le classi di entità in esso

  • File -> Aggiungi -> Progetto...

  • Selezionare Visual C# nel riquadro sinistro e quindi libreria di classi

  • Immettere STESample.Entities come nome e fare clic su OK

  • Progetto -> Aggiungi elemento esistente...

  • Passare alla cartella del progetto STESample

  • Selezionare questa opzione per visualizzare tutti i file (*.*)

  • Selezionare il file STETemplate.tt

  • Fare clic sulla freccia a discesa accanto al pulsante Aggiungi e selezionare Aggiungi come collegamento

    Add Linked Template

Si assicurerà anche che le classi di entità vengano generate nello stesso spazio dei nomi del contesto. In questo modo si riduce semplicemente il numero di istruzioni using che è necessario aggiungere in tutta l'applicazione.

  • Fare clic con il pulsante destro del mouse sul STETemplate.tt collegato in Esplora soluzioni e scegliere Proprietà
  • Nella finestra Proprietà impostare Spazio dei nomi degli strumenti personalizzati su STESample

Il codice generato dal modello STE richiederà un riferimento a System.Runtime.Serialization per la compilazione. Questa libreria è necessaria per gli attributi WCF DataContract e DataMember usati nei tipi di entità serializzabili.

  • Fare clic con il pulsante destro del mouse sul progetto STESample.Entities in Esplora soluzioni e selezionare Aggiungi riferimento...
    • In Visual Studio 2012 selezionare la casella accanto a System.Runtime.Serialization e fare clic su OK
    • In Visual Studio 2010 selezionare System.Runtime.Serialization e fare clic su OK

Infine, il progetto con il contesto in cui sarà necessario un riferimento ai tipi di entità.

  • Fare clic con il pulsante destro del mouse sul progetto STESample in Esplora soluzioni e scegliere Aggiungi riferimento...
    • In Visual Studio 2012 selezionare Soluzione nel riquadro sinistro, selezionare la casella accanto a STESample.Entities e fare clic su OK
    • In Visual Studio 2010 - selezionare la scheda Progetti , selezionare STESample.Entities e fare clic su OK

Nota

Un'altra opzione per spostare i tipi di entità in un progetto separato consiste nello spostare il file modello anziché collegarlo dal percorso predefinito. In questo caso, sarà necessario aggiornare la variabile inputFile nel modello per fornire il percorso relativo al file edmx (in questo esempio che sarebbe .. \BloggingModel.edmx).

Creare un servizio WCF

A questo punto è possibile aggiungere un servizio WCF per esporre i dati, si inizierà creando il progetto.

  • File -> Aggiungi -> Progetto...
  • Selezionare Visual C# nel riquadro sinistro e quindi applicazione servizio WCF
  • Immettere STESample.Service come nome e fare clic su OK
  • Aggiungere un riferimento all'assembly System.Data.Entity
  • Aggiungere un riferimento ai progetti STESample e STESample.Entities

È necessario copiare il stringa di connessione ef in questo progetto in modo che venga trovato in fase di esecuzione.

  • Aprire il file App.Config per il progetto **STESample **e copiare l'elemento connectionStrings
  • Incollare l'elemento connectionStrings come elemento figlio dell'elemento di configurazione del file Web.Config nel progetto STESample.Service

È ora possibile implementare il servizio effettivo.

  • Aprire IService1.cs e sostituire il contenuto con il codice seguente
    using System.Collections.Generic;
    using System.ServiceModel;

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

            [OperationContract]
            void UpdateBlog(Blog blog);
        }
    }
  • Aprire Service1.svc e sostituire il contenuto con il codice seguente
    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.");
                    }
                }
            }        
        }
    }

Utilizzare il servizio da un'applicazione console

Si creerà ora un'applicazione console che usa il servizio.

  • File -> Nuovo -> Progetto...
  • Selezionare Visual C# nel riquadro sinistro e quindi Applicazione console
  • Immettere STESample.ConsoleTest come nome e fare clic su OK
  • Aggiungere un riferimento al progetto STESample.Entities

È necessario un riferimento al servizio WCF

  • Fare clic con il pulsante destro del mouse sul progetto STESample.ConsoleTest in Esplora soluzioni e scegliere Aggiungi riferimento al servizio...
  • Fare clic su Individua
  • Immettere BloggingService come spazio dei nomi e fare clic su OK

È ora possibile scrivere codice per utilizzare il servizio.

  • Aprire Program.cs e sostituire il contenuto con il codice seguente.
    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);
                }
            }
        }
    }

È ora possibile eseguire l'applicazione per vederla in azione.

  • Fare clic con il pulsante destro del mouse sul progetto STESample.ConsoleTest in Esplora soluzioni e scegliere Debug -> Avvia nuova istanza

All'esecuzione dell'applicazione verrà visualizzato l'output seguente.

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

Utilizzare il servizio da un'applicazione WPF

Si creerà ora un'applicazione WPF che usa il servizio.

  • File -> Nuovo -> Progetto...
  • Selezionare Visual C# nel riquadro sinistro e quindi applicazione WPF
  • Immettere STESample.WPFTest come nome e fare clic su OK
  • Aggiungere un riferimento al progetto STESample.Entities

È necessario un riferimento al servizio WCF

  • Fare clic con il pulsante destro del mouse sul progetto STESample.WPFTest in Esplora soluzioni e scegliere Aggiungi riferimento al servizio...
  • Fare clic su Individua
  • Immettere BloggingService come spazio dei nomi e fare clic su OK

È ora possibile scrivere codice per utilizzare il servizio.

  • Aprire MainWindow.xaml e sostituire il contenuto con il codice seguente.
    <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>
  • Aprire il code-behind per MainWindow (MainWindow.xaml.cs) e sostituire il contenuto con il codice seguente
    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();
                }
            }
        }
    }

È ora possibile eseguire l'applicazione per vederla in azione.

  • Fare clic con il pulsante destro del mouse sul progetto STESample.WPFTest in Esplora soluzioni e scegliere Debug -> Avvia nuova istanza
  • È possibile modificare i dati usando la schermata e salvarli tramite il servizio usando il pulsante Salva

WPF Main window