Entity Framework para WinForms
Entity Framework para 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 ??)
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.
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.
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.
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.
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.
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).
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:
''' <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.
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:
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.
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.
Repita este proceso para agregar tres cuadros de texto enlazados más:
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.
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.
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.
' 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
' 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.
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
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:
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:
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.
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.
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.
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
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:
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:
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.
https://www.codeproject.com/Articles/221931/Entity-Framework-in-WinForms