Nielsen c33.tex V4 - 07/21/2009 2:06pm Page 772
Part V Data Connectivity
When tracking incremental changes, Sync Services uses net change tracking for peer-to-peer synchroniza-
tion. This means that the latest version is synchronized and all changes to a given row are applied in
order at each peer.
Peer-to-peer synchronization can be compared in many ways to a combination of offline data-set
technology and replication. Sync Services provides a programming model like that of offline datasets
but with a feature set like that of replication. As stated in the previous section, replication is a database
administrator function, but via Sync Services, developers can have the same synchronization support
through the many available synchronization classes, enabling developers to synchronize with other data
store types.
Like the offline scenario, the choice between using peer-to-peer transaction replication versus using Sync
Services for peer-to-peer synchronization, it comes down to need. Peer-to-peer transactional replication
is more of an administrator responsibility, but it does provide a highly available and scalable system and
requires almost no coding.
Sync Services, however, is for developers. It allows for synchronization of different transports or services,
and provides synchronization capabilities with data sources other than a database. Sync Services is the
mechanism for moving beyond just replicating schema and data from one database to another.
From an architecture standpoint, the peer-to-peer scenario has each peer containing its own application
code and Sync Services code to initiate and facilitate synchronization. This functionality is provided via
the Sync Services peer-to-peer API. During a typical synchronization, a pair of peers is synchronized.
For example, given a scenario in which three peers need to be synchronized, synchronization could take
place like this: Peer1 and peer2 are synchronized, then peer1 a nd peer3, and then finally peer2 and
peer3. In each synchronization, one peer acts as the local peer, and one peer acts as the remote peer.
However, this scenario might seem a bit redundant, as it is not necessary for each and every peer to be
synchronized. In the earlier example, peer1 and peer2 could be synchronized, then peer2 and peer3.
Peer1 and peer3 would be synchronized via peer2.
As for the components used in a peer-to-peer synchronization, Figures 33-12 and 33-13 show which
components are used in a two-tier and n-tier architecture, respectively.
FIGURE 33-12
A two-tier architecture in peer-to-peer synchronization
DbSyncProvider DbSyncProvider
DbSyncAdapterDbSyncAdapter
Local Computer
Remote
Computer
SyncOrchestrator
772
www.getcoolebook.com
Nielsen c33.tex V4 - 07/21/2009 2:06pm Page 773
Sync Framework 33
FIGURE 33-13
An
n
-tier architecture in peer-to-peer synchronization with a proxy
DbSyncProvider DbSyncProvider
DbSyncAdapterDbSyncAdapter
Local Computer Remote Computer
SyncOrchestrator Proxy Service
In a two-tier architecture, all the services exist on the local peer, as shown in Figure 33-12.
In an n-tier architecture, a proxy and service are also introduced, and some of the services reside on the
remote computer, as shown in Figure 33-13.
The n-tier architecture requires a service and a transport system which provides communication between
a proxy on the local computer and a service on the remote computer. The difference between peer-to-
peer and client-server synchronization is that the proxy is not part of the Sync Framework API. This
proxy must be written by the developer and must be derived from the
KnowledgeSyncProvider
class.
The
SyncOrchestrator in a peer-to-peer synchronization is responsible for the following:
■ Storing table information on the peer
■ Applying incremental changes to the peer database
■ Detecting conflict changes
■ Assisting applications in obtaining changes that occurred on the peer since the last synchro-
nization
By now it should be fairly clear that using Sync Services for ADO.NET 2.0 provides a very powerful yet
flexible synchronization mechanism. There are many options to consider. What makes Sync Services
great is simply how comprehensive it is for building offline and collaboration applications.
Summary
This chapter provided a look at the Microsoft Sync Framework and Sync Services for ADO.NET 2.0.
The chapter began by providing a brief and simple example demonstrating how easy it is to use this
great technology. It then jumped feet first into first an overview, and then the architecture, of the Sync
Framework.
That discussion was followed by an overview and architectural discussion about one of the Sync Frame-
work components, Sync Services, and why it should be considered when architecting offline solutions.
773
www.getcoolebook.com
Nielsen c33.tex V4 - 07/21/2009 2:06pm Page 774
www.getcoolebook.com
Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 775
LINQ
IN THIS CHAPTER
Overview and history of LINQ
Understanding LINQ
LINQ standard query operators
Syntax options
LINQ to SQL
LINQ to XML
LINQ to Dataset
LINQ to Entities
L
et’s get right to the point. I often see code like this:
string ConnectionString = @"Data Source=(local);
Initial Catalog = Adventureworks;UID=username;PWD=password";
using (SqlConnection conn = new
SqlConnection(ConnectionString))
{
conn.Open();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT LastName, FirstName,
MidleName FROM Person.Contact";
using (SqlDataReader rdr = cmd.ExecuteReader())
{
// do something
}
}
Maybe you have also seen code like this or have even done it yourself, but at
some point in your career one of the two have happened. Given the preceding
code, ask yourself two questions. First, will it compile when run? Second, if so,
will it run successfully? Set aside for a minute the fact that there is no
WHERE
clause and ignore the lack of encapsulation. That is not the intent of this example.
What is important are the two questions.
First, yes, it will compile. Second, when it runs it will fail. It will fail because the
column ‘‘MidleName’’ should actually be ‘‘MiddleName.’’ The problem is that you
need to run the program in order to catch this. Even worse, if your program does
error, where do you look? (That is, the developer was unaware that the column
name was misspelled.) Debugging an error such as this is time consuming and
unproductive. Equally, the example is ‘‘unfriendly’’ on several levels. First,
775
www.getcoolebook.com
Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 776
Part V Data Connectivity
two languages are being combined, in this case, C# and T-SQL. The problem is that the T-SQL language
is not understood in the context of .NET.
Another nuisance is the fact that there is a different set of technologies for the different data sources
(SQL databases, XML, collections, in-memory objects, and so on) that developers work with on a daily
basis. For example, you typically use ADO.NET to work with databases, but you might use XQuery,
XPath, or some of the XML classes built into the .NET Framework to work with XML.
Those are the two issues: Because queries against data are often written as strings embedded in code, the
result is no compile-time checking or IntelliSense support, as well as difficulty debugging; and multiple
technologies (different query languages) for different data sources (SQL, XML, etc.).
Wouldn’t it be nice if there were a way to solve both of these issues? Even better, wouldn’t it be nice if
there were a way to solve both of these issues with a single technology?
Luckily, there is an answer: Microsoft LINQ. This chapter provides a brief history and overview of
LINQ, and then looks at the different LINQ providers, including examples that demonstrate how to
use them.
LINQ Overview
Before looking at LINQ’s current incarnation, it would be wise to first look at its history. Microsoft
LINQ has been in the works for well over five years. LINQ (and other LINQ providers such as LINQ
to SQL) has roots that extend to previous projects, which have been growing and evolving for quite
some time.
LINQ goes all the way back to a previous Microsoft project named C-Omega (or C). C-Omega, in its
fullness, really contained more than what shows up in LINQ, as it contained functionality that truly
experimented with using integrated queries (a mix of C# and other query languages such as SQL and
XQuery). C was initially released to the public as a preview in 2004, and it quickly caught the attention
of other Microsoft people (such as Anders Hejlsberg) who then took that same concept to C# and other
languages.
Yet, as good as C-Omega was, LINQ now contains a lot of what C contained, plus many additional fea-
tures and functionality. It could be said that LINQ ‘‘leapfrogged’’ over C. Those familiar with Microsoft’s
ORM history know that LINQ to SQL is not Microsoft’s first endeavor at object-relational mapping. The
roots of LINQ to SQL can actually be traced to something called ObjectSpaces dating back to 2001. In
simple terms, ObjectSpaces is a set of data access APIs that enable data to be treated as objects, regard-
less of the source of the data.
This story has both a happy ending and a sad ending. The sad ending is that ObjectSpaces was never
officially released, simply because ObjectSpaces depended on WinFS and when Microsoft made the
announcement that WinFS would not make it into the first release of Windows Vista, that was the end
of ObjectSpaces. Happily, however, Microsoft learned a ton about ORM from ObjectSpaces and applied
that knowledge to LINQ to SQL, the result being a well thought-out and solid ORM product. Is it the
ORM product to end all ORM products? No, but even for a first release, LINQ to SQL is surely nothing
to sneeze at.
In the same category is a product called Nordic, developed by Paul Nielsen, which is a T-SQL-based
O/R DBMS fac¸ade. It is a nice utility that uses code-gen to emulate an object-database. It includes
776
www.getcoolebook.com
Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 777
LINQ 34
polymorphism, inheritance, inheritable workflow states, and inheritable complex expressions. Moreover,
according to Paul, it’s fast. This nice tool can be found on CodePlex at
www.codeplex.com/Nordic.
What Is LINQ?
LINQ stands for language-integrated query. In essence, this means that developers have at their disposal
a query language integrated directly into their .NET programming of choice (such as C# and Visual
Basic), giving them general-purpose query capabilities.
LINQ is a standard, unified query experience across different data sources, built directly into your .NET-
supported programming language. LINQ brings powerful query facilities to the .NET Framework lan-
guages, such as C# and VB.NET. Microsoft accomplished this by turning query set operations, trans-
forms, and constructs into high-level concepts (on the same level of classes and objects) within the .NET
Framework. LINQ makes a query a first-class construct within C# and Visual Basic.
Moreover, LINQ addresses the very large mismatch between programming languages and data sources
such as databases, XML, and in-memory objects. The entire goal of LINQ is to make the interaction
between objects and data sources easier and simpler, enabling developers to interact with the different
data sources (such as SQL and XML), because there is a disconnect between the data sources and the
programming languages that communicate with (that is, work with) each of them.
As stated earlier, LINQ is a ‘‘unified’’ query experience. This means that it unifies data access regardless
of the source of data. Simply stated, LINQ bridges the gap between the world of data and the world of
programming.
Prior to LINQ, developers had to learn different technologies to query different data sources. No more!
LINQ gives developers a new way of querying different types of data using strongly typed queries com-
mon across many different data sources. The result is improved developer productivity along with LINQ-
specific IntelliSense in the Visual Studio IDE and compile-time error checking!
That’s right. Because developers are now working with data in an object-oriented fashion, they are also
working in a strongly typed environment. The benefits of this include compile-time error checking, not
runtime error checking. In addition, developers can now write queries using Visual Studio’s IntelliSense,
driving the experience toward a more declarative programming model.
Because LINQ makes a query a first-class construct within the programmer’s development language,
developers can now write and execute queries against strongly typed objects using keywords, or
operators. These keywords, called standard query operators, are discussed in the next section.
Who should use LINQ? The real question is who shouldn’t use LINQ? LINQ is targeted toward appli-
cation developers who want a new way to query data using strongly typed queries and strongly typed
results. It is for developers seeking commonality across a number of different data sources, including but
not limited to relational databases. It is for developers who want improved productivity — and who like
using really cool technology.
Standard Query Operators
Standard query operators are essentially methods that form the LINQ pattern. They provide the query
capabilities, including filtering, projection, sorting, and more. A large portion of these methods that
make up the standard query operators operate on sequences, objects that have implemented the
IEnumerable<T> or IQueryable<T> interface. These two interfaces are what provide the querying
and iteration for queries over a non-generic collection.
777
www.getcoolebook.com
Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 778
Part V Data Connectivity
There are two sets of standard query operators that implement the two interfaces mentioned above.
The key to remember when working with the standard query operators is that regardless of the
set, each method of the standard query operators is a static member of the
Enumerable and
Queryable classes, meaning they can be called using either static method syntax or instance method
syntax.
The standard query operators also vary in their execution timing. This timing depends on the results
of the query. Whether a sequence of values is returned or a singleton value is returned determines
when the query is executed. Methods are executed immediately if they return a singleton value;
otherwise, the query execution is deferred and an enumerable object is returned if the method returns a
sequence.
Table 34-1 lists the 53 standard query operators, grouped according to their functionality.
Query expression syntax
To understand the flow of LINQ query syntax, compare it to standard T-SQL syntax. If you have written
any T-SQL, you know the basic T-SQL query syntax and how it is written. For instance, a simple query
looks like the following:
SELECT FirstName, LastName
FROM Person.Contact
TABLE 34-1
Standard Query Operators
Functionality Standard Query Operator Description
Sorting OrderBy Sorts values in ascending order
OrderByDescending Sorts values in descending order
ThenBy Applies a secondary sort in ascending order
ThenByDescending Applies a secondary sort in descending order
Reverse Sorts the elements of a collection in reverse order
Set Distinct Removes duplicate values from a collection
Except Returns the differences between two sets (elements in
one collection that do not exist in a second collection)
Intersect Returns the matches between two sets (elements that
appear in two separate collections)
Union Returns the union of two sets (unique elements that
appear in either of two collections)
continued
778
www.getcoolebook.com
Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 779
LINQ 34
TABLE 34-1
(continued )
Functionality Standard Query Operator Description
Filtering OfType Selects values from a sequence based on their ability to
be cast to a specified type
Where Selects values from a sequence based on a predicate
function
Quantifier All Determines whether all the elements in a sequence meet
a condition
Any Determines whether any of the elements in a sequence
meet a condition
Contains Determines whether a sequence contains a specified
element
Projection Select Projects values based on a transform function
SelectMany Projects sequences of values based on a transform
function and then combines them into a single sequence
Partitioning Skip Skips elements up to a specified position in a sequence
SkipWhile Skips elements based on a predicate function until an
element does not satisfy the condition
Take Takes elements up to a specified position in a sequence
TakeWhile Takes elements based on a predicate function until an
element does not satisfy the condition
Join Join Joins two sequences based on key selector functions
GroupJoin Joins two sequences based on key selector, grouping the
matches for each element
Grouping GroupBy Groups elements that share a common attribute
ToLookup Inserts elements into a Lookup(TKey, TElement)
Generation DefaultIfEmpty Replaces an empty collection with a default singleton
collection
Empty Returns an empty collection
Range Generates a collection that contains a sequence of
numbers
Repeat Generates a collection that contains one repeated value
Equality SequenceEqual Determines whether two sequences are equal by
comparing elements in a pair-wise manner
continued
779
www.getcoolebook.com
Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 780
Part V Data Connectivity
TABLE 34-1
(continued )
Functionality Standard Query Operator Description
Element ElementAt Returns the element at a specified index in a c ollection
ElementAtOrDefault Returns the element at the specified index in a
collection, or the first element that satisfies a condition
First Returns the first element of a collection, or the first
element that satisfies a condition
FirstOrDefault Returns the first element of a collection, or the first
element that satisfies a condition. A default value is
returned if the specified element does not exist.
Last Returns the last element of a collection or the last
element that satisfies a condition
LastOrDefault Returns the last element of a collection or the last
element that satisfies a condition. A default value is
returned if the specified element does not exist in the
collection.
Single Returns the only element of a collection or the only
element that satisfies a condition
SingleOrDefault Returns the only element of a collection or the only
element that satisfies a condition. A default value is
returned if the specified element does not exist in the
collection.
Conversion AsEnumerable Returns the input types as IEnumerable(T)
AsQueryable Converts a generic IEnumerable to a generic
IQueryable
Cast Casts the elements of a collection to a specified type
OfType Filters values based on their ability to be cast to a
specified type
ToArray Converts a collection to an array. Forces query
execution.
ToDictionary Puts elements into a Dictionary(TKey, TValue).
Forces query execution.
ToList Converts a collection to a List(T). Forces query
execution.
ToLookup Puts elements into a Lookup(TKey, TValue)as a
one-to-many dictionary. Forces query execution.
continued
780
www.getcoolebook.com
Nielsen c34.tex V4 - 07/23/2009 1:56pm Page 781
LINQ 34
TABLE 34-1
(continued )
Functionality Standard Query Operator Description
Concatenation Concat Concatenates two sequences into a single sequence
Aggregation Aggregate Performs a custom aggregation operation on the values of
a collection
Average Calculates the average value of a collection of values
Count Counts the elements in a collection; optionally, only
those elements that satisfy a predicate expression
LongCount Counts the elements in a large collection; optionally,
only those elements that satisfy a predicate expression
Max Determines the maximum value in a collection
Min Determines the minimum value in a collection
Sum Calculates the sum of the values in a collection
This example queries the Person.Contact table in the AdventureWorks database and returns the
FirstName and LastName columns for each row in the table. Because that’s so simple, the following
example adds a secondary table, and then applies a filter and a sort:
SELECT E.EmployeeID,C.FirstName, C.LastName
FROM Person.Contact AS C
INNER JOIN HoumanResources.Employee AS E ON C.ContactID = E.ContactID
WHERE E.EmployeeID < 100
ORDER BY C.LastName
This is the syntax with which all T-SQL developers are familiar. At the very minimum, the query begins
with a
SELECT clause, which specifies the columns you want to be returned by the query, followed
by a
FROM clause, which lists the tables and/or views containing the columns identified in the SELECT
clause.
The query could include one or more joins such as an
INNER JOIN or OUTER JOIN, followed by some
filtering using the
WHERE clause and possibly a GROUP BY or HAVING clause, and quite possibly some
ordering using the
ORDER BY clause.
How many developers have really stopped to think about how SQL Server processes these queries? Does
it execute the query from top to bottom, starting with the
SELECT clause and working its way down?
One might assume that, but that is not how a query is processed in SQL Server at all. SQL Server logi-
cally processes a query in the order indicated here by the number in parentheses:
(8) SELECT
(9) TOP
(1) FROM
(3) JOIN
(2) ON
781
www.getcoolebook.com