changing the Application type field on the Application tab.
Because this section involves working with ASP.NET applications, it is
recommended that you run Visual Studio 2010 in Administrator mode if you
are running Windows Vista. This will allow the debugger to be attached to the
appropriate process.
In the Web Service project, you will add a reference to the class library project. You also need
to modify the Service class file so it has two methods, in place of the default HelloWorld web
method:
Vb
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.ComponentModel
Imports CustomerObject
< System.Web.Services.WebService(Namespace:="http://tempuri.org/") > _
< System.Web.Services.WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1) > _
< ToolboxItem(False) > _
582 .
chaPter 27 dATASeTS And dATAbinding
Public Class CustomerService
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function RetrieveCustomers(ByVal Title As String) _
As AdventureWorksLTDataSet.CustomerDataTable
Dim ta As New AdventureWorksLTDataSetTableAdapters.CustomerTableAdapter
Return ta.GetData(Title)
End Function
<WebMethod()> _
Public Sub SaveCustomers(ByVal changes As Data.DataSet)
Dim changesTable As Data.DataTable = changes.Tables(0)
Dim ta As New AdventureWorksLTDataSetTableAdapters.CustomerTableAdapter
ta.Update(changesTable.Select)
End Sub
End
Code snippet CustomerService.asmx.vb
c#
namespace CustomerService{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class CustomerService : System.Web.Services.WebService{
[WebMethod]
public AdventureWorksLTDataSet.CustomerDataTable RetrieveCustomers
(string title){
var ta = new CustomerObject.AdventureWorksLTDataSetTableAdapters.
CustomerTableAdapter();
return ta.GetData(title);
}
[WebMethod()]
public void SaveCustomers(DataSet changes){
var changesTable = changes.Tables[0] as DataTable;
var ta = new CustomerObject.AdventureWorksLTDataSetTableAdapters.
CustomerTableAdapter();
ta.Update(changesTable.Select());
}
}
}
Code snippet CustomerService.asmx.cs
The first web method, as the name suggests, retrieves the list of customers based on the Title
that is passed in. In this method, you create a new instance of the strongly typed TableAdapter
and return the DataTable retrieved by the GetData method. The second web method is used to
save changes to a DataTable, again using the strongly typed TableAdapter. As you will notice, the
DataSet that is passed in as a parameter to this method is not strongly typed. Unfortunately,
Working with Data sources .
583
the generated strongly typed DataSet doesn’t provide a strongly typed GetChanges method,
which will be used later to generate a DataSet containing only data that has changed. This new
DataSet is passed into the SaveCustomers method so that only changed data needs to be sent to the
web service.
the web service data source
These changes to the web service complete the server side of the process, but your application
still doesn’t have access to this data. To access the data from your application, you need to add a
data source to the application. Again, use the Add New Data Source Wizard, but this time select
Service from the Data Source Type screen. To add a Web Service Data Source you then need to
click Advanced, followed by Add Web Reference. Clicking the “Web services in this solution” link
displays a list of web services available in your solution. The web service that you have just been
working on should appear in this list. When you click the hyperlink for that web service, the Add
Reference button is enabled, as shown in Figure 27-20.
fiGure 27-20
If an error is displayed when clicking the hyperlink you may need to build
and run the ASP.NET Web Service Application project. This starts the service
running so that the schema information can be correctly extracted by the Add
Web Reference dialog.
584 .
chaPter 27 dATASeTS And dATAbinding
Clicking the Add Reference button adds an AdventureWorksDataSet to the Data Sources window
under the CustomerService node. Expanding this node, you will see that the data source is very
similar to the data source you had in the class library.
browsing data
To actually view the data being returned via the web service, you need to add some controls to your
form. Open the form so the designer appears in the main window. In the Data Sources window, click
the Customer node and select Details from the drop-down. This indicates that when you drag the
Customer node onto the form, Visual Studio 2010 will create controls to display the details of
the Customer table (for example, the row contents), instead of the default DataGridView. Next,
select the attributes you want to display by clicking them and selecting the control type to use. When
you drag the Customer node onto the form, you should end up with the layout similar to Figure 27-21.
fiGure 27-21
In addition to adding controls for the information to be displayed and edited, a Navigator
control has also been added to the top of the form, and an AdventureWorksDataSet and a
CustomerBindingSource have been added to the non-visual area of the form.
The final stage is to wire up the Load event of the form to retrieve data from the web service, and to
add the Save button on the navigator to save changes. Right-click the save icon and select Enabled
to enable the Save button on the navigator control, and then double-click the save icon to generate
the stub event handler. Add the following code to load data and save changes via the web service
you created earlier:
Vb
Public Class CustomerForm
Private Sub CustomerForm_Load(ByVal sender As System.Object,
ByVal e As System.EventArgs) Handles Me.Load
Working with Data sources .
585
Me.CustomerBindingSource.DataSource = _
My.WebServices.CustomerService.RetrieveCustomers("%mr%")
End Sub
Private Sub CustomerBindingNavigatorSaveItem_Click _
(ByVal sender As System.Object,
ByVal e As System.EventArgs)
Handles CustomerBindingNavigatorSaveItem.Click
Me.CustomerBindingSource.EndEdit()
Dim ds = CType(Me.CustomerBindingSource.DataSource, _
CustomerService.AdventureWorksLTDataSet.CustomerDataTable)
Dim changesTable As DataTable = ds.GetChanges()
Dim changes As New DataSet
changes.Tables.Add(changesTable)
My.WebServices.CustomerService.SaveCustomers(changes)
End Sub
End Class
Code snippet CustomersForm.vb
c#
private void CustomersForm_Load(object sender, EventArgs e){
var service = new CustomerService.CustomerService();
this.CustomerBindingSource.DataSource = service.RetrieveCustomers("%mr%"); ;
}
private void CustomerBindingNavigatorSaveItem_Click(object sender, EventArgs e){
this.CustomerBindingSource.EndEdit();
var ds = this.CustomerBindingSource.DataSource
as CustomerService.AdventureWorksLTDataSet.CustomerDataTable;
var changesTable = ds.GetChanges();
var changes = new DataSet();
changes.Tables.Add(changesTable);
var service = new CustomerService.CustomerService();
service.SaveCustomers(changes);
}
Code snippet CustomersForm.cs
To retrieve the list of customers from the web service, all you need to do is call the appropriate
web method — in this case, RetrieveCustomers. Pass in a parameter of %mr%, which indicates
that only customers with a Title containing the letters “mr” should be returned. The Save method
is slightly more complex, because you have to end the current edit (to make sure all changes are
saved), retrieve the DataTable, and then extract the changes as a new DataTable. Although it would
be simpler to pass a DataTable to the SaveCustomers web service, only DataSets can be specified as
parameters or return values to a web service. As such, you can create a new DataSet and add the
changed DataTable to the list of tables. The new DataSet is then passed into the SaveCustomers
method. As mentioned previously, the GetChanges method returns a raw DataTable, which is
unfortunate because it limits the strongly typed data scenario.
586 .
chaPter 27 dATASeTS And dATAbinding
This completes the chapter’s coverage of the strongly typed DataSet scenario, and provides you with
a two-tiered solution for accessing and editing data from a database via a web service interface.
suMMary
This chapter provided an introduction to working with strongly typed DataSets. Support within
Visual Studio 2010 for creating and working with strongly typed DataSets simplifies the rapid
building of applications. This is clearly the first step in the process of bridging the gap between the
object-oriented programming world and the relational world in which the data is stored.
Hopefully this chapter has given you an appreciation for how the BindingSource,
BindingNavigator, and other data controls work together to give you the ability to rapidly build
data applications. Because the new controls support working with either DataSets or your own
custom objects, they can significantly reduce the amount of time it takes you to write an application.
28
language integrated
Queries (linQ)
what’s in this chaPter?
.
Querying objects with LINQ
.
Writing and querying XML with XLINQ
.
Querying and updating data with LINQ to SQL
Language Integrated Queries (LINQ) was designed to provide a common programming model
for querying data. In this chapter you see how you can take some very verbose, imperative
code and reduce it to a few declarative lines. This enables you to make your code more
descriptive rather than prescriptive; that is, describing what you want to occur, rather than
detailing how it should be done.
Although LINQ provides an easy way to filter, sort, and project from an in-memory object
graph, it is more common for the data source to be either a database or a file type, such as
XML. In this chapter you are introduced to LINQ to XML, which makes working with XML
data dramatically simpler than with traditional methods such as using the document object
model, XSLT, or XPath. You also learn how to use LINQ to SQL to work with traditional
databases, such as SQL Server, allowing you to write LINQ statements that will query the
database, pull back the appropriate data, and populate .NET objects that you can work with.
In Chapter 29 you are introduced to the ADO.NET Entity Framework for which there is also
a LINQ provider. This means that you can combine the power of declarative queries with the
fidelity of the Entity Framework to manage your data object life cycle.
588 .
chaPter 28 lAnguAge inTegrATed QuerieS (linQ)
linq ProViders
One of the key tenets of LINQ is the ability to abstract away the query syntax from the underlying
data store. LINQ sits behind the various .NET languages such as C# and VB and combines various
language features, such as extension methods, type inferences, anonymous types, and Lambda
expressions, to provide a uniform syntax for querying data.
A number of LINQ-enabled data sources come with Visual Studio 2010 and the .NET Framework
4.0: Objects, DataSets, SQL, Entities, and XML; each with its own LINQ provider that’s capable
of querying the corresponding data source. LINQ is not limited to just these data sources, and
providers are available for querying all sorts of other data sources. For example, there is a LINQ
provider for querying SharePoint. In fact, the documentation that ships with Visual Studio 2010
includes a walkthrough on creating your own LINQ provider.
In this chapter you see some of the standard LINQ operations as they apply to standard .NET
objects. You’ll then see how these same queries can be applied to both XML and SQL data sources.
As you will see, the syntax for querying the data remains constant, with only the underlying data
source changing.
old-school queries
Instead of walking through exactly what LINQ is, this
section starts with an example that demonstrates some of
the savings that these queries offer. The scenario is one in
which a researcher is investigating whether or not there is
a correlation between the length of a customer’s name and
the customer’s average order size by analyzing a collection
of customer objects. The relationship between a customer
and the orders is a simple one-to-many relationship as
shown in Figure 28-1.
In the particular query you are examining, the researchers are looking for the average Milk order
for customers with a first name greater than or equal to five characters, ordered by the first name:
c#
private void OldStyleQuery(){
Customer[] customers = BuildCustomers();
List<SearchResult> results = new List<SearchResult>();
SearchForProduct matcher = new SearchForProduct() { Product = "Milk" };
foreach (Customer c in customers){
if (c.FirstName.Length >= 5){
Order[] orders = Array.FindAll(c.Orders, matcher.ProductMatch);
if (orders.Length > 0){
SearchResult cr = new SearchResult();
cr.Customer = c.FirstName + " " + c.LastName;
foreach (Order o in orders){
cr.Quantity += o.Quantity;
fiGure 28-1
old-school Queries .
589
cr.Count++;
}
results.Add(cr);
}
}
}
results.Sort(CompareSearchResults);
ObjectDumper.Write(results, Writer);
}
Code snippet MainForm.cs
Vb
Private Sub OldStyleQuery()
Dim customers As Customer() = BuildCustomers()
Dim results As New List(Of SearchResult)
Dim matcher As New SearchForProduct() With {.Product = "Milk"}
For Each c As Customer In customers
If c.FirstName.Length >= 5 Then
Dim orders As Order() = Array.FindAll(c.Orders, _
AddressOf matcher.ProductMatch)