This comparer determines element equality by comparing the number of columns and the static data type of each column, and using the IComparable interface on the column’s dynamic data typ
Trang 1Console.WriteLine("Exception occurred: {0}", ex.Message);
Console.WriteLine("Document validated unsuccessfully.");
}
Check that out An entire method specified as a lambda expression Do lambda expressions rock
or what? Here are the results:
Here is the source XML document:
A validation error occurred processing object type XAttribute
The 'language' attribute is not declared
Exception occurred: The 'language' attribute is not declared
Document validated unsuccessfully
Now, I’ll try an example specifying to add the schema information, as shown in Listing 9-17
Listing 9-17 Unsuccessfully Validating an XML Document Against an XSD Schema Using a Lambda
Expression and Specifying to Add Schema Information
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("MiddleName", "Carson"),
new XElement("LastName", "Rattz")),
Trang 2new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
Console.WriteLine("Here is the source XML document:");
Console.WriteLine("{0}{1}{1}", xDocument, System.Environment.NewLine);
XmlSchemaSet schemaSet = new XmlSchemaSet();
"Schema element {0} must have MinOccurs = {1} and MaxOccurs = {2}{3}",
se.Name, se.MinOccurs, se.MaxOccurs, System.Environment.NewLine);
This example starts like the previous It creates an XML document This time, though, I added
an additional element for the first BookParticipant: MiddleName This is invalid because it is not ified in the schema I am validating against Unlike the previous example, I specify for the Validate method to add the schema information Also, unlike the previous example, I am not throwing an exception in my validation event handling code As you may recall, I mentioned previously that the validation must complete to have the schema information added, so your handler must not throw an exception Therefore, I also removed the try/catch block as well
spec-After the validation completes, I am enumerating all the elements in the document and displaying whether they are valid Additionally, I obtain the SchemaElement object from the added schema infor-mation Notice that I make sure the SchemaElement property is not null, because if the element is not valid, the SchemaElement property may be null After all, the element may not be valid because it is not
in the schema, so how could there be schema information? The same applies to the SchemaAttribute property for invalid attributes Once I have a SchemaElement object, I display its Name, MinOccurs, and MaxOccurs properties
Trang 3C H A P T E R 9 ■ A D D I T I O N A L X M L C A P A B I L I T I E S 329
Here are the results:
Here is the source XML document:
An exception occurred processing object type XElement
The element 'BookParticipant' has invalid child element 'MiddleName' List of
possible elements expected: 'LastName'
Element BookParticipants is Invalid
Schema element BookParticipants must have MinOccurs = 1 and MaxOccurs = 1
Element BookParticipant is Invalid
Schema element BookParticipant must have MinOccurs = 1 and MaxOccurs =
79228162514264337593543950335
Element FirstName is Valid
Schema element FirstName must have MinOccurs = 1 and MaxOccurs = 1
Element MiddleName is Invalid
Element LastName is NotKnown
Element BookParticipant is Valid
Schema element BookParticipant must have MinOccurs = 1 and MaxOccurs =
79228162514264337593543950335
Element FirstName is Valid
Schema element FirstName must have MinOccurs = 1 and MaxOccurs = 1
Element LastName is Valid
Schema element LastName must have MinOccurs = 1 and MaxOccurs = 1
There are no real surprises in this output Notice that the MaxOccurs property value for the
BookParticipant element is a very large number This is because in the schema, the maxOccurs attribute
is specified to be "unbounded"
For the final pair of validation examples, I will use one of the Validate method prototypes that
applies to validating elements The first thing you will notice about it is that it has an argument that
requires an XmlSchemaObject to be passed This means the document must have already been validated
This seems odd This is for a scenario where we have already validated once and need to revalidate a
portion of the XML tree
For this scenario, imagine I load an XML document and validate it to start Next, I have allowed
a user to update the data for one of the book participants and now need to update the XML document
Trang 4to reflect the user’s changes, and I want to validate that portion of the XML tree again, after the updates This is where the Validate method prototypes of the elements and attributes can come in handy.Because this example, shown in Listing 9-18, is more complex than some of the previous exam-ples, I will explain it as I go First, to be a little different, and because I need an expanded schema to facilitate an edit to the XML tree, I will define the schema programmatically instead of loading it from
a file, as I have in the previous examples
Listing 9-18 Successfully Validating an XML Element
<xs:element name='FirstName' type='xs:string' />
<xs:element minOccurs='0' name='MiddleInitial'
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add("", XmlReader.Create(new StringReader(schema)));
In the previous code, I merely copied the schema from the file that I have been using I did a search on the double quotes and replaced them with single quotes I also added a MiddleInitial element between the FirstName and LastName elements Notice that I specify the minOccurs attribute
as 0 so the element is not required Next, I create a schema set from the schema Next, it’s time to create an XML document:
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
Console.WriteLine("Here is the source XML document:");
Console.WriteLine("{0}{1}{1}", xDocument, System.Environment.NewLine);
Trang 5C H A P T E R 9 ■ A D D I T I O N A L X M L C A P A B I L I T I E S 331
There is nothing new here I just created the same document I usually do for the examples and
displayed it Now I will validate the document:
bool valid = true;
xDocument.Validate(schemaSet, (o, vea) =>
Notice that I validate a little differently than I have in previous examples I initialize a bool to
true, representing whether the document is valid Inside the validation handler, I set it to false So
if a validation error occurs, valid will be set to false I then check the value of valid after validation
to determine whether the document is valid, and display its validity In this example, the document
is valid at this point
Now, it’s time to imagine that I am allowing a user to edit any particular book participant The
user has edited the book participant whose first name is "Joe" So I obtain a reference for that element,
update it, and revalidate it after the update:
XElement bookParticipant = xDocument.Descendants("BookParticipant")
As you can see, I initialize valid to true and call the Validate method, this time on the
bookParticipant element instead of the entire document Inside the validation event handler, I set
valid to false After validation of the book participant element, I display its validity Here are the results:
Trang 6Here is the source XML document:
Document validated successfully
Element validated successfully
As you can see, the validation of the element is successful For the final example, I have the same code, except this time when I update the BookParticipant element, I will create a MiddleName element,
as opposed to MiddleInitial, which is not valid Listing 9-19 is the code
Listing 9-19 Unsuccessfully Validating an XML Element
<xs:element name='FirstName' type='xs:string' />
<xs:element minOccurs='0' name='MiddleInitial' type='xs:string' /> <xs:element name='LastName' type='xs:string' />
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add("", XmlReader.Create(new StringReader(schema)));
Trang 7C H A P T E R 9 ■ A D D I T I O N A L X M L C A P A B I L I T I E S 333
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
Console.WriteLine("Here is the source XML document:");
Console.WriteLine("{0}{1}{1}", xDocument, System.Environment.NewLine);
bool valid = true;
xDocument.Validate(schemaSet, (o, vea) =>
Trang 8This code is identical to the previous example except instead of adding a MiddleInitial element, I added a MiddleName element that is invalid Here are the results:
Here is the source XML document:
Document validated successfully
An exception occurred processing object type XElement
The element 'BookParticipant' has invalid child element 'MiddleName' List of
possible elements expected: 'MiddleInitial, LastName'
Element validated unsuccessfully
As you can see, the element is no longer valid Now, this example may seem a little hokey because
I said to imagine a user is editing the document No developer in their right mind would create a user interface that would intentionally allow a user to create edits that would be invalid But imagine if that user is in reality some other process on the XML document Perhaps you passed the XML docu-ment to someone else’s program to make some update and you know they personally have it in for you and are seeking your personal destruction Now it may make sense to revalidate You know you can’t trust them
XPath
If you are accustomed to using XPath, you can also gain some XPath query capabilities thanks to the System.Xml.XPath.Extensions class in the System.Xml.XPath namespace This class adds XPath search capability via extension methods
Prototypes
Here is a list of some of the method prototypes available in the System.Xml.XPath.Extensions class:XPathNavigator Extensions.CreateNavigator(this XNode node);
XPathNavigator Extensions.CreateNavigator(this XNode node, XmlNameTable nameTable);
object Extensions.XPathEvaluate(this XNode node, string expression);
object Extensions.XPathEvaluate(this XNode node, string expression,
IXmlNamespaceResolver resolver);
XElement Extensions.XPathSelectElement(this XNode node, string expression);
XElement Extensions.XPathSelectElement(this XNode node, string expression,
IXmlNamespaceResolver resolver);
Trang 9C H A P T E R 9 ■ A D D I T I O N A L X M L C A P A B I L I T I E S 335
IEnumerable<XElement> Extensions.XPathSelectElements(this XNode node,
string expression);
IEnumerable<XElement> Extensions.XPathSelectElements(this XNode node,
string expression, IXmlNamespaceResolver resolver);
Examples
Using these extension methods, it is possible to query a LINQ to XML document using XPath search
expressions Listing 9-20 is an example
Listing 9-20 Querying XML with XPath Syntax
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
XElement bookParticipant = xDocument.XPathSelectElement(
"//BookParticipants/BookParticipant[FirstName='Joe']");
Console.WriteLine(bookParticipant);
As you can see, I created my typical XML document I didn’t display the document this time,
though I called the XPathSelectElement method on the document and provided an XPath search
expression to find the BookParticipant element whose FirstName element’s value is "Joe" Here are
Using the XPath extension methods, you can obtain a reference to a System.Xml.XPath
XPathNavigator object to navigate your XML document, perform an XPath query to return an
element or sequence of elements, or evaluate an XPath query expression
Summary
At this point, if you came into this chapter without any knowledge of XML, I can only assume you are
overwhelmed If you did have a basic understanding of XML, but not of LINQ to XML, I hope I have
made this understandable for you The power and flexibility of the LINQ to XML API is quite intoxicating
While writing this chapter and creating the examples, I would find myself lulled into a state of
XML euphoria, a state without the underlying desire to avoid using “real” XML, only to find myself
back at my day job planning on taking advantage of the simplicity LINQ to XML offers, despite the
fact that my work project cannot use it because it has not been released yet So many times I thought,
Trang 10if I could just use functional construction to whip up this piece of XML, only to find the reality of the situation causing me to use my standby XML library, the String.Format method
Don’t chastise me for taking the easy way out As I previously mentioned, I was at a Microsoft seminar where the presenter demonstrated code that built XML in a similar manner
Having written the many examples in this chapter and the previous LINQ to XML chapters, I can’t tell you how excited I will be to actually use the LINQ to XML API in my real production code The fact
is that with LINQ to XML, because XML creation is largely based on elements rather than documents coupled with the capability of functional construction, creating XML is painless It might even be fun Combine the easy creation with the intuitive traversal and modification, and it becomes a joy to work with, considering the alternatives
Having all this ease of use working with XML piled on top of a powerfully flexible query language makes LINQ to XML my personal favorite part of LINQ If you find yourself dreading XML or intimi-dated to work with it, I think you will find the LINQ to XML API quite pleasant
Trang 11■ ■ ■
P A R T 4
LINQ to DataSet
Trang 13■ ■ ■
C H A P T E R 1 0
LINQ to DataSet Operators
While I haven’t covered LINQ to SQL yet, let me mention at this time that to utilize LINQ to SQL for a
given database, source code classes must be generated for that database and compiled, or a mapping
file must be created This means that performing LINQ queries with LINQ to SQL on a database
unknown until runtime is not possible Additionally, LINQ to SQL only works with Microsoft SQL
Server What is a developer to do?
The LINQ to DataSet operators allow a developer to perform LINQ queries on a DataSet, and
since a DataSet can be obtained using normal ADO.NET SQL queries, LINQ to DataSet allows LINQ
queries over any database that can be queried with ADO.NET This provides a far more dynamic
database-querying interface than LINQ to SQL
You may be wondering, under what circumstances would you not know the database until runtime?
It is true that for the typical application, the database is known while the application is being developed,
and therefore LINQ to DataSet is not as necessary But what about a database utility type application? For
example, consider an application such as SQL Server Enterprise Manager It doesn’t know what
databases are going to be installed on the server until runtime The Enterprise Manager application
allows you to examine whatever databases are installed on the server, with whatever tables are in a
specified database There is no way the Enterprise Manager application developer could generate
the LINQ to SQL classes at compile time for your database This is when LINQ to DataSet becomes a
necessity
While this part of the book is named LINQ to DataSet, you will find that the added operators are
really pertaining to DataTable, DataRow, and DataColumn objects Don’t be surprised that you don’t see
DataSet objects referenced often in this chapter It is understood that in real-life circumstances, your
DataTable objects will almost always come from DataSet objects However, for the purpose of database
independence, brevity, and clarity, I have intentionally created simple DataTable objects
program-matically, rather than retrieved them from a database, for most of the examples
The LINQ to DataSet operators consist of several special operators from multiple assemblies
and namespaces that allow the developer to do the following:
• Perform set operations on sequences of DataRow objects
• Retrieve and set DataColumn values
• Obtain a LINQ standard IEnumerable<T> sequence from a DataTable so Standard Query
Oper-ators may be called
• Copy modified sequences of DataRow objects to a DataTable
In addition to these LINQ to DataSet operators, once you have called the AsEnumerable operator,
you can call the LINQ to Objects Standard Query Operators on the returned sequence of DataRow
objects, resulting in even more power and flexibility
Trang 14Assembly References
For the examples in this chapter, you will need to add references to your project for the System.Data.dll and System.Data.DataSetExtensions.dll assembly DLLs, if they have not already been added
This will allow your code to find the LINQ to DataSet operators
Common Code for the Examples
Virtually every example in this chapter will require a DataTable object on which to perform LINQ to DataSet queries In real production code, you would typically obtain these DataTable objects by querying
a database However, for some of these examples, I present situations where the data conditions in
a typical database table will not suffice For example, I need duplicate records to demonstrate the Distinct method Rather than jump through hoops trying to manipulate the database to contain the data I may need, I programmatically create a DataTable containing the specific data I desire for each example This also relieves you of the burden of having a database for testing the majority of these examples
Since I will not actually be querying a database for the DataTable objects, and to make creating the DataTable objects easy, I generate them from an array of objects of a predefined class For the predefined class, I use the Student class
A Simple Class with Two Public Members
class Student
{
public int Id;
public string Name;
Converting an Array of Student Objects to a DataTable
static DataTable GetDataTable(Student[] students)
{
DataTable table = new DataTable();
table.Columns.Add("Id", typeof(Int32));
table.Columns.Add("Name", typeof(string));
Trang 15There isn’t anything complex in this method I just instantiate a DataTable object, add two
columns, and add a row for each element in the passed students array
For many of the examples of the LINQ to DataSet operators, I need to display a DataTable for the
results of the code to be clear While the actual data in the DataTable varies, the code needed to display
the DataTable object’s header will not Instead of repeating this code throughout all the examples, I
create the following method and call it in any example needing to display a DataTable header
The OutputDataTableHeader Method
static void OutputDataTableHeader(DataTable dt, int columnWidth)
{
string format = string.Format("{0}0,-{1}{2}", "{", columnWidth, "}");
// Display the column headings
foreach(DataColumn column in dt.Columns)
The purpose of the method is to output the header of a DataTable in a tabular form
DataRow Set Operators
As you may recall, in the LINQ to Objects API, there are a handful of Standard Query Operators that
exist for the purpose of making sequence set-type comparisons I am referring to the Distinct, Except,
Intersect, Union, and SequenceEqual operators Each of these operators performs a set operation on
two sequences
For each of these set-type operators, determining sequence element equality is necessary to
perform the appropriate set operation These operators perform element comparisons by calling the
GetHashCode and Equals methods on the elements For a DataRow, this results in a reference
compar-ison, which is not the desired behavior This will result in the incorrect determination of element
equality, thereby causing the operators to return erroneous results Because of this, each of these
operators has an additional prototype that I omitted in the LINQ to Objects chapters This additional
prototype allows an IEqualityComparer object to be provided as an argument Conveniently, a comparer
object has been provided for us specifically for these versions of the operators,
Trang 16System.Data.DataRowComparer.Default This comparer class is in the System.Data namespace in the System.Data.Entity.dll assembly This comparer determines element equality by comparing the number of columns and the static data type of each column, and using the IComparable interface
on the column’s dynamic data type if that type implements the interface; otherwise, it calls the System.Object’s static Equals method
Each of these additional operator prototypes is defined in the System.Linq.Enumerable static class just as the other prototypes of these operators are
In this section, I provide some examples to illustrate the incorrect and, more importantly, correct way to make these sequence comparisons when working with DataSet objects
Distinct
The Distinct operator removes duplicate rows from a sequence of objects It returns an object that when enumerated, enumerates a source sequence of objects and returns a sequence of objects with the duplicate rows removed Typically, this operator determines duplicates by calling each element’s data type’s GetHashCode and Equals methods However, for DataRow type objects, this would cause an incorrect result
Because I am going to call the additional prototype and provide the System.Data.DataRowComparer.Default comparer object, the element equality will be properly determined With it, a row is deemed
to be a duplicate by comparing DataRow objects using the number of columns in a row and the static data type of each column, and then using the IComparable interface on each column if its dynamic data type implements the IComparable interface, or calling the static Equals method in System.Object if it does not
Prototypes
The Distinct operator has one prototype I will cover
The Distinct Prototype
public static IEnumerable<T> Distinct<T> (
this IEnumerable<T> source,
Listing 10-1 The Distinct Operator with an Equality Comparer
Student[] students = {
new Student { Id = 1, Name = "Joe Rattz" },
new Student { Id = 6, Name = "Ulyses Hutchens" },
new Student { Id = 19, Name = "Bob Tanko" },
new Student { Id = 45, Name = "Erin Doutensal" },
new Student { Id = 1, Name = "Joe Rattz" },
new Student { Id = 12, Name = "Bob Mapplethorpe" },
new Student { Id = 17, Name = "Anthony Adams" },
Trang 17Notice that I use the AsEnumerable operator to get a sequence of DataRow objects from the
DataTable because that is what I must call the Distinct operator on Also notice that in the students
array, the record with an Id equal to 1 is repeated
You no doubt noticed that I call a method named Field on the DataRow object For now, just
understand that this is a helper method that makes obtaining a DataColumn object’s value from a
DataRow more convenient I cover the Field<T> operator in depth later in the “DataRow Field Operators”
section of this chapter
Here are the results:
Before calling Distinct()
Trang 18After calling Distinct()
Listing 10-2 The Distinct Operator Without an Equality Comparer
Student[] students = {
new Student { Id = 1, Name = "Joe Rattz" },
new Student { Id = 6, Name = "Ulyses Hutchens" },
new Student { Id = 19, Name = "Bob Tanko" },
new Student { Id = 45, Name = "Erin Doutensal" },
new Student { Id = 1, Name = "Joe Rattz" },
new Student { Id = 12, Name = "Bob Mapplethorpe" },
new Student { Id = 17, Name = "Anthony Adams" },
new Student { Id = 32, Name = "Dignan Stephens" }
IEnumerable<DataRow> distinct = dt.AsEnumerable().Distinct();
Console.WriteLine("{0}After calling Distinct(){0}",
Trang 19C H A P T E R 1 0 ■ L I N Q T O D A T A S E T O P E R A T O R S 345
dataRow.Field<int>(0),
dataRow.Field<string>(1));
}
The only difference between this code and the previous example is that the call to the Distinct
operator does not have an equality comparer provided Will it remove the duplicate row? Let’s take
The Except operator produces a sequence of DataRow objects that are in the first sequence of DataRow
objects that do not exist in the second sequence of DataRow objects The operator returns an object that,
when enumerated, enumerates the first sequence of DataRow objects collecting the unique elements,
followed by enumerating the second sequence of DataRow objects, removing those elements from
the collection that also occur in the first sequence Lastly, it yields the remaining elements in the
collection in the order they are collected
To determine that elements from the same sequence are unique, and that one element in one
sequence is or is not equal to an element in the other sequence, the operator must be able to
deter-mine whether two elements are equal Typically, this operator deterdeter-mines element equality by calling
each element’s data type’s GetHashCode and Equals methods However, for DataRow type objects, this
would cause an incorrect result
Because I am going to call the additional prototype and provide the System.Data.DataRowComparer
Default comparer object, the element equality will be properly determined With it, a row is deemed
to be a duplicate by comparing DataRow objects using the number of columns in a row and the static
Trang 20data type of each column, and then using the IComparable interface on each column if its dynamic data type implements the IComparable interface, or calling the static Equals method in System.Object if
it does not
Prototypes
The Except operator has one prototype I will cover
The Except Prototype
public static IEnumerable<T> Except<T> (
this IEnumerable<T> first,
of that query to be incorrect Listing 10-3 shows the code
Listing 10-3 The Except Operator with and Without the Comparer Object
Student[] students = {
new Student { Id = 1, Name = "Joe Rattz" },
new Student { Id = 7, Name = "Anthony Adams" },
new Student { Id = 13, Name = "Stacy Sinclair" },
new Student { Id = 72, Name = "Dignan Stephens" }
};
Student[] students2 = {
new Student { Id = 5, Name = "Abe Henry" },
new Student { Id = 7, Name = "Anthony Adams" },
new Student { Id = 29, Name = "Future Man" },
new Student { Id = 72, Name = "Dignan Stephens" }
Trang 21There isn’t much to this example I basically create two DataTable objects that are populated
from the Student arrays I create sequences from each DataTable object by calling the AsEnumerable
method I then call the Except operator on the two sequences and display the results of each As you
can see, the first time I call the Except operator, I pass the System.Data.DataRowComparer.Default
comparer object The second time I do not
Let’s look at the results of that code by pressing Ctrl+F5:
Results of Except() with comparer
As you can see, the Except operator called with the System.Data.DataRowComparer.Default
comparer object is able to properly determine the element equality for the two sequences, whereas
the Except operator without the comparer object does not identify any elements from the two sequences
as being equal, which is not the desired behavior for this operator
Intersect
The Intersect operator produces a sequence of DataRow objects that is the intersection of two
sequences of DataRow objects It returns an object that when enumerated enumerates the first sequence
of DataRow objects collecting the unique elements, followed by enumerating the second sequence of
DataRow objects, marking those elements occurring in both sequences Lastly, it yields the marked
elements in the order they are collected
Trang 22To determine that elements from the same sequence are unique, and that one element in one sequence is or is not equal to an element in the other sequence, the operator must be able to deter-mine whether two elements are equal Typically, this operator determines element equality by calling each element’s data type’s GetHashCode and Equals methods However, for DataRow type objects, this would cause an incorrect result.
Because I am going to call the additional prototype and provide the System.Data.DataRowComparer.Default comparer object, the element equality will be properly determined With it, a row is deemed
to be a duplicate by comparing DataRow objects using the number of columns in a row and the static data type of each column, and then using the IComparable interface on each column if its dynamic data type implements the IComparable interface, or calling the static Equals method in System.Object if it does not
Prototypes
The Intersect operator has one prototype I will cover
The Intersect Prototype
public static IEnumerable<T> Intersect<T> (
this IEnumerable<T> first,
new Student { Id = 1, Name = "Joe Rattz" },
new Student { Id = 7, Name = "Anthony Adams" },
new Student { Id = 13, Name = "Stacy Sinclair" },
new Student { Id = 72, Name = "Dignan Stephens" }
};
Student[] students2 = {
new Student { Id = 5, Name = "Abe Henry" },
new Student { Id = 7, Name = "Anthony Adams" },
new Student { Id = 29, Name = "Future Man" },
new Student { Id = 72, Name = "Dignan Stephens" }
Trang 23There is nothing new here I create a couple of DataTable objects from the two Student arrays
and obtain sequences from them I then call the Intersect operator first with the comparer object
and then without I display the results after each Intersect call Let’s look at the results of that code
As you can see, the Intersect operator with the comparer is able to properly determine the
element equality from the two sequences, whereas the Intersect operator without the comparer did
not identify any elements from the two sequences as being equal, which is not the desired behavior
for this operator
Union
The Union operator produces a sequence of DataRow objects that is the union of two sequences
of DataRow objects It returns an object that, when enumerated, enumerates the first sequence of
DataRow objects, followed by the second sequence of DataRow objects, yielding any element that has
not already been yielded
Trang 24To determine that elements have already been yielded, the operator must be able to determine whether two elements are equal Typically, this operator determines element equality by calling each element’s data type’s GetHashCode and Equals methods However, for DataRow type objects, this would cause an incorrect result.
Because I am going to call the additional prototype and provide the System.Data.DataRowComparer.Default comparer object, the element equality will be properly determined With it, a row is deemed
to be a duplicate by comparing DataRow objects using the number of columns in a row and the static data type of each column, and then using the IComparable interface on each column if its dynamic data type implements the IComparable interface, or calling the static Equals method in System.Object if it does not
Prototypes
The Union operator has one prototype I will cover
The Union Prototype
public static IEnumerable<T> Union<T> (
this IEnumerable<T> first,
new Student { Id = 1, Name = "Joe Rattz" },
new Student { Id = 7, Name = "Anthony Adams" },
new Student { Id = 13, Name = "Stacy Sinclair" },
new Student { Id = 72, Name = "Dignan Stephens" }
};
Student[] students2 = {
new Student { Id = 5, Name = "Abe Henry" },
new Student { Id = 7, Name = "Anthony Adams" },
new Student { Id = 29, Name = "Future Man" },
new Student { Id = 72, Name = "Dignan Stephens" }
Trang 25Again, there is nothing new here I create a couple of DataTable objects from the two Student
arrays and obtain sequences from them I then call the Union operator first with the comparer object
and then without I display the results after each Union call Here are the results:
Results of Union() with comparer
Notice that the results of the Union operator with the comparer object are correct, but the results
of the Union operator without the comparer object are not
Trang 26The SequenceEqual operator compares two sequences of DataRow objects to determine whether they are equal It enumerates two source sequences, comparing the corresponding DataRow objects If the two source sequences have the same number of records, and if all the corresponding DataRow objects are equal, true is returned Otherwise, the two sequences are not equal, and false is returned.This operator must be able to determine whether two elements are equal Typically, this oper-ator determines element equality by calling each element’s data type’s GetHashCode and Equals methods However, for DataRow type objects, this would cause an incorrect result
Because I am going to call the additional prototype and provide the System.Data.DataRowComparer.Default comparer object, the element equality will be properly determined With it, a row is deemed
to be a duplicate by comparing DataRow objects using the number of columns in a row and the static data type of each column, and then using the IComparable interface on each column if its dynamic data type implements the IComparable interface, or calling the static Equals method in System.Object if it does not
Prototypes
The SequenceEqual operator has one prototype I will cover
The SequenceEqual Prototype
public static bool SequenceEqual<T> (
this IEnumerable<T> first,
Listing 10-6 The SequenceEqual Operator with and Without a Comparer Object
Student[] students = {
new Student { Id = 1, Name = "Joe Rattz" },
new Student { Id = 7, Name = "Anthony Adams" },
new Student { Id = 13, Name = "Stacy Sinclair" },
new Student { Id = 72, Name = "Dignan Stephens" }
};
DataTable dt1 = GetDataTable(students);
IEnumerable<DataRow> seq1 = dt1.AsEnumerable();
DataTable dt2 = GetDataTable(students);
IEnumerable<DataRow> seq2 = dt2.AsEnumerable();
bool equal = seq1.SequenceEqual(seq2, System.Data.DataRowComparer.Default);
Console.WriteLine("SequenceEqual() with comparer : {0}", equal);