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 Sì
- 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
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