2. When the app executes, another compiler (known as the just-in-time compiler
22.6 Dynamically Binding Query Results
enables you to order results by an additional column. This is applied to theAuthorobjects that have already been ordered by last name.
Finally, line 30 calls theLoadextension method(defined in classDBExtensionsfrom the namespaceSystem.Data.Entity). This method executes the LINQ to Entities query and loads the results into memory. This data is tracked by theBookEntities DbContext
in local memory so that any changes made to the data can eventually be saved into the database. Lines 27–30 are equivalent to using the following statement:
Line 33 sets theauthorBindingSource’sDataSourcepropertyto theLocalproperty of thedbcontext.Authorsobject. In this case, theLocalproperty is anObservableCol- lection<Author>that represents the query results that were loaded into memory by lines 27–30. When aBindingSource’sDataSourceproperty is assigned anObservableCollec- tion<T>(namespaceSystem.Collections.ObjectModel), the GUI that’s bound to the
BindingSourceis notified of any changes to the data so the GUI can be updated accord- ingly. In addition, changes made by the user to the data in the GUI will be tracked so the
DbContextcan eventually save those changes to the database.
authorBindingNavigatorSaveItem_ClickEvent Handler: Saving Modifications to the Database
If the user modifies the data in theDataGridView, we’d also like to save the modifications in the database. By default, theBindingNavigator’sSave DataButton( ) is disabled.
To enable it, right click thisButton’s icon in theBindingNavigatorand selectEnabled. Then, double click the icon to create itsClickevent handler (lines 38–54).
Saving the data entered in theDataGridViewback to the database is a three-step process.
First, all controls on the form are validated (line 41) by calling theDisplayTableForm’s inheritedValidatemethod—if any control has an event handler for theValidatingevent, it executes. You typically handle this event to determine whether a control’s contents are valid. Next, line 42 callsEndEditon theauthorBindingSource, which forces it to save any pending changes into theBooksEntities model in memory. Finally, line 47 calls Save-
Changeson theBooksEntitiesobject (dbcontext) to store any changes into the database.
We placed this call in atrystatement, because theAuthorstable does not allow empty values for the first name and last name—these rules were configured when we originally created the database. WhenSaveChangesis called, any changes stored into theAuthorstable must sat- isfy the table’s rules. If any of the changes do not, aDBEntityValidationExceptionoccurs.
22.6 Dynamically Binding Query Results
Now that you’ve seen how to display an entire database table in aDataGridView, we show how to perform several different queries and display the results in aDataGridView. This app only reads data from the entity data model, so we disabled the buttons in theBind-
ingNavigatorthat enable the user to add and delete records. Later, we’ll explain why we do not support modifying the database in this example.
TheDisplay Query Resultsapp (Fig. 22.21) allows the user to select a query from the
ComboBoxat the bottom of the window, then displays the results of the query.
(from author in dbcontext.Authors
orderby author.LastName, author.FirstName select author).Load();
22.6.1 Creating theDisplay Query ResultsGUI
Perform the following steps to build theDisplay Query Resultsapp’s GUI.
Step 1: Creating the Project
Perform the steps in Section 22.5.2 to create a newWindows Forms Applicationproject namedDisplayQueryResultin the same solution as theDisplayTableapp. Rename the
Form1.cssource file toTitleQueries.cs. Set theForm’sTextproperty toDisplay Query Results. Be sure to set theDisplayQueryResultproject as the startup project.
Step 2: Creating aDataGridViewto Display theTitlesTable
FollowSteps 1and2in Section 22.5.3 to create the data source and theDataGridView. For this example, select theTitleclass (rather thanAuthor) as the data source, and drag theTitlenode from theData Sourceswindow onto the form. Remove theAuthorscolumn from theDataGridViewas it will not be used in this example.
Fig. 22.21 | Sample execution of theDisplay Query Resultsapp.
a) Results of the
“All titles” query, which shows the contents of the Titlestable ordered by the book titles
b) Results of the
“Titles with 2014 copyright” query
c) Results of the
“Titles ending with ’How to Program’” query
22.6 Dynamically Binding Query Results 871
Step 3: Adding aComboBoxto theForm
InDesignview, add aComboBoxnamedqueriesComboBoxbelow theDataGridViewon the
Form. Users will select which query to execute from this control. Set theComboBox’sDock property toBottomand theDataGridView’sDockproperty toFill.
Next, you’ll add the names of the queries to theComboBox. Open theComboBox’sString
Collection Editorby right clicking theComboBoxand selectingEdit Items…. You can also access theString Collection Editorfrom theComboBox’ssmart tag menu. Asmart tag menu provides you with quick access to common properties you might set for a control (such as theMultilineproperty of aTextBox), so you can set these properties directly inDesign view, rather than in thePropertieswindow. You can open a control’ssmart tag menuby clicking the small arrowhead ( ) that appears in the control’s upper-right corner inDesign view when the control is selected. In theString Collection Editor, add the following three items toqueriesComboBox—one for each of the queries we’ll create:
1. All titles
2. Titles with 2014 copyright
3. Titles ending with "How to Program"
22.6.2 Coding theDisplay Query ResultsApp Next you’ll create the code for this app (Fig. 22.22).
Customizing theForm’sLoadEvent Handler
Create theTitleQueries_Loadevent handler (lines 22–29) by double clicking the title bar inDesignview. When theFormloads, it should display the complete list of books from the
Titlestable, sorted by title. Line 24 calls theLoadextension method on theBookEnti-
ties DbContext’sTitlesproperty to load theTitlestable’s contents into memory. Rath- er than defining the same LINQ query as in lines 40–41, we can programmatically cause thequeriesComboBox_SelectedIndexChangedevent handler to execute simply by setting thequeriesComboBox’sSelectedIndexto0(line 28).
1 // Fig. 22.22: TitleQueries.cs
2 // Displaying the result of a user-selected query in a DataGridView.
3 using System;
4 using System.Data.Entity;
5 using System.Linq;
6 using System.Windows.Forms;
7
8 namespace DisplayQueryResult 9 {
10 public partial class TitleQueries : Form
11 {
12 public TitleQueries()
13 {
14 InitializeComponent();
15 } // end constructor 16
Fig. 22.22 | Displaying the result of a user-selected query in aDataGridView. (Part 1 of 2.)
17 // Entity Framework DbContext
18 private BooksExamples.BooksEntities dbcontext = 19 new BooksExamples.BooksEntities();
20
21 // load data from database into DataGridView
22 private void TitleQueries_Load( object sender, EventArgs e )
23 {
24 25
26 // set the ComboBox to show the default query that 27 // selects all books from the Titles table
28
29 } // end method TitleQueries_Load 30
31 // loads data into titleBindingSource based on user-selected query 32 private void queriesComboBox_SelectedIndexChanged(
33 object sender, EventArgs e )
34 {
35 // set the data displayed according to what is selected 36 switch ( queriesComboBox.SelectedIndex )
37 {
38 case 0: // all titles
39 // use LINQ to order the books by title 40
41
42 break;
43 case 1: // titles with 2014 copyright 44 // use LINQ to get titles with 2014 45 // copyright and sort them by title 46
47 48 49
50 break;
51 case 2: // titles ending with "How to Program"
52 // use LINQ to get titles ending with 53 // "How to Program" and sort them by title 54
55 56 57 58
59 break;
60 } // end switch
61 62
63 } // end method queriesComboBox_SelectedIndexChanged 64 } // end class TitleQueries
65 } // end namespace DisplayQueryResult
Fig. 22.22 | Displaying the result of a user-selected query in aDataGridView. (Part 2 of 2.)
dbcontext.Titles.Load(); // load Titles table into memory
queriesComboBox.SelectedIndex = 0;
titleBindingSource.DataSource =
dbcontext.Titles.Local.OrderBy( book => book.Title1 );
titleBindingSource.DataSource = dbcontext.Titles.Local
.Where( book => book.Copyright == "2014" ) .OrderBy( book => book.Title1 );
titleBindingSource.DataSource = dbcontext.Titles.Local
.Where( book =>
book.Title1.EndsWith( "How to Program" ) ) .OrderBy( book => book.Title1 );
titleBindingSource.MoveFirst(); // move to first entry
22.6 Dynamically Binding Query Results 873
queriesComboBox_SelectedIndexChangedEvent Handler
Next you must write code that executes the appropriate query each time the user chooses a different item fromqueriesComboBox. Double clickqueriesComboBoxinDesignview to generate aqueriesComboBox_SelectedIndexChangedevent handler (lines 32–63) in the
TitleQueries.csfile. In the event handler, add aswitchstatement (lines 36–60). Each
casein theswitchwill change thetitleBindingSource’sDataSourceproperty to the re- sults of a query that returns the correct set of data. The data bindings created by the IDE automaticallyupdate thetitleDataGridVieweach time we change itsDataSource. The
MoveFirstmethodof theBindingSource(line 62) moves to the first row of the result each time a query executes. The results of the queries in lines 40–41, 46–49 and 54–58 are shown in Fig. 22.21(a), (b) and (c), respectively. Because we do not modify the data in this app, each of the queries is performed on the in-memory representation of theTitlestable, which is accessible throughdbcontext.Titles.Local.
Ordering the Books By Title
Lines 40–41 invoke theOrderByextension method ondbcontext.Titles.Localto order theTitleobjects by theirTitle1property values. As we mentioned previously, the IDE renamed theTitlecolumn of the database’sTitlestable asTitle1in the generatedTi-
tleentity data model class to avoid a naming conflict with the class’s name. Recall that
Localreturns anObservableCollection<T>containing the row objects of the specified table—in this case,Localreturns anObservableCollection<Title>. When you invoke
OrderByon anObservableCollection<T>, the method returns anIEnumerable<T>. We assign that object to thetitleBindingSource’sDataSourceproperty. When theData-
Sourceproperty changes, theDataGridViewiterates through the contents of theIEnumer-
able<T>and displays the data.
Selecting Books with 2014 Copyright
Lines 46–49 filter the titles displayed by using theWhereextension methodwith the lamb- da expression
as an argument. This lambda expression takes oneTitleobject (namedbook) as its pa- rameter and uses it to check whether the givenTitle’sCopyrightproperty (astringin the database) is equal to2014. A lambda expression that’s used with theWhereextension method must return aboolvalue. OnlyTitleobjects for which this lambda expression returnstruewill be selected. We useOrderByto order the results by theTitle1property so the books are displayed in ascending order by title. The type of the lambda’sbookpa- rameter isinferredfromdbcontext.Titles.Local, which containsTitleobjects. As soon as thetitleBindingSource’sDataSourceproperty changes, theDataGridViewis updated with the query results.
Selecting Books with Titles That End in “How to Program”
Lines 54–58 filter the titles displayed by using theWhereextension method with the lamb- da expression
book => book.Copyright == "2014"
book => book.Title1.EndsWith( "How to Program" )
as an argument. This lambda expression takes oneTitleobject (namedbook) as its pa- rameter and uses it to check whether the givenTitle’sTitle1property value ends with
"How to Program". The expressionbooks.Title1returns thestringstored in that prop- erty, then we use the string class’sEndsWithmethod to perform the test. We order the re- sults by theTitle1property so the books are displayed in ascending order by title.