Entity Framework in WinForms. Part 1

Introduction


I bring to your attention a translation of an article by Bernardo Castilho devoted to binding these entities to WinForms controls. I hope the article will also help you in the work as well as me.


Today, Winforms is still the platform for many data-oriented business applications, and data binding is an integral part of Winforms development. If you used the standard ADO.NET classes to work with data, then you are familiar with the tools used to create DataSet classes and bind these classes at design time using their DataSource , DataMember, or Binding properties .

The bad news is that these rich development capabilities are not applicable to Entity Framework data sources.. Even a simple binding requires writing code. To get a working basic binding, a little code is enough, but to get a full binding, which we are used to (such as automatic sorting, filtering, and hierarchical binding), a significant amount of work is required.

The good news is that using the EFWinForms library you can easily perform data binding and create a reusable component for data binding in Winforms. The library has two components:

  • EntityDataSource : a component that encapsulates a data entity model and presents its elements as data sources suitable for binding, with full support during development.
  • EntityBindingNavigator : A control that provides navigation for viewing and provides a user interface for adding and deleting records, saving or reverting changes to the database.


EntityDataSource Component


The EntityDataSource component plays the role of DataSet and BindingSource in standard ADO.NET programming. To use it, you must first create an Entity Data Model.

Thereafter, drag component EntityDataSource on shape and property ObjectContextType type ObjectContext set in one of the presented Entity Data Model contexts. (If you have EF6, use the DbContextType property instead of ObjectContextType). When you do this, the EntityDataSource componentcreates a context object and uses reflection to find all the data sources available in the context. These data sources then appear in the designer using the IListSource implementation .

After that, you can add controls to the form and bind them to EntityDataSource using their DataSource , DataMember and Binding properties as usual. One EntityDataSource provides access to all tables and views in the model and saves changes only by calling the SaveChanges method .

Examples


The best way to understand how EntityDataSource works is to consider a few examples. The next section discusses how to use EntityDataSource to implement four typical data binding scenarios. All scripts use a single Entity Data Model, based on the Northwind database .

To use it, you must first create an Entity Data Model.

Creating an Entity Data Model


To create an Entity Data Model , right-click the project tree in the Solution Explorer window and then select “Add | New Item ... ” .

The “Add New Item” dialog box appears . Select “ADO.NET Entity Data Model” , name the model and click the “Add” button located at the bottom of the form .



The “Entity Data Model Wizard” dialog box will appear in which you need to choose which model you will create: a model generated from an existing database or an empty model. Select the first option and click “Next” .



The next step is to select the database to create the model. You can select an existing connection, or using the “New Connection” button , create a new one. In our example, we will create a connection to the Northwind SQL Server database.



The database file is called “NorthWnd.mdf and is part of the example. After you have selected a database, the wizard prompts you to select the tables, views, and stored procedures that you want to include in the Entity Data Model . In our example, we simply select all the tables, as shown in the figure below:



Click the “Finish” buttonto create a model. Two elements will be added to your project: the file “Model1.edmx” describing the conceptual model using XML and the associated “Model1.Designer.cs” which contains the generated code, including the ObjectContext used to access data and entity classes, such as Product , Employee , etc.

Double-click on the edmx file. The Entity Data Model Designer window will appear ., in which you can view, edit the model, and recreate it at any time if you suddenly change the database schema or if you want to change the list of tables and views that you need to include in the model. All automatically generated classes in the Designer.cs file are declared as partial. This allows us to expand them by adding business logic to separate files that will not be changed if you recreate the model from the database.

Now you can use the data model as shown below:

	public Form1()
	{
		InitializeComponent();
		using 
		(
		var ctx = new NORTHWNDEntities())
		{
		  dataGridView1.DataSource = ctx.Products.ToList();
		}
	}

This code creates an ObjectContext object that provides data for the model, builds a list containing all products, and displays the list in a table. You can edit products and save changes to the database by calling the ctx.SaveChanges () method .

If you run the code, you will notice some serious limitations: you cannot sort or filter data, you cannot add or remove items from the list, and of course you cannot configure table columns (Grid) during development using the table editor (Grid).

These limitations are due to the fact that a list is used as the source (just a “snapshot” of the data). IBindingList, which Winforms creates automatically, in this case provides only minimal functionality.

Creating a Grid View (With AutoLookup)


To eliminate these shortcomings, add the EntityDataSource component to the form and in the Properties window , set its ObjectContextType property to “Sample.NORTHWNDEntities”, as shown in the figure below (Note: if you have EF6, use DbContextType instead of ObjectContextType):



The EntityDataSource component uses the value from ObjectContextType to create an object context to generate views for all the elements described in the data model.

Now add a DataGridView control to the form and in the Properties window , set the DataSource property to entityDataSource1, and the DataMember property in Products , as shown below: Columns will be automatically created



in the DataGridView to display the properties of the Product class . In designer mode, you can reorder the columns, set their width, title, alignment, format, etc.

If you start the project now, you will see that the table is filled automatically, and you can perform all the actions you want, including editing, sorting , adding or removing items.

All this is possible because the EntityDataSource component wraps the product list in the EntityBindingList class , which implements the IBindingListView interface and supports sorting, filtering, adding and deleting items.

Saving Changes


After editing the data, you probably want to save the changes to the database. To demonstrate how this can be done, add three buttons on the form and set their Text property to “Save”, “Cancel”, and “Refresh” and attach the following handlers to the Click events of these buttons:

		// save/cancel/refresh changes in the data source
		void _btnSave_Click(object sender, EventArgs e)
		{
			entityDataSource1.SaveChanges();
		}
		void _btnCancel_Click(object sender, EventArgs e)
		{
			entityDataSource1.CancelChanges();
		}
		void _btnRefresh_Click(object sender, EventArgs e)
		{
			entityDataSource1.Refresh();
		}

This code is self-explanatory. The first button saves all changes back to the database. The second cancels the changes by re-extracting the data, and the third saves the changes and selects the data again.

There is one important detail that is often forgotten: when saving changes to the database, exceptions may occur. For example, when saving changes that violate database integrity restrictions. Unfortunately, there are no general recipes for dealing with this type of exception. Their nature depends on the database schema and the application itself.

No matter how you plan to deal with possible exceptions, the first thing to do is catch them. To do this, you need to wrap the call to the SaveChanges method with a try / catch block , or add an event handlerDataError in the EntityDataSource component . This is how our application will be able to handle possible errors when saving data:

		// report any errors
		void entityDataSource1_DataError(object sender, DataErrorEventArgs e)
		{
			MessageBox.Show("Error Detected:\r\n" + e.Exception.Message);
			entityDataSource1.CancelChanges();
			e.Handled = true;
		}

The code issues a warning, discards the changes, and sets the Handled parameter to True to indicate that the error has already been processed and that it is not necessary to throw exceptions.

Using Dictionary Search to Represent Related Entities


To finish with this example, let's look at a regular scenario. The Product class has two properties - Category and Supplier, which are related entities. By default, these properties are not presented in the table, but using the column editor you can add columns for these entities to our table. The image below shows how this can be done:



The problem is that the DataGridView does not know how to display the related entity, the default method is simply ToString , and as a result we get two read-only columns whose cells are filled with “Sample” values .Category ”and“ Sample.Supplier ”.

But to do the real job, you need a column that shows the categories and names of suppliers, ideally with an editor that allows you to change categories and suppliers by selecting from lists. This is usually done by writing code to create and bind custom columns (in a DataGridViewComboBoxColumn if you use a DataGridView control ).

Because this is a common scenario, the EntityDataSource component supports the extended AutoLookup property . This property automatically becomes available in any DataGridView or C1FlexGrid control placed on the form (in C1FlexGridis a popular component of the tabular presentation of data, much faster and more sophisticated than DataGridView ).

Note that although the EntityDataSource component supports C1FlexGrid , the EFWinForms assembly is independent of the C1FlexGrid assembly. This is achieved by using the keyword “dynamic” , which relies mainly on reflection to bind properties at runtime. The same mechanism can be used to extend the EntityDataSource component to support other components of the data table view.

The figure below shows how You can enable the AutoLookup property in the DataGridView :



Как только вы включите свойство AutoLookup, компонент EntityDataSource будет автоматически сканировать столбцы в таблице, чтобы заменить столбцы, привязанные к сущностям на редактируемые столбцы, основанные на “data map”, содержащие список возможных значений связанных сущностей и отображения значений для каждого из них.

На рисунке ниже показан результат установки свойства AutoLookup в True:



Обратите внимание, как столбцы “Category” и “Supplier” показывают имена категорий и названия поставщиков, и что можно поменять поставщика для продукта, просто выбрав нового из списка.

Вы можете удивиться, как EntityDataSource выбирает нужное поле из связанных сущностей для отображения в таблице. Это делается с помощью следующего алгоритма:

  1. If the class implements the ToString method (and not just inherits it), then the ToString implementation is used to represent the entity.
  2. Otherwise, if the class has a property of type string , the name of which has the word “Name” , then this property is used to represent the entity.
  3. Otherwise, if the class has a property of type string , the name of which has the word “Description” , then this property is used to represent the entity.
  4. If none of the above applies, then data binding cannot be performed for this class.

The first rule is the most general and flexible. For example, the Employee class in the Northwind database has the Firstname and Lastname properties . One of them can be used to represent entities in the list, but ideally I would like to use both. To do this, we simply override the ToString method in the Employee class and create a string consisting of both properties:

			
	/// 
	/// Add a field with the employee's full name
	/// 
	public partial class Employee
	{
		public string FullName
		{
		  get { return string.Format("{0} {1}", FirstName, LastName); }
		}
		public override string ToString()
		{
		  return FullName;
		}
	}

Note that using a partial class extends the standard class created using the ADO.NET Entity Framework wizard. If at some point you decide to regenerate the data entity model, our implementation of the ToString method will not be affected.

In the Northwind database, Employee is the only class that requires any special methods to bind data. All other classes have properties such as “CustomerName” or “CategoryDescription” , which are automatically used by the EntityDataSource component and provide the desired effect.

The first example is over.

Also popular now: