Esta documentación está archivada y no tiene mantenimiento.

Tutorial: Serializar entidades de seguimiento propio (Entity Framework)

Visual Studio 2010

El tutorial de este tema muestra el escenario en que un servicio de Windows Communication Foundation (WCF) expone una serie de operaciones que devuelven gráficos de entidades. A continuación, una aplicación cliente manipula dicho gráfico y envía las modificaciones a una operación de servicio que valida y guarda las actualizaciones en una base de datos utilizando Entity Framework. Para obtener más información, vea Trabajar con entidades de seguimiento propio.

Por lo general, deseará separar el proyecto modelo del proyecto que contiene los tipos de entidad de seguimiento propio. Posteriormente, el cliente solo necesitará incluir el proyecto de tipos de entidad.

Una manera de lograr este aislamiento consiste en separar los tipos de entidad de seguimiento propio del modelo y mover la plantilla que genera los tipos de entidad a una biblioteca de clases independiente. En la plantilla de entidades de seguimiento propio es necesario incluir la ubicación del archivo .edmx para obtener acceso a los metadatos. Si mueve las plantillas del proyecto original a otros proyectos, deberá abrir los archivos de plantilla en un editor y modificar la cadena inputFile según la ubicación relativa del archivo .edmx. Para obtener más información sobre cómo trabajar con plantillas, vea ADO.NET Self-Tracking Entity Template.

Otra manera de separar los tipos de entidad del modelo es dejar los archivos de plantilla en el proyecto original, pero deshabilitar la generación de código para las plantillas. Vincule las plantillas desde un proyecto diferente, de modo que el código se genere en ese proyecto diferente en lugar del original. Al agregar un elemento a un proyecto como un vínculo, el contenido real del elemento se mantiene en la ubicación indicada por el proyecto original. Este método de separar los tipos de entidad del modelo se muestra en este tutorial.

Este tutorial realiza las siguientes acciones:

  • Crea el proyecto de biblioteca de clases que contiene el modelo basado en School.

  • Utiliza la plantilla Generador de entidades de seguimiento propio ADO.NET para generar los tipos de entidad, el objeto ObjectContext con tipo, y una clase de extensión que contiene métodos ApplyChanges sobrecargados.

  • Crea el proyecto de biblioteca de clases vinculado a la plantilla de tipos de entidad de seguimiento propio creada en el primer proyecto.

  • Crea el servicio WCF que expone un conjunto de operaciones que devuelven los gráficos de entidades y aplica los cambios realizados en el cliente a la base de datos utilizando Entity Framework .

  • Crea las aplicaciones cliente (consola y Windows Presentation Foundation [WPF]) que manipulan el gráfico y envían las modificaciones utilizando las operaciones expuestas en el servicio WCF.

Ee789839.note(es-es,VS.100).gifNota:
Puede descargar STESchoolModelExample en el sitio de ejemplos de documentación de Entity Framework en la galería de código de MSDN.

  1. Cree un nuevo proyecto de biblioteca de clases. Escriba STESchoolModel como nombre del proyecto y de la solución.

  2. Quite el archivo de código fuente predeterminado que se agregó al proyecto.

  3. Utilice el Asistente para Entity Data Model para generar un modelo basado en las tablas Department, Course, OnlineCourse y OnsiteCoursede la base de datos School. Para obtener más información, vea Modelo School.

  4. Abra el archivo .edmx en ADO.NET Entity Data Model Designer (Entity Designer).

  5. Siga las instrucciones de asignación de herencia del tema Walkthrough: Mapping Inheritance - Table-per-Type.

  6. Haga clic con el botón secundario del mouse en una zona vacía de la superficie de Entity Designer, elija Agregar elemento de generación de código y, a continuación, seleccione Generador de entidades de seguimiento propio ADO.NET. Cambie el nombre de la plantilla predeterminada a SchoolModel.

    Ee789839.note(es-es,VS.100).gifNota:
    Cuando los archivos de plantilla se agregan al proyecto, puede aparecer una advertencia de seguridad pidiéndole que acepte solo si confía en el origen de la plantilla. Haga clic en Aceptar.

  7. Las carpetas SchoolModel.Context.tt y SchoolModel.tt se agregan al proyecto. Bajo la carpeta SchoolModel.Context.tt, hay dos archivos que definen el objeto ObjectContext con tipo y la clase de extensión que contiene métodos ApplyChanges sobrecargados. Bajo la carpeta SchoolModel.tt, hay archivos que definen los tipos de entidad y también una clase de aplicación auxiliar que contiene la lógica del seguimiento de cambios utilizada por las entidades de seguimiento propio.

    Los dos pasos siguientes muestran cómo deshabilitar la generación de código en este proyecto. Más tarde se habilitará para los tipos de la biblioteca de clases de STESchoolModelTypes y para el contexto de objetos de STESchoolModelService.

  8. Seleccione SchoolModel.tt. En la ventana Propiedades, desactive TextTemplatingFileGenerator de la propiedad CustomTool. Elimine los archivos bajo la carpeta SchoolModel.tt.

  9. Seleccione SchoolModel.Context.tt. En la ventana Propiedades, borre el valor de la propiedad CustomTool. Elimine los archivos bajo la carpeta SchoolModel.Context.tt.

    Si está trabajando con el proyecto de Visual Basic, es posible que deba hacer clic en Mostrar todos los archivos en el Explorador de soluciones para ver todos los archivos del proyecto.

  10. Compile el proyecto.

  1. Cree un nuevo proyecto de biblioteca de clases denominado STESchoolModelTypes en la misma solución que el proyecto anterior.

  2. Quite el archivo de código fuente predeterminado que se agregó al proyecto.

  3. Agregue un vínculo al archivo SchoolModel.tt para que los tipos de entidad de seguimiento propio se generen en esta solución. En el Explorador de soluciones, haga clic con el botón secundario en STESchoolModelTypes, haga clic en Agregar y, a continuación, haga clic en Elemento existente.

  4. En el cuadro de diálogo Agregar elemento existente, vaya al proyecto STESchoolModel y haga clic en SchoolModel.tt (no presione ENTRAR). En la lista Agregar, seleccione Agregar como vínculo.

  5. Agregue una referencia a la biblioteca System.Runtime.Serialization. Esta biblioteca es necesaria para los atributos DataContract y DataMember de WCF que se utilizan en los tipos de entidad serializables.

  6. Compile el proyecto.

  1. Cree un proyecto de aplicación de servicios WCF denominado STESchoolModelService en la misma solución que el proyecto anterior.

  2. Agregue una referencia a System.Data.Entity.dll.

  3. Agregue una referencia a los proyectos STESchoolModel y STESchoolModelTypes.

  4. Agregue un vínculo al archivo SchoolModel.Context.tt para que los tipos de contexto se generen en esta solución. En el Explorador de soluciones, haga clic con el botón secundario en STESchoolModelService, haga clic en Agregar y, a continuación, haga clic en Elemento existente.

  5. En el cuadro de diálogo Agregar elemento existente, vaya al proyecto STESchoolModel y haga clic en SchoolModel.Context.tt (no presione ENTRAR). En la lista Agregar, seleccione Agregar como vínculo.

  6. En la ventana Propiedades para el archivo SchoolModel.Context.tt, escriba STESchoolModelTypes en la propiedad Espacio de nombres de la herramienta personalizada. Esto agrega el tipo de contexto de objetos al mismo espacio de nombres que el espacio de nombres para los tipos de entidad de seguimiento propio, lo cual es necesario.

  7. (Solo Visual Basic) Agregue Import STESchoolModelTypes a los archivos de código fuente generados a partir del archivo SchoolModel.Context.tt. Abra el archivo SchoolModel.Context.tt y localice la cadena Imports System. Agregue Import STESchoolModelTypes después de otras importaciones. Los archivos de código fuente generados incluirán este espacio de nombres.

  8. Agregue la cadena de conexión al archivo Web.config para que el motor en tiempo de ejecución de Entity Framework pueda encontrar los metadatos. Abra el archivo app.config para el proyecto STESchoolModel. Copie el elemento connectionStrings y, a continuación, agréguelo como elemento secundario del elemento configuration del archivo Web.config.

  9. Abra el archivo de interfaz del servicio. De forma predeterminada, se denomina IService1.

  10. Agregue el espacio de nombres donde se definen las entidades de seguimiento propio: STESchoolModelTypes.

  11. Reemplace la definición de interfaz de servicio por el siguiente código:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        void UpdateDepartment(Department updated);
        [OperationContract]
        List<Department> GetDepartments();
    }
    
    
  12. Abra el código fuente del servicio. De forma predeterminada, se denomina Service1.srv.cs o Service1.srv.vb.

  13. Agregue el espacio de nombres donde se definen las entidades de seguimiento propio: STESchoolModelTypes.

  14. (Solo Visual Basic) Agregue Imports STESchoolModelService.STESchoolModelTypes al archivo Service1.srv.cs.

  15. Reemplace la definición de clase de servicio por el siguiente código:

Ee789839.Important(es-es,VS.100).gif Nota:
Debe realizar la validación en el objeto actualizado antes de aplicar los cambios.

public class Service1 : IService1
{
    /// <summary>
    /// Updates department and its related courses. 
    /// </summary>
    public void UpdateDepartment(Department updated)
    {
        using (SchoolEntities context =
            new SchoolEntities())
        {
            try
            {
                // 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.Departments.ApplyChanges(updated);
                context.SaveChanges();

            }
            catch (UpdateException ex)
            {
                // 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 the department. Try your request again.");
            }
        }
    }

    /// <summary>
    /// Gets all the departments and related courses. 
    /// </summary>
    public List<Department> GetDepartments()
    {
        using (SchoolEntities context = new SchoolEntities())
        {
            // Use System.Data.Objects.ObjectQuery(T).Include to eagrly load the related courses.
            return context.Departments.Include("Courses").OrderBy(d => d.Name).ToList();
        }
    }

}

  1. Cree una aplicación de consola. Escriba STESchoolModelTest para el nombre del proyecto, en la misma solución que el proyecto anterior.

  2. Agregue una referencia al servicio STEASchoolModelService. Para agregar una referencia al servicio, en el Explorador de soluciones, haga clic con el botón secundario del mouse en la carpeta de referencias y, a continuación, seleccione Agregar referencia de servicio.

    De forma predeterminada, WCF genera un proxy que devuelve la colección IEnumerable. Dado que el método GetDepartments de STESchoolModelService devuelve List, debe configurar el servicio para especificar el tipo devuelto adecuado.

  3. Haga clic con el botón secundario del mouse en el nombre del servicio (ServiceReference1) y, a continuación, seleccione Configurar referencia de servicio. En el cuadro de diálogo Configurar referencia de servicio, seleccione el tipo System.Collections.Generic.List en la lista Tipo de colección.

  4. Agregue una referencia al proyecto STESchoolModelTypes .

  5. Abra el archivo app.config y, a continuación, agregue la cadena de conexión al archivo. Abra el archivo app.config de los proyectos STESchoolModel y, a continuación, copie el elemento connectionStrings para agregarlo como un elemento secundario del elemento configuration del archivo Web.config.

  6. Abra el archivo que contiene la función principal. Incluya los siguientes espacios de nombres: STESchoolModelTest.ServiceReference1 y STESchoolModelTypes (donde se definen los tipos de seguimiento propio).

  7. Pegue el siguiente código en la función principal. El código contiene las llamadas de función a los métodos que se definen en el paso siguiente.

    
    // Note, the service's GetDepartments method returns System.Collections.Generic.List.
    // By default, when WCF generates a proxy the return collection types are converted to IEnumerable.
    // The WCF service has to be configured to specify the List return type. 
    // To specify the List collection type, open the Configure Service Reference dialog and 
    // select the System.Collections.Generic.List type from the Collection type list. 
    
    Console.WriteLine("See the existing departments and courses.");
    DisplayDepartmentsAndCourses();
    Console.WriteLine();
    Console.WriteLine();
    
    // Use some IDs to create
    // new Department and Course. 
    // The newly created objects will
    // be then deleted.
    
    int departmentID = 100;
    
    int courseID = 50;
    
    AddNewDepartmentAndCourses(departmentID, courseID);
    Console.WriteLine("See existing and added.");
    DisplayDepartmentsAndCourses();
    Console.WriteLine();
    UpdateDepartmentAndCourses(departmentID, courseID);
    Console.WriteLine("See existing and updated.");
    DisplayDepartmentsAndCourses();
    Console.WriteLine();
    DeleteDepartmentAndCourses(departmentID);
    
    
  8. Agregue los métodos siguientes a la clase. Los métodos muestran cómo realizar las siguientes acciones: mostrar los objetos devueltos por el servicio, agregar nuevos objetos, actualizar objetos y eliminar objetos. Para obtener más información, vea los comentarios del código.

    
    static void DisplayDepartmentsAndCourses()
    {
        using (var service = new Service1Client())
        {
            // Get all the departments.
            List<Department> departments = service.GetDepartments();
            foreach (var d in departments)
            {
                Console.WriteLine("ID: {0}, Name: {1}", d.DepartmentID, d.Name);
                // Get all the courses for each department. 
                // The reason we are able to access
                // the related courses is because the service eagrly loaded the related objects 
                // (using the System.Data.Objects.ObjectQuery(T).Include method).
                foreach (var c in d.Courses.OfType<OnlineCourse>())
                {
                    Console.WriteLine("  OnLineCourse ID: {0}, Title: {1}", c.CourseID, c.Title);
                }
                foreach (var c in d.Courses.OfType<OnsiteCourse>())
                {
                    Console.WriteLine("  OnSiteCourse ID: {0}, Title: {1}", c.CourseID, c.Title);
                }
            }
        }
    }
    
    
    static void AddNewDepartmentAndCourses(int departmentID, int courseID)
    {
        using (var service = new Service1Client())
        {
            Department newDepartment = new Department()
            {
                DepartmentID = departmentID,
                Budget = 13000.000m,
                Name = "New Department",
                StartDate = DateTime.Now
            };
    
            OnlineCourse newCourse = new OnlineCourse()
            { 
                CourseID = courseID,
                DepartmentID = departmentID,
                URL = "http://www.fineartschool.net/Trigonometry",
                Title = "New Onsite Course",
                Credits = 4
            };
    
            // Add the course to the department.
            newDepartment.Courses.Add(newCourse);
    
            // The newly create objects are marked as added, the service will insert these into the store. 
            service.UpdateDepartment(newDepartment);
    
            // Let’s make few more changes to the saved object. 
            // Since the previous changes have now been persisted, call AcceptChanges to
            // reset the ChangeTracker on the objects and mark the state as ObjectState.Unchanged.
            // Note, AcceptChanges sets the tracking on, so you do not need to call StartTracking
            // explicitly.
            newDepartment.AcceptChanges();
            newCourse.AcceptChanges();
    
            // Because the change tracking is enabled
            // the following change will set newCourse.ChangeTracker.State to ObjectState.Modified.
            newCourse.Credits = 6;
            service.UpdateDepartment(newDepartment);
    
        }
    }
    
    static void UpdateDepartmentAndCourses(int departmentID, int courseID)
    {
        using (var service = new Service1Client())
        {
            // Get all the departments.
            List<Department> departments = service.GetDepartments();
            // Use LINQ to Objects to query the departments collection 
            // for the specific department object.
            Department department = departments.Single(d => d.DepartmentID == departmentID);
            department.Budget = department.Budget - 1000.00m;
    
            // Get the specified course that belongs to the department.
            // The reason we are able to access the related course
            // is because the service eagrly loaded the related objects 
            // (using the System.Data.Objects.ObjectQuery(T).Include method).
            Course existingCourse = department.Courses.Single(c => c.CourseID == courseID);
            existingCourse.Credits = 3;
    
            service.UpdateDepartment(department);
        }
    }
    
    static void DeleteDepartmentAndCourses(int departmentID)
    {
        using (var service = new Service1Client())
        {
            List<Department> departments = service.GetDepartments();
    
            Department department = departments.Single(d => d.DepartmentID == departmentID);
    
            // When MarkAsDeleted is called, the entity is removed from the collection,
            // if we modify the collection over which foreach is looping an exception will be thrown.
            // That is why we need to make a copy of the courses collection by 
            // calling department.Courses.ToList();
            List<Course> courses = department.Courses.ToList();
            foreach (var c in courses)
            {
    
                // Marks each comment for the post as Deleted.
                // If another entity have a foreign key relationship with this Course object
                // an exception will be thrown during save operation. 
                c.MarkAsDeleted();
            }
    
            department.MarkAsDeleted();
            service.UpdateDepartment(department);
        }
    }
    
    

  1. Cree una aplicación WPF. Escriba STESchoolModelWPFTest para el nombre del proyecto, en la misma solución que el proyecto anterior.

  2. Agregue una referencia al servicio STEASchoolModelService. Para agregar una referencia al servicio, en el Explorador de soluciones, haga clic con el botón secundario del mouse en la carpeta de referencias y, a continuación, seleccione Agregar referencia de servicio.

    De forma predeterminada, WCF genera un proxy que devuelve la colección IEnumerable. Dado que el método GetDepartments de STESchoolModelService devuelve List, debe configurar el servicio para especificar el tipo devuelto adecuado.

  3. Haga clic con el botón secundario del mouse en el nombre del servicio (ServiceReference1) y, a continuación, seleccione Configurar referencia de servicio. En el cuadro de diálogo Configurar referencia de servicio, seleccione el tipo System.Collections.Generic.List en la lista Tipo de colección.

  4. Agregue una referencia al proyecto STESchoolModelTypes .

  5. Abra el archivo app.config y, a continuación, agregue la cadena de conexión al archivo. Abra el archivo app.config de los proyectos STESchoolModel y, a continuación, copie el elemento connectionStrings para agregarlo como un elemento secundario del elemento configuration del archivo Web.config.

    Ahora puede eliminar el archivo app.config del proyecto STESchoolModel porque nunca se utiliza.

    De forma predeterminada, la plantilla de proyecto agrega el archivo MainWindow.xaml y el archivo de código subyacente correspondiente al proyecto.

  6. Abra MainWindow.xaml y, a continuación, reemplace el código XAML predeterminado con el XAML que define la ventana STESchoolModelWPFTest en WPF. Para obtener más información, vea los comentarios del código.

    <Window x:Class="STESchoolModelWPFTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="508" Width="919" Loaded="Window_Loaded">
        <!-- The code begind code sets the departmentsItemsGrid to the root of the object graph.-->
        <Grid Name="departmentsItemsGrid">
            <!-- comboBoxDepartment points to the root of the graph, that is why the Path is not specified-->
            <ComboBox DisplayMemberPath="DepartmentID" ItemsSource="{Binding}"
                      IsSynchronizedWithCurrentItem="true" 
                      Height="23" Margin="122,12,198,0" Name="comboBoxDepartment" VerticalAlignment="Top"/>
            <!-- listViewItems Path is set to Courses because it is bound to Department.Courses.-->
            <ListView ItemsSource="{Binding Path=Courses}" Name="listViewItems" Margin="34,46,34,50" >
                <ListView.View>
                    <GridView AllowsColumnReorder="False" ColumnHeaderToolTip="Courses" >
                        <GridViewColumn DisplayMemberBinding="{Binding Path=CourseID}" 
                            Header="CourseID" Width="70"/>
                        <!--The TextBox controls are embedded in the two of the following columns.
                            This is done to enable editing in the ListView control. -->
                        <GridViewColumn Header="Title" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBox Height="25" Width="100" Text="{Binding Path=Title}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                        <GridViewColumn Header="Credits" Width="100" >
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBox Height="25" Width="100" Text="{Binding Path=Credits}" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>
            <Label Height="28" Margin="34,12,0,0" Name="departmentLabel" VerticalAlignment="Top" 
                   HorizontalAlignment="Left" Width="93">Department:</Label>
            <!--When the Save and Close button is clicked all the objects will be sent to the service 
                where all the updated objects will be saved to the database. -->
            <Button Height="23" HorizontalAlignment="Right" Margin="0,0,34,12" 
                    Name="buttonClose" VerticalAlignment="Bottom" Width="127" Click="buttonClose_Click">Save and Close</Button>
        </Grid>
    </Window>
    
    
  7. Abra el archivo MainWindow.xaml.cs (o .vb) y, a continuación, reemplace el código predeterminado subyacente con el siguiente código (vea los comentarios del código para obtener más explicación).

    public partial class MainWindow : Window
    {
        private List<Department> departments;
    
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            using (var service = new Service1Client())
            {
                // Set the parent of of your data bound controls to the root of the graph.
                // In the xaml page the appropriate paths should be set on each data bound control.
                // For the comboBoxDepartment it is empty because it is bound to Departments (which is root).
                // For the listViewItems it is set to Courses because it is bound to Department.Courses.
                // Note, that the TextBox controls are embedded in the two of the columns in the listViewItems.
                // This is done to enable editing in the ListView control.
                departments = service.GetDepartments();
                this.departmentsItemsGrid.DataContext = departments;
            }
        }
    
        private void buttonSave_Click(object sender, RoutedEventArgs e)
        {
            using (var service = new Service1Client())
            {
                // Save all the departments and their courses. 
                foreach (var department in departments)
                {
                    service.UpdateDepartment(department);
    
                    // Call AcceptChanges on all the objects 
                    // to resets the change tracker and set the state of the objects to Unchanged.
                    department.AcceptChanges();
                    foreach (var course in department.Courses)
                        course.AcceptChanges();
                }
            }
    
        }
    
        private void buttonClose_Click(object sender, RoutedEventArgs e)
        {
            //Close the form.
            this.Close();
        }
    }
    
    
Mostrar: