5.7 Lazy Loading and Prefetch
Although
the language-to-SQL mapping layer in Linq-to-SQL is nicely designed and
optimized, there might be situations in which too many database
roundtrips will occur. Hold on, though. I’m certainly not here to say
that Linq-to-SQL is broken or unreliable. What I mean is that,
especially for quite complex and spanned queries, the system might end
up performing too many roundtrips. This mostly depends on the data that
is initially fetched and the use you make of it. For example, if you
first select a data set (say, customers) and then loop through the
selected records and access child fields (say, orders), it might happen
that the Linq-to-SQL engine needs to go back to the database for each
step to load the related record. To avoid this overhead, you can decide
to prefetch a given related table. You typically do this in the ContextCreated event, as shown here:
void LinqDataSource1_ContextCreated(object sender,
LinqDataSourceStatusEventArgs e)
{
DataLoadOptions options = new DataLoadOptions();
// Prefetch the Order Details table
options.LoadWith<Order>(Order => Order.Order_Details);
(e.Result as NorthwindDataContext).LoadOptions = options;
}
Prefetching some related tables makes the first query more complex, but it might save a good number of subsequent roundtrips.
Important
Note,
though, that Linq-to-SQL automatically expands only the deepest
association that it finds in the query. This is a precise design choice
that was made to reduce the complexity and amount of data returned by
joined queries. In general, if you are going to have more than one
level of data association, be ready to deal with a number of queries
being executed as you access individual records. To see if some sort of
optimization is necessary, always run the SQL Profiler and keep an eye
on what Linq-to-SQL is really doing for you. |
5.8 Updating Data
When you set to true the value of any EnableUpdate, EnableInsert, or EnableDelete properties, the LinqDataSource control creates the commands for updating, inserting, and deleting data. The following example demonstrates how to use a FormView control bound to a Linq-to-SQL source to edit the address of a customer:
<asp:LinqDataSource ID="LinqDataSource1" runat="server"
EnableUpdate="true"
ContextTypeName="NorthwindDataContext"
TableName="Customers"
Where="CustomerID == @CustomerID">
<WhereParameters>
<asp:ControlParameter Name="CustomerID"
ControlID="DropDownList1"
PropertyName="SelectedValue" />
</WhereParameters>
</asp:LinqDataSource>
<asp:FormView ID="FormView1" runat="server"
DefaultMode="Edit"
DataSourceID="LinqDataSource1"
DataKeyNames="CustomerID">
<EditItemTemplate>
<h2>Change the Address</h2>
<asp:TextBox ID="NewAddress" runat="server"
Text='<%# Bind("Address") %>' />
<asp:Button ID="Button1" runat="server"
Text="Update Address"
CommandName="Update" />
</EditItemTemplate>
</asp:FormView>
For the code to work, there should be a one-to-one correspondence between bindable properties (for example, Address)
and public properties on the data-entity object being displayed. In
other words, the preceding code will work only if the objects bound to
the LinqDataSource—the Customer class—feature a writable property named Address. If you need to programmatically control any values to be updated, you create handlers for events such as Updating, Inserting, or Deleting. Here’s an example of how to validate a value being assigned:
void LinqDataSource1_Updating(object sender,
LinqDataSourceUpdateEventArgs e)
{
if (!Validate((e.OriginalObject as Customer).Address))
{
// Invalid data, cancel the operation
e.Cancel = true;
}
}
bool Validate(object member)
{
...
}
The OriginalObject property of the LinqDataSourceUpdateEventArgs object contains the data-entity object being updated. You can replace it using the NewObject property, or you can cancel the operation via the Cancel property.
Note
A LinqDataSource that supports update commands needs to have the Select property left blank. Setting the Select property, in fact, causes the LinqDataSource control to return an instance of a dynamically created class rather than an instance of the data-entity class. |
5.9 LinqDataSource vs ObjectDataSource vs SqlDataSource
The difference between SqlDataSource and ObjectDataSource
should be fairly clear. The first leads you to add SQL code directly in
the markup of ASP.NET pages or, at best, in the code-behind class.
Although the resulting page might still work just great, it’s obviously
not a best practice to recommend. It might be an effective shortcut for
simple sites or temporary pages; however, for the sake of layer
separation, you should not use SqlDataSource in any ASP.NET applications with a domain model of any significant complexity.
ObjectDataSource
is exactly the opposite. It pushes the use of objects and makes the
binding between presentation data-bound controls and data sources
happen through the mediation of made-to-measure objects. In most cases,
the objects bound to ObjectDataSource won’t be just the top-layer objects of your business tier. But ObjectDataSource
allows you to preserve—and, to a good extent, it mandates you to have—a
well-designed business tier. It might just require that you add a thin
façade on top of it to fulfill the control’s contract with bound
objects.
What about LinqDataSource,
instead? To me, it falls somewhere in between the other two. To enable Linq-to-SQL you must create a data
model—and it’s
by far easier if you do this through the O/R designer embedded in
Visual Studio 2008. This data model is rooted in a data context class
and initially contains only data container classes with data but no
behavior. The idea behind LinqDataSource
is that you use the auto-generated object model to manage data, and you
provide any required behavior through direct injections of Linq-to-SQL
commands in the various properties of the control. In the end, the
pattern behind LinqDataSource and SqlDataSource
is nearly the same; the only difference is the language you
use—Linq-to-SQL in one case, and raw SQL in the other. Another
important difference that must be noted between SqlDataSource and LinqDataSource is that SqlDataSource can accommodate any relational data source, whereas LinqDataSource is limited to SQL Server.
The LinqDataSource
control is not designed to accept methods to call to execute queries or
updates. So it requires an underlying object model, but it uses this
object model only to back up a query language of a higher level than
SQL. The purposes of LinqDataSource and ObjectDataSource are clearly different. ObjectDataSource enables you to take advantage of RAD data binding on top of your existing middle tier. LinqDataSource
and related tools give you, instead, a quick way to build an extremely
thin and, to some extent, anemic object model, Additionally, it can be
quite difficult to deploy on a physically different tier without
significant refactoring.
The bottom line that I see is that you should consider ObjectDataSource for enterprise-class applications and wherever the complexity of the domain model is significant. LinqDataSource is a much smarter replacement for SqlDataSource, with the current technological limitation of being restricted to use with SQL Server.