5.4 Events of the LinqDataSource Control
The
control supports associated sets of related of events for each key
storage operation. In addition, it provides three events for the
creation and disposal of the data context object. See Table 11 for details.
Table 11. Events of the LinqDataSource Control
Event | Description |
---|
ContextCreating, ContextCreated, ContextDisposing | The ContextCreating event fires before the data context object is created, whereas the ContextCreated event is raised immediately after. The ContextDisposing event is raised before the data context object is disposed of. |
Deleting, Deleted | The Deleting event is raised before the Delete operation is executed; the Deleted event is raised after the Delete operation has been successfully completed. |
Inserting, Inserted | The Inserting event is raised before the Insert operation is executed; the Inserted event is raised after the Insert operation has been successfully completed. |
Selecting, Selected | The Selecting event is raised before the Select operation is executed; the Selected event is raised after the Select operation has been successfully completed. |
Updating, Updated | The Updating event is raised before the Update operation is executed; the Updated event is raised after the Update operation has been successfully completed. |
It should be noted that the LinqDataSource control doesn’t create its own internal copy of the data context object if a Select operation is executed in the Selecting
event. In this case, the control reasonably assumes that you’re using
your own data context object (for example, a global member of the
code-behind class) and hence doesn’t proceed with the creation of an
object that will likely be a duplicate. Put another way, if you want to
take control of the query yourself without relying on the Select property, you must also create the data context manually. A good question is, when would you want to do this? Perhaps when the Select property of the LinqDataSource control might not be appropriate for a query? Just read on.
5.5 Selecting, Sorting, and Filtering Data
The Select property of the LinqDataSource
control allows you to decide which of the properties in the data-entity
record will be retrieved. If you leave the property to its default
value—the empty string—all properties in the data-entity record will be
retrieved. This means that, if the data context is a database table, the Linq-to-SQL runtime will be running for you a kind of SELECT * FROM table query. To restrict the set of properties retrieved, you can use the following syntax:
<asp:LinqDataSource ID="LinqDataSource1" runat="server"
ContextTypeName="NorthwindDataContext"
TableName="Customers"
Select="new (CustomerID, CompanyName, Country)" />
The LinqDataSource control returns a List<T>
collection that contains instances of a dynamically created class that
just includes the properties whose names have been passed as an
argument to the new function.
What if you need to order data by one or more properties? You just use the OrderBy property, as shown here:
<asp:LinqDataSource ID="LinqDataSource2" runat="server"
ContextTypeName="NorthwindDataContext"
TableName="Customers"
Select="new (CustomerID, CompanyName)"
OrderBy="CompanyName DESC, CustomerID">
...
</asp:LinqDataSource>
The example selects CustomerID and CompanyName properties out of the bound list of customers and then sorts them by company name (in descending order, as indicated) and ID.
How about filtering data by applying some criteria? That’s exactly the purpose of the Where
clause. You can specify a static clause where any value to use is known
beforehand, or you can put placeholders in the clause to be filled
dynamically using parameters. Here’s an example of a static Where clause:
<asp:LinqDataSource ID="LinqDataSource2" runat="server"
ContextTypeName="NorthwindDataContext"
TableName="Customers"
Select="new (CustomerID, CompanyName)"
OrderBy="customerid, companyname desc"
Where='Country == "Germany"'>
Note that if you intend to use a literal string in the Where
clause, you have to wrap the string with double quotes and use single
quotes for the query string. The clause can also be parametric. In this
case, the value assigned to the Where property references a parameter name whose details and bindings are detailed in the WhereParameters section, as shown next:
<asp:LinqDataSource ID="LinqDataSource1" runat="server"
ContextTypeName="NorthwindDataContext"
TableName="Customers"
Select="new (CustomerID, CompanyName)"
OrderBy="customerid, companyname desc"
Where="Country == @Country">
<WhereParameters>
<asp:ControlParameter Name="Country"
Type="String"
ControlID="CountryList"
PropertyName="SelectedValue" />
</WhereParameters>
</asp:LinqDataSource>
<asp:DropDownList ID="CustomerList" runat="server"
AutoPostBack="true"
DataSourceID="LinqDataSource1"
DataTextField="CompanyName"
DataValueField="CompanyName" />
The @Country parameter is bound to the SelectedValue property of a list control in the page named CountryList.
When you have several parameters to take into account in the Where clause, there’s a shortcut you can take—at least in some relatively simple cases. You leave the Where property blank and set the AutoGenerateWhereClause property to true, and the LinqDataSource control automatically generates the clause for you. All that you have to do is list required parameters in the WhereParameters section of the control’s markup. This is beneficial because you don’t have to specify all criteria in the Where property and you can have the control prepare an ad hoc Where clause that includes each listed parameter.
At the same time, you can’t just expect the LinqDataSource control to read your mind and auto-generate all possible types of filters. The effects of the AutoGenerateWhereClause
property are limited to testing parameters for equality and to do so in
the context of an AND operation. In addition, parameter names must
exactly match property names on the target data-entity objects. For any
other needs, you just write the Where clause manually and leave the AutoGenerateWhereClause property set to its default value of false. Note that you’ll receive an exception if the Where property is non-empty and AutoGenerateWhereClause is true.
5.6 When the Select Property Is Not Enough
The
LINQ syntax is fairly rich, as we’ll see in the next chapter. Not all
the possibilities it offers, though, can be expressed as a plain string
and assigned to the Select property of the LinqDataSource
control. In general, this happens as the complexity of the query grows
beyond a certain threshold. Popular examples are when you need to use
JOINs or, as we’ll see in a moment, DISTINCT queries. The LINQ syntax
fully supports JOINs and DISTINCT queries, but the parser of the string
assigned to the Select property just doesn’t recognize these operators.
The workaround is quite simple and straightforward: you leave the Select property blank and add an event handler for the data source’s Selecting event:
<asp:LinqDataSource ID="LinqDataSource1" runat="server"
ContextTypeName="NorthwindDataContext"
TableName="Customers"
OnSelecting="LinqDataSource1_Selecting" />
This can be done either declaratively as just shown or programmatically in the Init event of the page:
protected void Page_Init(object sender, EventArgs e)
{
LinqDataSource1.Selecting +=
new EventHandler<LinqDataSourceSelectEventArgs>(
LinqDataSource1_Selecting);
}
In the body of the handler,
you can use the full LINQ syntax to run the query without being limited
by the string format and the capabilities of the embedded data source
parser. Here’s how to distinctively select all countries in which you
have customers:
private NorthwindDataContext db;
protected void Page_Init(object sender, EventArgs e)
{
db = new NorthwindDataContext();
}
protected void LinqDataSource1_Selecting(object sender,
LinqDataSourceSelectEventArgs e)
{
var countries = (from c in db.Customers
select new { c.Country }).Distinct();
e.Result = countries;
}
The Result property on the LinqDataSourceSelectEventArgs class holds the list of objects that the data source will share with its bound controls.
It is key to note that, when defined, the Selecting/Selected events fire before the ContextCreating/ContextCreated pair. However, if in the Selecting event you set the Result
property of the event data structure, no data context object is ever
created and no related events are fired. What does this mean to you? If
you need to use your own LINQ query, you should also worry about
creating the data context object manually. A good place to create the
data context manually is in the Page_Init method. Figure 2 shows a sample page in action that gets the list of countries and then lets users see how that list is related to customers.