0% encontró este documento útil (0 votos)
166 vistas24 páginas

Entity Framework para WinForms

Este documento describe cómo usar Entity Framework en aplicaciones WinForms. Presenta un componente llamado EntityDataSource que permite el enlace de datos en tiempo de diseño a modelos Entity Framework de la misma manera que se enlazan datos a DataSets. Explica cómo crear un modelo de datos Entity Framework e incluye ejemplos de cómo usar EntityDataSource para mostrar datos en controles como cuadrículas y proporcionar funcionalidad como filtrado y ordenación.

Cargado por

marcelgarriga
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
166 vistas24 páginas

Entity Framework para WinForms

Este documento describe cómo usar Entity Framework en aplicaciones WinForms. Presenta un componente llamado EntityDataSource que permite el enlace de datos en tiempo de diseño a modelos Entity Framework de la misma manera que se enlazan datos a DataSets. Explica cómo crear un modelo de datos Entity Framework e incluye ejemplos de cómo usar EntityDataSource para mostrar datos en controles como cuadrículas y proporcionar funcionalidad como filtrado y ordenación.

Cargado por

marcelgarriga
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd

Entity Framework en WinForms

Bernardo Castilho
28 de julio de 2014CPOL
Un componente que facilita el uso de Entity Framework en proyectos de WinForms, incluido el soporte de enlace
en tiempo de diseño.
Descargar muestra (EF4, C #) - 1.03 MB
Descargar muestra (EF4, C #, separando la capa de datos de la capa de la interfaz de usuario) - 120
KB
Descargar muestra (EF4, VB, proporcionada por Mr 'VBForever') - 946 KB
Descargar muestra (EF5, C #) - 1.3 MB 
Descargar muestra (EF6 C #) = 1.3 MB   

Introducción 
ADO.NET Entity Framework es la última tecnología de datos de Microsoft. Le permite crear modelos de datos
conceptuales que son fáciles de programar. También incluye una capa que une los modelos conceptuales con el
almacén de datos real, para que pueda transferir fácilmente los datos entre el modelo conceptual y una base de
datos de respaldo.

Si está acostumbrado a las clases tradicionales de datos ADO.NET ( DataSet, DataAdapter, etc.), entonces
usted probablemente encontrará que trabajar con ADO.NET Entity Framework hace muchas cosas mucho más
simples:

 En lugar de trabajar con clases que se derivan de DataTable y DataRow (por ejemplo
CustomerDataTable, CustomerDataRow), trabajas con los objetos reales (por ejemplo, Customer). El
resultado es un código mucho más limpio y más intuitivo.
 No hay adaptadores de datos. Los datos se recuperan automáticamente cada vez que lo solicita, y
guardar los cambios solo requiere una llamada al método SaveChanges.
 El código de la capa de acceso a datos que Visual Studio genera automáticamente es más fácil de
mantener, ampliar y reutilizar.

No discutiremos los detalles técnicos de ADO.NET Entity Framework. Ese es un tema profundo y bien cubierto en
muchos excelentes artículos y libros. Algunos de ellos se enumeran en la sección de Referencias al final de este
documento.

En su lugar, nos enfocaremos en usar ADO.NET Entity Framework en las aplicaciones WinForms. Aunque todas
las excelentes características de Entity Framework se pueden usar en aplicaciones WinForms, hay poco soporte
para el enlace de datos. Esto es lamentable ya que WinForms sigue siendo la plataforma elegida para muchas
aplicaciones comerciales orientadas a datos, y el enlace de datos es una parte esencial del desarrollo de
WinForms.

Si ha utilizado las clases de datos ADO.NET tradicionales, que está familiarizado con las herramientas de Visual
Studio utilizados para la creación de clases DataSet, y los controles de unión a esas clases en tiempo de
diseño, mediante el uso de sus DataSource, DataMember o propiedades Binding.

La mala noticia es que este rico soporte de tiempo de diseño no es aplicable a las fuentes de datos de Entity
Framework. Incluso los escenarios de enlace simples requieren código. Un pequeño código es suficiente para
que los enlaces realmente básicos funcionen, pero se requiere un trabajo considerable para obtener la
experiencia de enlace completa a la que probablemente esté acostumbrado (por ejemplo, la clasificación
automática, el filtrado y el enlace jerárquico).

La buena noticia es que este soporte se puede agregar con relativamente poco trabajo, y el resultado se puede
encapsular en un componente reutilizable que permite escenarios de enlace enriquecidos para aplicaciones
WinForms. Ese es el objetivo de la biblioteca EFWinForms presentada aquí. La biblioteca contiene dos
componentes: (Control de Usuario ??)

 EntityDataSource: Un componente que encapsula un modelo de datos de entidad y expone sus


elementos como fuentes de datos adecuadas para el enlace, con soporte completo en tiempo de diseño.
 EntityBindingNavigator: Un control que proporciona navegación para una vista, así como una
interfaz de usuario para agregar y eliminar registros, y guardar o cancelar cambios en la base de datos.

El componente EntityDataSource
El componente EntityDataSource desempeña el papel de una DataSet y un BindingSource en la
programación ADO.NET tradicional.

Para usarlo, comienza creando un Modelo de Datos de Entidad (Entity Data Model) (este es el primer paso en
cualquier proyecto de ADO.NET Entity Framework, y es análogo a crear la clase DataSet en la programación
tradicional de ADO.NET).

Una vez que se ha creado el modelo de datos, puede arrastrar un componente EntityDataSource al
formulario y establecer su propiedad ObjectContextType en el tipo ObjectContext representado por el
Modelo de datos de la entidad (nota: si está utilizando la versión EF6 del proyecto, use la DbContextType
propiedad en lugar de ObjectContextType). Cuando hace eso, el componente EntityDataSource crea
una instancia de un contexto de objeto y usa Reflection para encontrar todas las fuentes de datos disponibles en
el contexto. Estas fuentes de datos se exponen a los diseñadores que usan una implementación IListSource.

Después de eso, se puede agregar controles a la forma y se unen a los EntityDataSource que utilizan
sus DataSource, DataMember o propiedades Binding como de costumbre. Un solo EntityDataSource
proporciona acceso a todas las tablas y vistas en el modelo, y guardar los cambios solo requiere una llamada
al método SaveChanges.

Ejemplos
La mejor manera de entender cómo funciona EntityDataSource es mirando algunos ejemplos. Las
siguientes secciones describen cómo utilizar EntityDataSource para implementar cuatro escenarios de
enlace típicos.

Todos los escenarios usan el mismo modelo de datos de entidad, basado en la base de datos tradicional de
NorthWind.

Crear el modelo de datos de entidad (paso común)

Para usar el marco de entidades de ADO.NET, debe crear un modelo de datos de entidad de ADO.NET. Esta es la
capa que contiene el modelo de datos conceptual y la infraestructura requerida para cargar y guardar el modelo
conceptual desde y hacia la base de datos.

Para crear el modelo de datos de entidad, haga clic con el botón derecho en el árbol del explorador de
proyectos en Visual Studio y seleccione opción “Agregar | Nuevo elemento ... ". Aparecerá el cuadro de diálogo
"Agregar nuevo elemento" que se muestra en la imagen a continuación. Seleccione el "Modelo de datos de
entidad ADO.NET", opcionalmente dele un nombre significativo al modelo y haga clic en el botón "Agregar" en
la parte inferior del formulario. 
Aparecerá el "Asistente del modelo de datos de entidad" que se muestra en la imagen a continuación. El primer
paso le permite seleccionar si desea crear el modelo a partir de una base de datos existente o de un modelo
vacío. Seleccione la primera opción y haga clic en "Siguiente".

El siguiente paso es donde selecciona la base de datos que definirá el modelo. Puede seleccionar una conexión
existente o utilizar el botón "Nueva conexión" para crear una nueva. En nuestro ejemplo, crearemos una
conexión a la versión de SQL Server de la base de datos Northwind.
El archivo de la base de datos se llama " NorthWnd.MDF " y se incluye con la muestra.

Una vez que haya seleccionado la base de datos, el asistente le pedirá que elija las tablas, vistas y
procedimientos almacenados que desea incluir en el modelo de datos de la entidad. En nuestra muestra,
simplemente seleccionaremos todas las tablas, como se muestra en la imagen a continuación:
Haga clic en el botón "Finalizar" para generar el modelo. Esto agregará dos elementos a su proyecto:
un archivo " Model1.edmx " que contiene las definiciones XML utilizadas por ADO.NET Entity Framework para
especificar el modelo y un " Model1.Designer.vb " asociado que contiene el código generado, incluyendo
un ObjectContext utilizado para acceder a las clases de datos y de datos tales como Product, Employee,
etc.

Al abrir el archivo edmx , se muestra el Diseñador de modelos de datos de entidad, que le permite inspeccionar y
editar el modelo. También le permite volver a generar el modelo en cualquier momento, lo cual es importante en
caso de que cambie el esquema de la base de datos o si cambia de opinión sobre qué tablas y vistas desea
incluir en el modelo.

Las clases de datos generadas automáticamente en el archivo " Model1.Designer.vb " se declaran como clases


parciales. Esto le permite extenderlos agregando lógica empresarial personalizada en archivos separados que no
se modificarán si decide volver a generar el modelo a partir de la base de datos.

En este punto, puede comenzar a usar el modelo de datos de la siguiente manera:

public Form1()
{
InitializeComponent();
using (var ctx = new NORTHWNDEntities())
{
dataGridView1.DataSource = ctx.Products.ToList();
}
}
VB.NET:
Dim ctx As var = New NORTHWNDEntities

El código crea un archivo ObjectContext que expone los datos del modelo, crea una lista que contiene todos
los productos y muestra la lista en una cuadrícula. Puede editar los productos, y si no hubiéramos eliminado el
objeto de contexto, podríamos guardar los cambios en la base de datos llamando ctx.SaveChanges().
Sin embargo, si ejecuta el código, notará algunas limitaciones serias: no puede ordenar o filtrar los datos, no
puede agregar o eliminar elementos de la lista y, por supuesto, no puede personalizar las columnas de la
cuadrícula en tiempo de diseño utilizando el editor de columnas de la cuadrícula.

Estas limitaciones se deben al hecho de que la lista utilizada como fuente de datos es simplemente una
instantánea de los datos. Entonces, aunque los objetos en la lista son "en vivo", la lista en sí no lo es. Y
lo IBindingList que WinForms crea automáticamente para usted en este caso proporciona una
funcionalidad mínima.

Crear una vista de cuadrícula (con Auto búsqueda)

Para abordar estas limitaciones, agregue un componente EntityDataSource al formulario y use la ventana
de propiedades para establecer su propiedad ObjectContextType en "Sample.NORTHWNDEntities", como se
muestra en la imagen a continuación (nota: si está utilizando la versión EF6 del proyecto, use la propiedad
DbContextType en lugar de ObjectContextType):

El componente EntityDataSource usa el valor ObjectContextType para crear un contexto de objeto que
se usa para generar vistas para todos los elementos definidos en el modelo de datos.

Ahora agregue un control DataGridView al formulario y use la ventana de propiedades para establecer
la propiedad DataSource al componente EntityDataSource y la propiedad DataMember en "Productos",
como se muestra a continuación:
En este punto, la cuadrícula creará automáticamente columnas para exponer las propiedades en la clase
Product. Puede usar el diseñador de columnas de la cuadrícula para reordenar las columnas, establecer su
ancho, título, alineación, formato, etc.

Si ejecuta el proyecto ahora, verá que la cuadrícula se rellena automáticamente y puede realizar todas las tareas
que esperaría, incluida la edición, la clasificación y la adición o eliminación de elementos.

Todo esto funciona porque el componente EntityDataSource envuelve de forma transparente la lista de
productos en una clase EntityBindingList que implementa la interfaz IBindingListView y admite la
clasificación, el filtrado, la adición y la eliminación de elementos.

Guardando los cambios

Después de editar los datos, en algún momento, es probable que desee guardar los cambios nuevamente en la
base de datos. Esto es muy fácil de hacer, cortesía de ADO.NET Entity Framework. Para ilustrar, agregue tres
botones al formulario, establezca su propiedad Text en "Guardar", "Cancelar" y "Actualizar", y adjunte los
siguientes controladores al evento Click:

' save/cancel/refresh changes in the data source
 Private Sub _btnSave_Click(ByVal sender As Object, ByVal e As EventArgs)
        entityDataSource1.SaveChanges
 End Sub
    
 Private Sub _btnCancel_Click(ByVal sender As Object, ByVal e As EventArgs)
        entityDataSource1.CancelChanges
 End Sub
    
 Private Sub _btnRefresh_Click(ByVal sender As Object, ByVal e As EventArgs)
        entityDataSource1.Refresh
 End Sub

El código se explica por sí mismo. El primer botón guarda todos los cambios en la base de datos. El segundo
cancela los cambios volviendo a buscar los datos y sobrescribiendo los cambios, y el tercero vuelve a buscar los
datos pero conserva los cambios.

Sin embargo, falta un detalle importante: al guardar los cambios en la base de datos, debe estar preparado para
hacer frente a las excepciones. Un caso típico son los cambios que violarían las restricciones de la base de
datos. Desafortunadamente, no hay recetas generales para lidiar con este tipo de excepciones. Su naturaleza
depende del esquema de la base de datos y de la aplicación misma.

Independientemente de cómo planee lidiar con las posibles excepciones, el primer paso es atraparlas. Para hacer
esto, puede agregar un bloque try/catch alrededor de la llamada SaveChanges, o puede adjuntar un
controlador al evento DataError del componente EntityDataSource. Así es como nuestra aplicación de
muestra maneja posibles errores al guardar los datos:

Private Sub entityDataSource1_DataError(ByVal sender As Object, ByVal e As DataErrorEv
entArgs)
        MessageBox.Show(("Error Detected:"& vbCrLf + e.Exception.Message))
        entityDataSource1.CancelChanges
        e.Handled = true
End Sub

El código emite una advertencia, cancela los cambios y establece el parámetro Handled a true para indicar
que se ha manejado el error y no se deben lanzar excepciones.

Uso de diccionarios de búsqueda para representar entidades relacionadas


Para terminar este primer ejemplo, exploremos un escenario común. La clase Product tiene dos
propiedades, Category y Supplier, que representan entidades relacionadas. De forma predeterminada, estas
propiedades no están incluidas en la cuadrícula, pero puede usar el editor de columnas de la cuadrícula para
crear esas columnas. La siguiente imagen muestra cómo hacerlo:

El problema con esto es que la cuadrícula no sabe cómo representar la entidad relacionada, por lo que
simplemente usa el método ToString, y el resultado son dos columnas de solo lectura que consisten solo en
"Sample.Category" y "Sample.Supplier".

Pero lo que realmente le gustaría tener es una columna que muestre la categoría y los nombres de los
proveedores, idealmente con editores que le permitan editar la categoría y el proveedor seleccionando de las
listas. Esto generalmente se hace escribiendo código para crear y vincular columnas personalizadas
(un DataGridViewComboBoxColumn si está utilizando el control DataGridView).

Debido a que este es un escenario tan común, el componente EntityDataSource proporciona una
propiedad de extensor llamada AutoLookup. Esta propiedad está automáticamente disponible en
cualquier control DataGridView o C1FlexGrid en el formulario ( C1FlexGrid es un control de cuadrícula
popular que es sustancialmente más rápido y más rico en funciones que el DataGridView).

Tenga en cuenta que aunque el componente EntityDataSource es compatible C1FlexGrid,


el ensamblaje EFWinForms no depende del ensamblaje C1FlexGrid . Esto se logró usando la palabra clave 
Dynamic que esencialmente se basa en Reflection para enlazar propiedades en tiempo de ejecución. El mismo
mecanismo podría usarse para extender el componente EntityDataSource con soporte para otras
cuadrículas.

La imagen a continuación muestra cómo puede habilitar la propiedad AutoLookup en un DataGridView:


Una vez que habilite la propiedad AutoLookup en cualquier cuadrícula, el componente EntityDataSource
escaneará automáticamente las columnas en la cuadrícula para reemplazar cualquier columna regular vinculada
a entidades relacionadas con columnas editables basadas en un "mapa de datos" que contiene una lista de las
posibles entidades relacionadas y valores de visualización para cada uno.

La siguiente imagen muestra el efecto de establecer AutoLookup como verdadero en nuestra cuadrícula de


productos:

Observe cómo las columnas "Categoría" y "Proveedor" ahora muestran la categoría y los nombres de los
proveedores, y cómo puede seleccionar un nuevo proveedor para un producto seleccionando de una lista.

En este punto, es posible que se pregunte cómo se deben EntityDataSource seleccionar los campos de las
entidades relacionadas en la cuadrícula. Esto se hace usando el siguiente algoritmo:

1. Si la clase implementa un método ToString (en lugar de simplemente heredarlo), entonces


la implementación ToString se usa para representar a la entidad.
2. De lo contrario, si la clase contiene una propiedad de tipo string que tiene "Name" en su nombre,
entonces esa propiedad se utiliza para representar a la entidad.
3. De lo contrario, si la clase contiene una propiedad de tipo string que tiene "Descripción" en su
nombre, esa propiedad se utiliza para representar a la entidad.
4. Si no se aplica nada de lo anterior, no se puede crear un mapa de datos para esa clase.
La primera regla es la más general y flexible. Por ejemplo, la clase Employee en la base de datos Northwind
tiene propiedades FirstName y LastName. Cualquiera de los dos podría usarse para representar a la entidad
en una lista, pero idealmente, le gustaría usar ambos. Para hacer esto, simplemente anulamos el método
ToString en la clase Employee y construimos la representación de cadena que queremos usar:

''' <summary>
''' Add a field with the employee's full name
''' </summary>
Public Class Employee
    
    Public ReadOnly Property FullName As String
        Get
            Return String.Format("{0} {1}", FirstName, LastName)
        End Get
    End Property
    
    Public Overrides Function ToString() As String
        Return Me.FullName
    End Function
End Class

Observe cómo esto amplía la clase predeterminada creada por el asistente ADO.NET Entity Framework. Si en
algún momento decide volver a generar el modelo de datos de la entidad, nuestra nueva propiedad FullName
y la implementación ToString no se verá afectada.

En la base de datos Northwind, Employee es la única clase que requiere una consideración especial para los
mapas de datos. Todas las demás clases tienen propiedades como " CustomerName" o
" CategoryDescription" que son utilizadas automáticamente por EntityDataSource y proporcionan el
efecto deseado.

Esto concluye el primer ejemplo. En todos los ejemplos que siguen, estableceremos la propiedad  AutoLookup
a true en todas las cuadrículas.

Crear una vista de detalles maestros

Muchos modelos de datos tienen entidades que contienen listas de entidades relacionadas. Por ejemplo, las
categorías tienen listas de productos, los pedidos tienen detalles de pedidos, etc. Este tipo de relación a menudo
se representa como dos cuadrículas, una maestra que muestra las entidades "contenedor" y un detalle que
muestra la lista de entidades relacionadas para la maestra seleccionada actualmente.

Crear enlaces de maestros detalles en WinForms es extremadamente fácil. Para ilustrar, agreguemos dos
cuadrículas al formulario (en una nueva página de pestaña). La cuadrícula superior mostrará las categorías y la
cuadrícula inferior mostrará los productos en la categoría seleccionada actualmente.

Para crear los enlaces, seleccione la cuadrícula superior y establezca la propiedad DataSource en
el componente EntityDataSource y la propiedad DataMember en "Categorías", como se muestra a
continuación:
A continuación, seleccione la cuadrícula inferior y nuevamente, configure la propiedad DataSource para
el componente EntityDataSource. Esta vez, establezca la propiedad DataMember en
"Categorías.Productos", como se muestra a continuación:

Observe cómo la propiedad DataMember de la cuadrícula de detalles se establece en "Categorías.Productos", y


no directamente en "Productos". Esto es lo que hace que la cuadrícula de detalles se sincronice automáticamente
con la selección actual en la cuadrícula maestra.

Como antes, puede usar el editor de columnas de la cuadrícula para personalizar las columnas y puede usar
la propiedad AutoLookup del extensor para mostrar entidades relacionadas (como las de cada
producto Supplier).

Si vuelve a ejecutar el proyecto, verá que nuestra página de maestros detalles funciona como se esperaba. A
medida que navega por las categorías en la cuadrícula superior, verá los productos correspondientes en la
cuadrícula inferior.

Crear una vista de formulario (con un EntityBindingNavigator)


Las cuadrículas son excelentes porque le permiten editar, organizar y navegar por los datos. Pero en muchos
casos, un diseño de estilo de formulario personalizado puede proporcionar una mejor experiencia de usuario. En
estos casos, debe proporcionar al usuario una forma de navegar por los datos.

WinForms proporciona un control BindingNavigator que maneja esto por usted. Los trabajos
BindingNavigator con el componente BindingSource para proporcionar un recuento de registro,
botones para navegar hasta las primeras anterior, siguiente y último registro, y para añadir y eliminar registros.

El ensamblaje EFWinForms incluye un control EntityBindingNavigator que proporciona una funcionalidad


similar pero funciona contra el EntityDataSourcecomponente. Además de la funcionalidad de navegación,
el control EntityBindingNavigator incluye botones para guardar o cancelar los cambios.

Para crear la vista de formulario, comience agregando un control EntityBindingNavigator a la página y


estableciendo su propiedad  DataSource a EntityDataSource en el formulario y la DataMember
propiedad en "Pedidos". Esto permitirá a los usuarios seleccionar el pedido actual, agregar o eliminar pedidos,
guardar los cambios nuevamente en la base de datos o cancelar los cambios.

A continuación, agreguemos algunos cuadros de texto encuadernados al formulario. Comience agregando una


etiqueta de "Fecha de pedido" y un cuadro de texto al lado. Seleccione el cuadro de texto y haga clic en el botón
de puntos suspensivos al lado del nodo (DataBindings) / (Avanzado) en la ventana de propiedades:

Aparecerá el cuadro de diálogo "Formateo y enlace avanzado". Seleccione el nodo "Orders.OrderDate" en el


menú desplegable "Binding" como se muestra en la imagen a continuación.
Después de elegir el enlace, use el mismo cuadro de diálogo para seleccionar el formato que desea usar para
mostrar la fecha del pedido.

Repita este proceso para agregar tres cuadros de texto enlazados más:

 "Cliente", vinculado a "Pedidos.Cliente.NombreEmpresa",


 "Empleado", vinculado a "Pedidos.Empleado.Nombre completo", y
 "Importe", vinculado a "Pedidos. Importe".

Observe que la clase Order aún no tiene una propiedad Amount. Crearemos esta propiedad extendiendo
la Order clase de la misma manera que extendimos la Employee clase anteriormente. Aquí está la
implementación:

''' <summary>
''' Add a field with total order amount.
''' </summary>
Public Class Order
    
    ' calculate amount for this order
    Public ReadOnly Property Amount As Decimal
        Get
            Dim q = from
            od
            Me.Order_Details
            Dim od.Amount As select
            Return q.Sum
        End Get
    End Property
End Class
''' <summary>
''' Add a field with order detail amount.
''' </summary>
Public Class Order_Detail
    
    ' calculate amount for this order detail
    Public ReadOnly Property Amount As Decimal
        Get
            Return (Quantity  _
                        * (UnitPrice * (1 - CType(Discount,Decimal))))
        End Get
    End Property
End Class

El código usa LINQ para calcular la cantidad total para cada  Order_Detail y para cada Order.

Finalmente, agregue una cuadrícula al formulario y use la ventana de propiedades para establecer su propiedad
DataSource en el componente EntityDataSource y la propiedad DataMember en
"Orders.Order_Details". Este es otro ejemplo de enlace jerárquico. Cuando el usuario navega a un pedido
utilizando el control EntityBindingNavigator, la cuadrícula mostrará automáticamente los detalles del
pedido actual.

Use el editor de columnas de la cuadrícula para agregar una columna vinculada a la propiedad Product de los
detalles del pedido y asegúrese de que la propiedad AutoLookup de la cuadrícula esté establecida en true.

Si ejecuta el proyecto ahora, verá una pantalla similar a la de la imagen a continuación:

Esta vista única muestra la mayoría de las características de la biblioteca EFWinForms :


los componentes EntityDataSource y EntityBindingNavigator, la característica AutoLookup
del enlace EntityDataSource simple y jerárquico. Y todo esto se hizo sin escribir una sola línea de código (a
excepción de las extensiones de datos, que no son realmente parte de la interfaz de usuario).

Crear una vista de gráfico (con filtrado)

Ahora que hemos cubierto las cuadrículas y los cuadros de texto, nuestro próximo ejemplo mostrará cómo
puede usar el enlace para crear un gráfico basado en un filtro dinámico.

Nuestro cuadro mostrará los precios unitarios del producto. Para evitar mostrar demasiados datos, permitiremos
que los usuarios especifiquen el precio unitario mínimo que debe incluirse en el gráfico.

El primer paso en esta muestra es la creación de un nuevo componente EntityDataSource. Esto es necesario


porque de manera predeterminada, cada uno EntityDataSource expone solo una vista de cada tabla. Si
aplicamos el filtro a la tabla de productos EntityDataSource que hemos estado usando en toda la muestra,
el filtro también se aplicaría a todas las otras muestras. En algunos casos, eso puede ser deseable, pero ese no es
el caso aquí. Queremos que el filtro se aplique solo al gráfico.
Después de agregar el nuevo EntityDataSource al formulario, recuerde establecer su propiedad
ObjectContextType en "Sample.NORTHWNDEntities", como lo hicimos antes.

Para facilitar la vinculación de los datos al gráfico en tiempo de diseño, también agregaremos
un componente BindingSource regular al formulario y estableceremos su propiedad DataSource en el
nuevo componente EntityDataSource que acabamos de agregar al formulario, y su propiedad
DataMember en "Productos". El componente BindingSource también facilita la aplicación del filtro
mediante el uso de la ventana de propiedades.

Inicialice también estableciendo BindingSource la propiedad Name en "chartBindingSource", la propiedad


Filter en "(No descontinuado) y UnitPrice> 30", y la propiedad Sort en "UnitPrice":

Después de agregar el nuevo EntityDataSource y los componentes BindingSource al formulario,


agreguemos la nueva interfaz de usuario, que consiste en un cuadro de texto donde el usuario ingresará el
precio unitario mínimo para el gráfico, el control del gráfico y una cuadrícula para mostrar los datos que se
grafican.

Para vincular el gráfico, comience configurando su propiedad DataSource al componente BindingSource


que acabamos de agregar al formulario. Luego seleccione el gráfico y use el botón de puntos suspensivos para
abrir el editor de la propiedad Series:
Esto abrirá el editor de series, que usaremos para establecer la propiedad XValueMember de la serie en
"ProductName" y YValueMember en "UnitPrice":

A continuación, vincule la cuadrícula estableciendo su propiedad DataSource en la nueva BindingSource


como hicimos para el gráfico. Tenga en cuenta que en este caso, no tenemos que establecer la propiedad
DataMember ya que el origen del enlace ya es específico de la tabla de productos. Use el editor de columnas
de la cuadrícula para personalizar cómo se deben mostrar los datos y establezca la propiedad AutoLookup en
true en caso de que desee mostrar entidades relacionadas en la cuadrícula (por ejemplo, proveedor de
productos).

Para habilitar el filtrado, agregaremos controladores a dos eventos de cuadro de texto:

' update filter when the user edits the text box
    Private Sub _txtMinPrice_Validated(ByVal sender As Object, ByVal e As EventArgs)
        ApplyFilter
    End Sub
    
    Private Sub _txtMinPrice_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)
        If (e.KeyChar = 13) Then
            ApplyFilter
            e.Handled = true
        End If
        
    End Sub

Y aquí está el método que realmente aplica el filtro:

' apply the filter
    Private Sub ApplyFilter()
        ' never show discontinued products
        Dim filter = "(Not Discontinued)"
        ' apply minimum price condition
        Dim minPrice = _txtMinPrice.Text.Trim
        If Not String.IsNullOrEmpty(minPrice) Then
            Dim d As Double
            If Not Double.TryParse(minPrice, d) Then
                MessageBox.Show("Invalid Minimum Unit Price, please try again.")
            Else
                filter = (filter + String.Format(" and (UnitPrice >= {0})", minPrice))
            End If
            
        End If
        
        ' set the filter
        chartBindingSource.Filter = filter
    End Sub

El método crea una expresión de cadena que comienza especificando que no queremos mostrar ningún
producto descontinuado, luego agrega la condición de que el precio debe ser mayor o igual al precio mínimo
especificado por el usuario. Finalmente, el método aplica la expresión resultante a la propiedad Filter
del componente BindingSource.

Antes de que nuestro gráfico esté listo, se debe agregar un controlador de eventos más:

' update chart when list changes
    Private Overloads Sub chartBindingSource_ListChanged(ByVal sender As Object, ByVal e As List
ChangedEventArgs)
        chart1.DataBind
    End Sub
    

A diferencia de las cuadrículas, que escuchan los cambios en la fuente de datos, el gráfico debe actualizarse
explícitamente cuando cambian los datos. Esta puede ser una función de optimización o un error. De cualquier
manera, llamar al método DataBind actualiza el gráfico.

Ahora puede ejecutar el proyecto nuevamente y ver el efecto de cambiar el valor del precio unitario mínimo en
el gráfico: de manera predeterminada, el filtro está configurado para mostrar productos con un
valor UnitPrice mayor o igual a $ 30:

Si eleva el precio unitario mínimo a $ 50 y presiona Enter, el cuadro muestra inmediatamente el cambio y solo
quedan cinco productos:
Observe que la cuadrícula también se filtra y, si ordena los datos en la cuadrícula, el gráfico cambia para reflejar
el nuevo orden de clasificación.

Crear vistas personalizadas (con LINQ)

Una de las grandes características de Entity Framework es que funciona muy bien con LINQ, que proporciona
una gran flexibilidad y productividad para crear vistas personalizadas de sus datos.

Puede convertir el resultado de cualquier consulta LINQ en una lista y usarlo como fuente de enlace, pero como
ya comentamos anteriormente, eso tiene algunas limitaciones. Las vistas creadas de esta manera no se pueden
ordenar o filtrar, por ejemplo.

La clase EntityDataSource también puede ayudar en este caso. Tiene un método CreateView que toma
cualquiera IEnumerable y lo convierte en un IBindingListView con soporte para ordenar y filtrar.

Para ilustrar esta característica, creemos una pestaña adicional con un control DataGridView. A diferencia de
las muestras anteriores, que usaban vistas ya definidas en el modelo de datos, esta mostrará una vista creada en
base a una consulta LINQ. Aquí está el código:

Sub ShowLinq()
' some LINQ query
Dim q = from Order o in entityDataSource1.EntitySets("Orders") select new
OrderID = o.OrderID,
ShipName = o.ShipName,
ShipAddress = o.ShipAddress,
ShipCity = o.ShipCity,
ShipCountry = o.ShipCountry,
Customer = o.Customer.CompanyName,
Address = o.Customer.Address,
City = o.Customer.City,
Country = o.Customer.Country,
SalesPerson = o.Employee.FirstName & " " & o.Employee.LastName,
OrderDate = o.OrderDate,
RequiredDate = o.RequiredDate,
ShippedDate = o.ShippedDate,
Amount = ( from Order_Detail od in o.Order_Details select CDbl(od.UnitPrice *
od.Quantity * (1 - od.Discount) )).Sum()
End Sub

' create BindingList (sortable/filterable)


Dim bindingList = entityDataSource1.CreateView(q)
' assign BindingList to grid
_gridLINQ.DataSource = bindingList

La mayor parte del código es una consulta LINQ que combina datos de pedidos, clientes, empleados y detalles
de pedidos para generar una vista similar a la consulta "Facturas" en la base de datos. La consulta incluye una
consulta anidada que agrega las cantidades de los detalles del pedido para cada pedido.

El resultado de la consulta se muestra en la cuadrícula, que se puede ordenar o filtrar como de costumbre:

Usando una capa de datos para separar las


preocupaciones
Cuando se publicó este artículo por primera vez, muchos lectores señalaron de inmediato que la aplicación de
muestra contenía tanto la capa de acceso a datos como la interfaz de usuario, y que este es un mal diseño. Estoy
de acuerdo en que en la mayoría de las aplicaciones esta separación es una buena idea. Separar la capa de datos
de la interfaz de usuario tiene muchas ventajas, que incluyen, por ejemplo:

 Organización mejorada, claridad y mantenimiento más fácil.


 La capa de datos se puede reutilizar en muchas aplicaciones.
 Restringir el acceso a la base de datos mejora la integridad y la seguridad de la base de datos.

Teniendo esto en cuenta, los tentadores menús "Agregar fuente de datos" en Visual Studio parecen menos
atractivos. Le permiten agregar la capa de datos directamente a la aplicación y vincularla a los elementos de la
interfaz de usuario sin separación, lo cual es excelente para crear aplicaciones rápidamente, pero a menudo
genera dolores de cabeza de mantenimiento.

Esta sección muestra cómo puede usar el componente EntityDataSource para crear capas de datos
reutilizables que encapsulan todo el acceso a los datos y proporcionan datos a las capas de IU implementadas
como ensamblajes separados. El resultado es una arquitectura más limpia, segura y fácil de mantener.
La solución que se describe a continuación se incluye en el archivo SeparateConcerns.zip . La solución consta de
tres proyectos:

 EFWinForms : esta es la misma clase incluida en la aplicación original. Define la clase


EntityDataSource utilizada por la capa de datos.
 DataLayer : esta es la capa de acceso a datos. La clase DataSource contiene todo el código
relacionado con el acceso a la base de datos, incluido el modelo de datos de la entidad y la cadena de conexión.
 SampleApplication : esta es la capa de la interfaz de usuario. Implementa un único formulario de
detalles maestros que usa la clase DataLayer para todos los accesos a datos. El enlace de datos funciona
exactamente como lo hizo en la muestra original, pero este proyecto no tiene ningún conocimiento sobre la
base de datos (sin cadena de conexión, modelo de datos, etc.).

Tenga en cuenta que esta sigue siendo una aplicación WinForms pura. No utiliza WCF, servicios web, etc. Es solo
una pequeña variación del tema original, lo que demuestra que puede separar fácilmente la capa de acceso a
datos de la interfaz de usuario si eso es apropiado para su aplicación y aún así disfrutar de los beneficios de
Entity Framework, WinForms y enlace de datos.

Implementando la capa de datos

El proyecto de capa de datos implementa una clase DataSource que se implementa de la siguiente manera:

Public Class DataSource
    Inherits Component
    Implements IListSource
    
    ' ** fields
    Private _ds As EFWinforms.EntityDataSource
    
    Private Const DATABASEFILE As String = "c:\util\database\northwnd.mdf"
    
    Private Const CONNECTIONSTRING As String = ("metadata=res://*/Model1.csdl|
res://*/Model1.ssdl|res:" + ("//*/Model1.msl;provider=System.Data.SqlClient;" + ("provider conne
ction string=""""Data Source=.\SQLEXPRESS;" + ("AttachDbFilename="  _
                + (DATABASEFILE + (";" + ("Integrated Security=True;Connect Timeout=30;" + "User 
Instance=True;MultipleActiveResultSets=True""""")))))))
    
    ' ** ctors
    Public Sub New()
        MyBase.New
        InitializeComponent
        ' check that the database file exists
        If Not System.IO.File.Exists(DATABASEFILE) Then
            Throw New Exception(("Database file not found. " + ("This sample requires the NorthW
ind database at " + DATABASEFILE)))
        End If
        
        ' create entity data source
        Me._ds = New EFWinforms.EntityDataSource
        ' create object context (specifying the connection string)
        Me._ds.ObjectContext = New northwndEntities(CONNECTIONSTRING)
    End Sub
    
    Public Sub New(ByVal container As IContainer)
        MyBase.New
        container.Add(Me)
    End Sub
    
    ' ** object model
    Public Sub SaveChanges()
        ' optionally perform any logic/validation before saving ...
        ' save the changes if everything is OK
        Me._ds.SaveChanges
    End Sub
    
    ' ** IListSource
    ReadOnly Property IListSource_ContainsListCollection As Boolean Implements IListSource.Conta
insListCollection
        Get
            Return true
        End Get
    End Property
    
    Function IListSource_GetList_(() As System.Collections.IList Implements IListSource.GetList.
(
        Return CType(Me._ds,IListSource).GetList
    End Function

La clase DataSource hereda de Component, lo que significa que se puede agregar a los
formularios. Implementa la interfaz IListSource, lo que significa que puede usarse como una fuente de
enlace enriquecida.

El constructor DataSource crea una instancia de un EntityDataSource y la vincula a un modelo de datos


de entidad utilizando la propiedad ObjectContext Tenga en cuenta que en este caso, la cadena de conexión
especifica una ubicación absoluta para la base de datos. La clase verifica que el archivo de la base de datos esté
instalado correctamente y genera una excepción si no se puede encontrar. Antes de ejecutar el proyecto, debe
copiar el archivo " northwnd.mdf " a la carpeta " c: \ util \ database ". Este es el mismo archivo de base de datos
utilizado en la muestra original. ( Sugerencia : la constante de cadena de conexión de aspecto desagradable se
copió del archivo App.Config ).

El modelo de objetos para el DataSource consiste en un único método SaveChanges, que se implementa


con una llamada al componente EntityDataSource subyacente. Esto es necesario si desea que los clientes
puedan guardar los cambios en la base de datos (recuerde, el cliente ya no tiene acceso a la base de datos más
allá de lo que proporciona la capa de datos). La implementación del método se puede utilizar para realizar
cualquier validación que sea necesaria antes de confirmar los cambios en la base de datos.

Finalmente, la interfaz IListSource se implementa delegando todas las llamadas


al componente EntityDataSource subyacente.

Esa es toda la capa de datos. En las aplicaciones reales, puede personalizarlo trabajando con el Modelo de datos
de entidad, ya sea utilizando el diseñador o escribiendo una lógica comercial personalizada, exactamente como
lo hicimos en la aplicación original.

Implementación de la capa de IU
La capa de IU es una aplicación WinForms separada que consta de un único formulario que se muestra a
continuación:
El formulario contiene dos cuadrículas y un componente DataSource. El componente DataSource es el
implementado por el proyecto de capa de datos y descrito anteriormente.

Las cuadrículas están vinculadas a la fuente de datos utilizando las siguientes propiedades (establecidas en
tiempo de diseño como de costumbre):

' top grid: categories
dataGridView1.DataSource = dataSource1
dataGridView1.DataMember = "Categories"
' bottom grid: products for the currently selected category
dataGridView2.DataSource = dataSource1
dataGridView2.DataMember = "Categories.Products"

Observe cómo funciona el mecanismo de enlace de datos exactamente como lo hizo en la muestra original. Pero
esta vez la interfaz de usuario no tiene conocimiento de la base de datos o la cadena de conexión. El ensamblaje
de la capa de datos podría usarse en otros proyectos y mantenerse por separado de los proyectos de la interfaz
de usuario.

Limitaciones
Creo que la biblioteca EFWinForms es un proyecto interesante que hace que el uso de ADO.NET Entity
Framework en WinForms sea mucho más fácil (y espero que después de leer este artículo esté de acuerdo).

Pero en su estado actual, tiene algunas limitaciones que debe considerar antes de usarlo en sus proyectos:

1. Requisitos de memoria : la clase EntityDataSource no incluye ningún tipo de filtrado del lado del
servidor, ni ningún tipo de almacenamiento en caché inteligente o gestión de memoria. Si tiene una tabla con un
millón de registros, los traerá a la memoria y no los descartará hasta que se elimine el componente.
2. Alcance del contexto : cada instancia EntityDataSource encapsula todos los ObjectContext y
cada uno de los datos que contiene. No puede compartir fácilmente un contexto en toda la aplicación. Por
ejemplo, si su aplicación usa múltiples formularios, entonces cada formulario generalmente tendrá una copia de
los datos EntityDataSource y su propia. Los cambios aplicados a los objetos en un contexto no serán
visibles para otros contextos a menos que los datos se guarden en la base de datos y los contextos se actualicen.
3. Compatibilidad limitada con LINQ : como se mostró en el último ejemplo, puede usar las declaraciones
EntityDataSource arbitrarias de LINQ. Pero esas vistas son algo limitadas, no le permiten agregar o eliminar
registros, por ejemplo.

Si alguna de estas limitaciones le concierne, hay opciones. Puede optar por mejorar la biblioteca (se incluye el
código fuente completo, y cualquier corrección o mejora sería muy apreciada).

Otra opción sería utilizar uno de los varios productos comerciales que son sustancialmente más potentes
que EFWinForms e incluyen características como el almacenamiento en caché inteligente de datos, las fuentes
de datos virtuales y el soporte multiplataforma. La sección de referencias a continuación incluye enlaces a
algunos de estos productos. (Uno de los productos enumerados, ComponentOne Studio para Entity Framework,
es producido por la compañía para la que trabajo y está estrechamente relacionado con el contenido de este
artículo).

Entity Framework 5 
Este artículo fue escrito usando la versión 4.x de Microsoft Entity Framework. Desde entonces, Microsoft lanzó la
versión 5, que se instala de manera predeterminada con Visual Studio 2012, pero también se puede usar con
Visual Studio 2010.
La versión 5 de Entity Framework tiene una serie de adiciones y cambios. La diferencia más visible desde la
perspectiva de este artículo es que los modelos creados con EF4 tienen un objeto ObjectContext que
representa la base de datos y colecciones ObjectSet<T> que representan las tablas de la base de datos. Por el
contrario, los modelos EF5 tienen un objeto DbContext que representa la base de datos y colecciones
DBSet<T> que representan las tablas de la base de datos.

La versión original de la EFWinForms biblioteca se basa en clases ObjectContext y ObjectSet y por lo


tanto no funciona con los nuevos modelos EF5.

Afortunadamente, el mapeo entre las clases antiguas y las nuevas es sencillo. Solo tardé unos 30 minutos en
crear una nueva versión de la biblioteca <string> EFWinForms basada en EF5. Puede descargar esta nueva
versión desde el enlace en la parte superior del artículo, o puede obtenerla directamente desde
aquí: EFWinForms para Entity Framework 5 . 

Nota : tuve algunos problemas para que el método Refresh de contexto funcione correctamente. Al principio
me pareció que EF5 simplemente ignoró el valor del argumento RefreshMode. Resultó (después de algunas
investigaciones adicionales y la ayuda de algunos lectores) que EF5 no detecta cambios automáticamente
cuando se llama al Refresh método. Esto se puede solucionar llamando
al DbContext.ChangeTracker.DetectChanges método antes de llamar Refresh. El siguiente código
muestra la nueva llamada:

    ''' <summary>
    ''' Refreshes this set's view by re-loading from the database. 
    ''' </summary>
    Public Sub RefreshView()
        If ((Not (_list) Is Nothing)  _
                    AndAlso (Not (Query) Is Nothing)) Then
            ' call this before Refresh (required in EF5 but not in EF4!)
            _ds.DbContext.ChangeTracker.DetectChanges
            ' refresh and make sure client wins 
            Dim ctx = CType(_ds.DbContext,System.Data.Entity.Infrastructure.IObjectContextAdapte
r).ObjectContext
            ctx.Refresh(RefreshMode.ClientWins, Query)
            ' show changes
            _list.Refresh
        End If
        
    End Sub

He actualizado el código para abordar esto y ahora ambos métodos CancelChanges y los RefreshView


parecen funcionar como lo hicieron en la versión EF4 del código.

Entity Framework 6 
Varios lectores me preguntaron sobre Entity Framework 6, que Microsoft lanzó en octubre de 2013. EF6 tiene
algunas mejoras significativas sobre la versión anterior. Puede encontrar detalles sobre las nuevas funciones
aquí: 

http://blogs.msdn.com/b/adonet/archive/2013/10/17/ef6-rtm-available.aspx 

Actualizar proyectos de EF5 a EF6 es fácil y directo. Básicamente requiere usar nuget para instalar EF6 en sus
proyectos (reemplazará a EF5 automáticamente), volver a generar los modelos de datos con las herramientas de
tiempo de diseño EF6 y ajustar algunos espacios de nombres. Puede encontrar una buena guía detallada aquí:

http://msdn.microsoft.com/en-US/data/upgradeEF6  
Seguí los pasos de esta guía para actualizar la muestra incluida en este artículo, y conseguí que funcionara en un
par de minutos. No tuve que cambiar el código en absoluto, excepto por los ajustes del espacio de nombres, lo
cual fue una agradable sorpresa. Agregué la versión EF6 del proyecto a las descargas incluidas con el
proyecto. Espero que lo disfrutes. 

Referencias 
Los siguientes enlaces apuntan a artículos o libros sobre ADO.NET Entity Framework:

 Introducción al marco de la entidad  


 Entity Framework 4 consejos para el desarrollo de WinForms  
 Muestra simple con Entity Framework
 El rendimiento y el marco de la entidad
 Presentación de ADO.NET Entity Framework
 Utilice una entidad de Entity Framework como fuente de datos de WinForms
 Marco de entidad de programación

Los siguientes enlaces apuntan a algunos productos comerciales que admiten o amplían el Marco de entidades
ADO.NET. La lista no es exhaustiva y no constituye una recomendación o respaldo. Se entiende solo como un
punto de partida para su propia investigación y para ilustrar el rico ecosistema que está creciendo rápidamente
alrededor del Marco de Entidades de ADO.NET:

 ComponentOne Studio para Entity Framework


 CodeFluent Entities
 CodeSmith PLinqO para Entity Framework
 DevArt Entity Developer
 IdeaBlade DevForce
 LLBLGen Pro

Historia
 8 de julio de 2011: se actualizó la muestra. La muestra original contenía una referencia a una asamblea
de terceros. El ensamblaje no se usa y se puede eliminar de forma segura, pero puede evitar que algunas
personas ejecuten el proyecto. La versión actualizada de la muestra en el archivo adjunto elimina la referencia.
 10 de julio de 2011: se actualizó la muestra. El EntityNavigator en una de las pestañas tenía todos los
botones ocultos, eso se corrige en la versión adjunta.
 17 de julio de 2011: Código actualizado para corregir un par de pequeños problemas relacionados con el
enlace jerárquico.
 18 de julio de 2011: se corrigieron algunos problemas menores relacionados con el uso en tiempo de
diseño y la configuración de la ObjectContextpropiedad.
 19 de julio de 2011: se agregó una sección que describe cómo puede separar la capa de datos de la capa
de la interfaz de usuario y se agregó una nueva muestra para demostrarlo.  
 25 de julio de 2013: se agregó una sección que describe cómo usar la biblioteca con Entity Framework 5,
que se incluye con Visual Studio 2012. 
 5 de febrero de 2014: se agregó una sección que describe cómo usar la biblioteca con Entity Framework
6, que se incluye con Visual Studio 2012 y 2013. 

C:\Users\marce\Documents\Visual Studio 2015\DataGridView\EFWinForms_VB

https://www.codeproject.com/Articles/221931/Entity-Framework-in-WinForms

También podría gustarte