Implement data access - Exam Ref 70-483: Programming in C# (2013)

Exam Ref 70-483: Programming in C# (2013)

Chapter 4. Implement data access

Each application that you create depends on some type of data. Together we already produce so much data that we’re speaking about big data. When building your applications, you will often need to work with data from all kinds of storage types. Maybe a file on a hard drive, a relational database, or a service that some other application exposes.

In this chapter, you learn how to work with files, databases, and web services. You look at how Language Integrated Query (LINQ) can help you with writing readable, strongly typed queries against all kinds of data sources.

When working with data, you also need to know about serializing your data. In this chapter, you learn about different kinds of serialization and how you configure it to suit your needs.

When you talk about data, you also need in-memory constructs to work with it. This is where collections come in. In this chapter, you look at the collections.NET Framework offers for various different scenarios and how you can create your own collections.

Objectives in this chapter:

§ Objective 4.1: Perform I/O operations

§ Objective 4.2: Consume data

§ Objective 4.3: Query and manipulate data and objects by using LINQ

§ Objective 4.4: Serialize and deserialize data

§ Objective 4.5: Store data in and retrieve data from collections

Objective 4.1: Perform I/O operations

Working with files is a common task when building an application. Sometimes you have to create a file to store data or to parse a file and extract some content from it. Maybe you are writing plain text or binary data—this is where a Stream object can be used. Maybe you are using streams to send bytes over the network to another application. The .NET Framework also offers support for those scenarios.

Another important topic when it comes to dealing with input/output (I/O) is performance. The new async keyword in C# can be used to easily implement asynchronous operations that can improve usability and scalability of your application.

THIS OBJECTIVE COVERS HOW TO:

§ Work with files.

§ Work with streams.

§ Read and write from the network.

§ Implement asynchronous I/O operations.

Working with files

Files are an important aspect of almost every application. That’s why the .NET Framework offers an infrastructure that can help you with all your file-related work. All the necessary types can be found in the System.IO namespace.

Drives

When working with the file system, you obviously start with a storage medium, which can be a hard drive, CD player, or another storage type. The .NET Framework offers the DriveInfo class to access the drives on your computer. A DriveInfo object doesn’t have any specific methods for dealing with drives (for example, there is not an eject method for a CD player). It does have several properties to access information such as the name of the drive, size, and available free space. Example 4-1 shows how to enumerate the current drives and display some information about them.

Example 4-1. Listing drive information

DriveInfo[] drivesInfo = DriveInfo.GetDrives();

foreach (DriveInfo driveInfo in drivesInfo)

{

Console.WriteLine("Drive {0}", driveInfo.Name);

Console.WriteLine(" File type: {0}", driveInfo.DriveType);

if (driveInfo.IsReady == true)

{

Console.WriteLine(" Volume label: {0}", driveInfo.VolumeLabel);

Console.WriteLine(" File system: {0}", driveInfo.DriveFormat);

Console.WriteLine(

" Available space to current user:{0, 15} bytes",

driveInfo.AvailableFreeSpace);

Console.WriteLine(

" Total available space: {0, 15} bytes",

driveInfo.TotalFreeSpace);

Console.WriteLine(

" Total size of drive: {0, 15} bytes ",

driveInfo.TotalSize);

}

}

Directories

A drive contains a list of directories and files. To work with those items, you can use the DirectoryInfo object or the static Directory class. Both classes offer access to your folder structure. When executing only a single operation against your file system, it can be more efficient to use the static Directory class. When you want to execute multiple operations against a folder, DirectoryInfo is a better choice.

You can use both classes to create a new folder. When you create a new folder, you automatically have both read and write rights to the folder. Example 4-2 shows how to create a new directory with the static Directory class or DirectoryInfo.

Example 4-2. Creating a new directory

var directory = Directory.CreateDirectory(@"C:\Temp\ProgrammingInCSharp\Directory");

var directoryInfo = new DirectoryInfo(@"C:\Temp\ProgrammingInCSharp\DirectoryInfo");

directoryInfo.Create();

As you can see, a DirectoryInfo object can be initialized with a non-existing folder. Calling Create will create the directory. After creating the folder, you can call other instance members on the DirectoryInfo object that now points to the newly created folder.

You might try to create a new directory in a location in which you don’t have sufficient permissions. In such a case, a UnauthorizedAccessException will be thrown.

You can also remove a folder, but trying to remove a folder that doesn’t exist throws DirectoryNotFoundException. You can use the Exists method on the static Directory class or the Exists property on the DirectoryInfo object to determine whether a folder exists (see Example 4-3).

Example 4-3. Deleting an existing directory

if (Directory.Exists(@"C:\Temp\ProgrammingInCSharp\Directory"))

{

Directory.Delete(@"C:\Temp\ProgrammingInCSharp\Directory");

}

var directoryInfo = new DirectoryInfo(@"C:\Temp\ProgrammingInCSharp\DirectoryInfo");

if (directoryInfo.Exists)

{

directoryInfo.Delete();

}

One important thing to remember when working with directories and files is that the operating system controls access to all elements on your local computer or on a shared network drive. Access to folders can be arranged by using the DirectorySecurity class from theSystem.Security.AccessControl namespace. Example 4-4 shows how you can use these to allow everyone to access a folder. This, of course, requires that the executing program has the rights to make this modification.

Example 4-4. Setting access control for a directory

DirectoryInfo directoryInfo = new DirectoryInfo("TestDirectory");

directoryInfo.Create();

DirectorySecurity directorySecurity = directoryInfo.GetAccessControl();

directorySecurity.AddAccessRule(

new FileSystemAccessRule("everyone",

FileSystemRights.ReadAndExecute,

AccessControlType.Allow));

directoryInfo.SetAccessControl(directorySecurity);

Besides methods for creating and removing directories, you can also query a method for attributes, subdirectories, or files.

The Directory and DirectoryInfo classes both have a method for retrieving all subdirectories of a given directory. This returns an array of DirectoryInfo objects.

You can specify a search pattern and an enumeration of type SearchOption that enables you to automatically search only the top folder or all subfolders. When looping through all folders, it can be that you are trying to access a folder without the required access rights. This throws anUnauthorizedAccessException. When you use SearchOption.AllDirectories and an exception is thrown, you won’t get any results. Example 4-5 shows an example of walking the directory tree manually and handling any exception that can occur. It also shows how to use a search pattern to limit the results to all folders containing the character a. It limits the recursive depth of the function to make sure you can control how long the method takes to execute.

Example 4-5. Building a directory tree

private static void ListDirectories(DirectoryInfo directoryInfo,

string searchPattern, int maxLevel, int currentLevel)

{

if (currentLevel >= maxLevel )

{

return;

}

string indent = new string('-', currentLevel);

try

{

DirectoryInfo[] subDirectories = directoryInfo.GetDirectories(searchPattern);

foreach (DirectoryInfo subDirectory in subDirectories)

{

Console.WriteLine(indent + subDirectory.Name);

ListDirectories(subDirectory, searchPattern,maxLevel, currentLevel + 1);

}

}

catch (UnauthorizedAccessException)

{

// You don't have access to this folder.

Console.WriteLine(indent + "Can't access: " + directoryInfo.Name);

return;

}

catch (DirectoryNotFoundException)

{

// The folder is removed while iterating

Console.WriteLine(indent + "Can't find: " + directoryInfo.Name);

return;

}

}

// List the subdirectories for Program Files containing the character 'a' with a maximum

depth of 5

DirectoryInfo directoryInfo = new DirectoryInfo(@"C:\Program Files");

ListDirectories(directoryInfo, "*a*", 5, 0);

A search pattern can consist of several wildcard characters that form a search pattern as you can see in Table 4-1.

Table 4-1. Wildcards for a search pattern

Wildcard character

Description

Example

*

Zero or more characters

*m* matches Common Files and Media, but not Windows

?

Exactly one character

?edia matches Media, but not Windows Media Player

When working with a huge directory tree, it can be more efficient to use EnumerateDirectories instead of GetDirectories. When using EnumerateDirectories, you can start enumerating the collection before it’s been completely retrieved. When using GetDirectories, you get a list of folder names and you have to wait until the whole list of names is ready.

Another method that can come in handy is the method MoveTo on DirectoryInfo or Move on Directory classes when you want to move an existing directory to a new location. Example 4-6 shows how to use them.

Example 4-6. Moving a directory

Directory.Move(@"C:\source", @"c:\destination");

DirectoryInfo directoryInfo = new DirectoryInfo(@"C:\Source");

directoryInfo.MoveTo(@"C:\destination");

Besides checking which subdirectories a certain directory contains, you can also work with the files that a directory contains. As you can see in Example 4-7, the static Directory.GetFiles(string path) function returns an array of strings, whereas the DirectoryInfo.GetFiles() returns an array of FileInfo objects. This brings us to the next topic.

Example 4-7. Listing all the files in a directory

foreach (string file in Directory.GetFiles(@"C:\Windows"))

{

Console.WriteLine(file);

}

DirectoryInfo directoryInfo = new DirectoryInfo(@"C:\Windows");

foreach (FileInfo fileInfo in directoryInfo.GetFiles())

{

Console.WriteLine(fileInfo.FullName);

}

Working with files

Directories are necessary only to give some structure to the files they need to store. Just as with directories, you can use both a static File class and a FileInfo object to access files.

You can determine whether a file exists and when it does, you can take action, such as deleting it. Example 4-8 shows how to see whether a file exists and delete it if it does.

Example 4-8. Deleting a file

string path = @"c:\temp\test.txt";

if (File.Exists(path))

{

File.Delete(path);

}

FileInfo fileInfo = new FileInfo(path);

if (fileInfo.Exists)

{

fileInfo.Delete();

}

You can also move files around by using the File.Move(string source, string destination) or FileInfo.MoveTo(string destination). Moving a file deletes the original if the move is successful. If that’s not what you want, you can also use Copy and CopyTo. Example 4-9 shows how to move a file; Example 4-10 shows how to copy a file.

Example 4-9. Moving a file

string path = @"c:\temp\test.txt";

string destPath = @"c:\temp\destTest.txt";

File.CreateText(path).Close();

File.Move(path, destPath);

FileInfo fileInfo = new FileInfo(path);

fileInfo.MoveTo(destPath);

Example 4-10. Copying a file

string path = @"c:\temp\test.txt";

string destPath = @"c:\temp\destTest.txt";

File.CreateText(path).Close();

File.Copy(path, destPath);

FileInfo fileInfo = new FileInfo(path);

fileInfo.CopyTo(destPath);

Working with paths

When accessing files, you often have a need for combining a directory and a file name. Manually concatenating them by using simple string addition can work, but it is error-prone. For example, adding the strings shown in Example 4-11 results in an invalid path.

Example 4-11. Don’t manually concatenate strings to form a file path

string folder = @"C:\temp";

string fileName = "test.dat";

string fullPath = folder + fileName; // Results in C:\temptest.dat

The static class Path that can be found in System.IO has some helper methods for dealing with these kinds of situations. One of these methods is the static Combine method that has a number of overloads that accept multiple string parameters. Example 4-12 shows how the Combine method results in a correct path.

Example 4-12. Using Path.Combine

string folder = @"C:\temp";

string fileName = "test.dat";

string fullPath = Path.Combine(folder, fileName); // Results in C:\\temp\\test.dat

The Path class offers some other helpful methods: GetDirectoryName, GetExtensions, GetFileName, and GetPathRoot. Example 4-13 shows how you can use these methods on a string that contains a full path.

Example 4-13. Using other Path methods to parse a path

string path = @"C:\temp\subdir\file.txt";

Console.WriteLine(Path.GetDirectoryName(path)); // Displays C:\temp\subdir

Console.WriteLine(Path.GetExtension(path)); // Displays .txt

Console.WriteLine(Path.GetFileName(path)); // Displays file.txt

Console.WriteLine(Path.GetPathRoot(path)); // Displays C:\

The Path class can also help you when you need to temporarily store some data. You can use GetRandomFileName to create a random file or directory name. GetTempPath returns the location of the current user’s temporary folder, and GetTempFileName creates a temporary file that you can then use to store some data in.

EXAM TIP

Never try to manually add strings together to form a path. Always use the Path class when combining multiple strings together to form a legal path.

Working with streams

Of course, the most interesting thing you do with files is to create and read them. When working with files in the .NET Framework, it’s important to know about the Stream class, which is a base class that is used in the .NET Framework for I/O operations. It’s an abstraction of a sequence of bytes. For example, a file is, in essence, a sequence of bytes stored on your hard drive or a DVD, but a network socket also works with sequences of bytes just as an interprocess communication pipe. The Stream class provides a generic interface for all these types of input/output.

The base Stream class

A stream has three fundamental operations:

§ Reading

§ Writing

§ Seeking

Reading from a stream means that you get a series of bytes. You can then translate those into some meaningful data such as text or deserialize them to an object.

Writing is the reverse operation: You translate an object into a series of bytes and then send it off to the stream. It can then be sent across the network or persisted to a file on some other storage medium, such as a hard disk.

Seeking refers to the fact that some streams have the concept of a current position. You can query for the current position of a cursor and move it around. Seeking isn’t supported by all streams. Files mostly support seeking (depending on the file type). But a network socket doesn’t have the concept of a current position. You can’t move forward or backward in a stream of bytes that is being sent to you over a network cable.

When working with the File and FileInfo classes, you will often deal with a FileStream, which supports the methods for reading and writing bytes in files, and for setting the current position by executing a seek operation. Example 4-14 shows how to create a file by using a FileStream.

Example 4-14. Create and use a FileStream

string path = @»c:\temp\test.dat»;

using (FileStream fileStream = File.Create(path))

{

string myValue = "MyValue";

byte[] data = Encoding.UTF8.GetBytes(myValue);

fileStream.Write(data, 0, data.Length);

}

When writing to a FileStream, you can use the synchronous method Write, which expects a byte array. A simple File object does not store text directly. As already mentioned, a Stream works with bytes, so you need to convert your text into bytes.

MORE INFO: USING SYNCHRONOUS AND ASYNCHRONOUS I/O OPERATIONS

For more information about the difference between asynchronous and synchronous I/O operations, see the section Implementing asynchronous I/O operations later in this chapter.

Encoding and decoding

The process of converting characters into bytes (and the other way around) is called encoding and decoding. The Unicode Consortium is responsible for maintaining a standard that describes how this should happen.

A char, which is the most basic character type, is equivalent to a single Unicode character taking 2 bytes in memory. A string is simply a sequence of chars. System.Text.Encoding is the class that helps you convert between bytes and strings.

The .NET Framework offers several encoding standards that you can use. UTF-8 is one that suffices for general purpose use. It can represent all Unicode characters and it is used as the default encoding in a lot of the .NET Framework classes. Other encodings are ASCII, BigEndianUnicode,Unicode, UTF32, and UTF7.

To make this a little easier when working with text, the File class also supports a CreateText method that creates a file with an UTF-8 encoding for you. CreateText returns a StreamWriter, a class that inherits from TextWriter and enables you to directly write characters to a Stream with a particular encoding, as shown in Example 4-15.

Example 4-15. Using File.CreateText with a StreamWriter

string path = @»c:\temp\test.dat»;

using (StreamWriter streamWriter = File.CreateText(path))

{

string myValue = «MyValue»;

streamWriter.Write(myValue);

}

Of course, you will also want to read data from a file. You can do this by directly using a FileStream object, reading the bytes, and converting them back to a string with the correct encoding. Example 4-16 shows how to read the bytes from a file and convert them to a string with UTF-8 encoding.

Example 4-16. Opening a FileStream and decode the bytes to a string

using (FileStream fileStream = File.OpenRead(path))

{

byte[] data = new byte[fileStream.Length];

for (int index = 0; index < fileStream.Length; index++)

{

data[index] = (byte)fileStream.ReadByte();

}

Console.WriteLine(Encoding.UTF8.GetString(data)); // Displays: MyValue

}

If you know that you are parsing a text file, you can also use a StreamReader (as the opposite of the StreamWriter) to read a text file. The StreamReader uses a default encoding and returns the bytes to you as a string (see Example 4-17).

Example 4-17. Opening a TextFile and reading the content

using (StreamReader streamWriter = File.OpenText(path))

{

Console.WriteLine(streamWriter.ReadLine()); // Displays: MyValue

}

Using different types of streams together

Because of the way Streams are designed, you can couple multiple Stream objects together to perform a more complex operation. This principle is called the decorator pattern. For example, when you want to compress some data you can use a GZipStream., which takes another Stream object in its constructor. The second Stream is used as the input or output for the compression algorithm. By giving it a FileStream object, you can easily compress some data (such as another file) and store it to disk. You can also use a MemoryStream as the input or output for a GZipStream. You can find the GZipStream in the System.IO.Compression namespace. Example 4-18 creates some data, writes it to a file, and then compresses the file. As you can see, the compressed file is a lot smaller than the original file.

MORE INFO: DECORATOR PATTERN

For more information about the decorator pattern, see http://en.wikipedia.org/wiki/Decorator_pattern.

Example 4-18. Compressing data with a GZipStream

string folder = @»c:\temp»;

string uncompressedFilePath = Path.Combine(folder, "uncompressed.dat");

string compressedFilePath = Path.Combine(folder, "compressed.gz");

byte[] dataToCompress = Enumerable.Repeat((byte)'a', 1024 * 1024).ToArray();

using (FileStream uncompressedFileStream = File.Create(uncompressedFilePath))

{

uncompressedFileStream.Write(dataToCompress, 0, dataToCompress.Length);

}

using (FileStream compressedFileStream = File.Create(compressedFilePath))

{

using (GZipStream compressionStream = new GZipStream(

compressedFileStream, CompressionMode.Compress))

{

compressionStream.Write(dataToCompress, 0, dataToCompress.Length);

}

}

FileInfo uncompressedFile = new FileInfo(uncompressedFilePath);

FileInfo compressedFile = new FileInfo(compressedFilePath);

Console.WriteLine(uncompressedFile.Length); // Displays 1048576

Console.WriteLine(compressedFile.Length); // Displays 1052

As Example 4-18 shows, you can pass another Stream to the constructor of a GZipStream. When writing data to the GZipStream, it compresses the data and then immediately forwards it to the FileStream.

Another example where this can be used is with a BufferedStream. Hard drives are optimized for reading larger blocks of data. Reading a file byte by byte can be slower than reading big chunks of data and processing them byte by byte. Just as with the GZipStream, the BufferedStream takes another Stream in its constructor. The BufferedStream helps you with checking to determine whether it’s possible to read or write larger chunks of data at once. Example 4-19 shows how to write some data to a BufferedStream that wraps a FileStream.

Example 4-19. Using a BufferedStream

string path = @»c:\temp\bufferedStream.txt»;

using (FileStream fileStream = File.Create(path))

{

using (BufferedStream bufferedStream = new BufferedStream(fileStream))

{

using (StreamWriter streamWriter = new StreamWriter(bufferedStream))

{

streamWriter.WriteLine(«A line of text.»);

}

}

}

The file system is not just for you

In this chapter, you have already seen on multiple occasions that a check is performed to determine whether a file exists on disk. You also created files, opened them, and moved some data in and out. But is it true that if File.Exists returns false, you can safely assume the file is not there when you want to create it?

No, because you are not the only user accessing the file system. While you are working with the file system, some other users are doing the exact same thing. Maybe they remove the folder you wanted to use to create a new file. Or they suddenly change the permissions on a file so you can’t access it any more.

Normally, when dealing with a situation in which multiple users access shared resources, we start using a locking mechanism to synchronize resource usage. C# has a locking mechanism that you can use to synchronize access to code when multiple threads are involved. This ensures that a certain piece of code cannot be executed simultaneously at the same moment in time.

MORE INFO: LOCKING

For more infomation on how to synchronize access to resources by using a locking mechanism, see Chapter 1.

However, the file system does not have these locking mechanisms. It is a multithreaded system, but without any of the safety regulations that you want to see.

Take the code in Example 4-20, which determines whether a file exists and then reads all text in it.

Example 4-20. Depending on File.Exists when reading file content

private static string ReadAllText()

{

string path = @"C:\temp\test.txt";

if (File.Exists(path))

{

return File.ReadAllText(path);

}

return string.Empty;

}

Another user might remove the file between the call to Exists and ReadAllText, which would cause an exception to be thrown. You can, however, anticipate the exceptions that can be thrown and make sure that your application knows how to deal with them. See Example 4-21.

Example 4-21. Using exception handling when opening a file

string path = @"C:\temp\test.txt";

try

{

return File.ReadAllText(path);

}

catch (DirectoryNotFoundException) { }

catch (FileNotFoundException) { }

return string.Empty;

When working with the file system, you need to remind yourself that exceptions can and will occur. Good exception handling is important for a robust application that works with files.

MORE INFO: EXCEPTION HANDLING

For more information on how to handle exceptions and create a robust application, see Chapter 1.

Communicating over the network

The .NET Framework has support for enabling your applications to communicate across a network. The System.Net namespace defines a large number of classes that hide the complexity of executing network operations while providing an easy-to-use interface.

Of all those members, the ones that you will probably use the most are WebRequest and WebResponse. These classes are abstract base classes that offer support for communicating over a network. Specific implementations define the protocol to use for communication. For example, you can use HttpWebRequest and HttpWebResponse when using the HTTP protocol.

WebRequest and WebResponse

WebRequest and WebResponse form a pair of classes that you can use together to send a request for information and then receive the response with the data you requested.

A WebRequest is created by using a static Create method on the WebRequest class. The Create method inspects the address that you pass to it and then selects the correct protocol implementation. If you would pass the address http://www.microsoft.com to it, it would see that you are working with the HTTP protocol and it would return an HttpWebRequest. After creating the correct WebRequest, you can set other properties such as authentication or caching instructions.

When you are finished composing your request, you call the GetResponse method to execute the request and retrieve the response (see Example 4-22).

Example 4-22. Executing a web request

WebRequest request = WebRequest.Create("http://www.microsoft.com");

WebResponse response = request.GetResponse();

StreamReader responseStream = new StreamReader(response.GetResponseStream());

string responseText = responseStream.ReadToEnd();

Console.WriteLine(responseText); // Displays the HTML of the website

response.Close();

Implementing asynchronous I/O operations

When working with I/O, you often have to wait for an operation to finish. Maybe you have to wait before a file is read from your hard drive or before a network request returns from somewhere in the world.

All the code that you’ve seen until now in this chapter is called synchronous code. The code is executed line by line and waits till each method is finished. An innocent-looking line of code can suddenly take a whole lot of time while you are waiting for it to finish. This can have a severe impact on the user experience and the scalability of your application.

When you create a desktop application such as a Windows Presentation Foundation (WPF), WinForm, or Windows Store application, your application has one main thread that is responsible for updating the user interface. This thread is responsible for processing all user activity. If this thread is busy with something else, the application appears to be unresponsive. Most users will then try to close such an application because they think something is wrong. It’s important to execute long-running operations on another thread, which ensures that your application stays responsive while it’s executing the long-running operation in the background.

With a server application, things are a little different. When the user hits your ASP.NET web server, he has to wait before the request is finished. If the page takes a long time to load, the user perceives this as poor performance. What’s happening on the server is that a thread is picking up the request and starts creating the response for the user. The server starts collecting data, producing HTML, and performing all kinds of other actions. A server has a limited number of threads for handling incoming requests. When a thread is waiting for I/O to happen, it can’t work on any other requests. When all threads are busy, and a new request comes in, the new request is queued. This issue is called “thread pool starvation.” All threads are waiting for some I/O to happen with a CPU load of 0%, and the response times for new requests will go through the roof.

This is where async operations can be useful. When your application waits for some external response that’s not CPU-bound, you can make your code work asynchronously to remain responsive and to free the main thread for other requests.

Async code was already possible before C#5. You could use the Event-based Asynchronous Programming pattern to wire up your code to continue processing when an external request finishes. However, writing asynchronous code completely by hand and getting all the small details right is difficult, especially when you need exception handling. It’s no trivial task to make sure that your code handles all situations well.

Async/await

C#5 introduced the new async/await keywords. Async/await signal to the compiler that you want to execute some asynchronous code. The compiler then is responsible for turning your “synchronous-looking” code into a state machine that handles all possible situations.

I/O is one area of the .NET Framework that benefits greatly from the new support for async/await. When you look through the I/O methods in this chapter, you will see many methods have an async equivalent that returns Task or Task<T>.

It’s important to know that the static File class does not support real asynchronous I/O. If you call asynchronous methods, it fakes this by using another thread from the thread pool to work with the file. For real async I/O, you need to use the FileStream object and pass a true value for theuseAsync parameter.

Example 4-23 shows an example of how to write asynchronously to a file. The Write method is replaced by WriteAsync, the method is marked with the async modifier to signal to the compiler that you want some help on transforming your code, and finally you use the await keyword on the returned task.

Example 4-23. Writing asynchronously to a file

public async Task CreateAndWriteAsyncToFile()

{

using (FileStream stream = new FileStream("test.dat", FileMode.Create,

FileAccess.Write, FileShare.None, 4096, true))

{

byte[] data = new byte[100000];

new Random().NextBytes(data);

await stream.WriteAsync(data, 0, data.Length);

}

}

The returned Task object represents some ongoing work by encapsulating the state of the asynchronous operation. Eventually, the Task object returns the result of the operation or exceptions that were asynchronously raised.

In the case of Example 4-23, the method doesn’t have any value that’s returned to the caller. That’s why WriteAsync returns a regular Task object. When a method does have a return value, it returns Task<T>, where T is the type of the value that is returned.

In Example 4-24, the GetStringAsync method is used. This method returns Task<string>, which means that eventually when the process is finished, a string value is available (or an exception).

Example 4-24. Executing an asynchronous HTTP request

public async Task ReadAsyncHttpRequest()

{

HttpClient client = new HttpClient();

string result = await client.GetStringAsync("http://www.microsoft.com");

}

Whenever the .NET Framework offers an async equivalent of a synchronous method, it is best to use it. It creates a better user experience and a more scalable application. However, when you are working on a performance-critical application, it pays to know what the compiler does for you. If you are uncertain, use a profiler to actually measure the difference so you can make an informed decision.

Running I/O operations in parallel

Real asynchronous I/O makes sure that your thread can do other work until the operating system notifies your application that the I/O is ready. Multiple async I/O operations can still be executed one after the other.

When you look at Example 4-25, you can see that multiple awaits are used in one method. With the current code, each web request starts when the previous one is finished. The thread won’t be blocked while running these requests, but the amount of time the method takes is the sum of the three web requests.

Example 4-25. Executing multiple awaits

public async Task ExecuteMultipleRequests()

{

HttpClient client = new HttpClient();

string microsoft= await client.GetStringAsync("http://www.microsoft.com");

string msdn = await client.GetStringAsync("http://msdn.microsoft.com");

string blogs = await client.GetStringAsync("http://blogs.msdn.com/");

}

You can also write code that will execute those operations in parallel. If you would execute those requests in parallel, you would only have to wait as long as the longest request takes (the other two will already be finished). You can do this by using the static method Task.WhenAll (see Example 4-26). As soon as you call GetStringAsync, the async operation gets started. However, you don’t immediately wait for the result. Instead, you let all three requests start and then you wait for them to finish. Now, all three operations run parallel, which can save you a lot of time.

Example 4-26. Executing multiple requests in parallel

public async Task ExecuteMultipleRequestsInParallel()

{

HttpClient client = new HttpClient();

Task microsoft = client.GetStringAsync("http://www.microsoft.com");

Task msdn = client.GetStringAsync("http://msdn.microsoft.com");

Task blogs = client.GetStringAsync("http://blogs.msdn.com/");

await Task.WhenAll(microsoft, msdn, blogs);

}

Using Tasks and async/await makes using asynchronous code a lot easier. Whenever possible, determine whether an operation is taking a long time and is not CPU-bound. If true or likely to be true, running it asynchronously is a good idea.

THOUGHT EXPERIMENT

Building a File Explorer

You need to create a custom File Explorer that uses WPF for a customer. Other members of your team work on the user interface of the File Explorer. You are tasked with creating the code that handles all the I/O. The File Explorer should be an abstraction over the file system. It shouldn’t show any drives; instead, it should group files into categories that depend on the location and file type. The categories and locations are given to you by the customer. For example, you have a category “Administration” that contains Microsoft Office documents from multiple locations.

1. Which classes do you plan to use?

2. How will you filter the files by specific file types?

3. Do you need asynchronous code?

Objective summary

§ You can work with drives by using Drive and DriveInfo.

§ For folders, you can use Directory and DirectoryInfo.

§ File and FileInfo offer methods to work with files.

§ The static Path class can help you in creating and parsing file paths.

§ Streams are an abstract way of working with a series of bytes.

§ There are many Stream implementations for dealing with files, network operations, and any other types of I/O.

§ Remember that the file system can be accessed and changed by multiple users at the same time. You need to keep this in mind when creating reliable applications.

§ When performing network requests, you can use the WebRequest and WebResponse classes from the System.Net namespace.

§ Asynchronous I/O can help you create a better user experience and a more scalable application.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You are creating a new file to store some log data. Each time a new log entry is necessary, you write a string to the file. Which method do you use?

a. File.CreateText

b. FileInfo.Create

c. File.Create

d. File.AppendText

2. You have built a complex calculation algorithm. It takes quite some time to complete and you want to make sure that your application remains responsive. What do you do?

a. Use async/await.

b. Run the code synchronously.

c. Use Task.Run.

d. Use a BackgroundWorker.

3. You are writing an application that will be deployed to Western countries. It outputs user activity to a text file. Which encoding should you use?

a. UTF-8

b. UTF-7

c. ASCII

d. UTF-32

Objective 4.2: Consume data

Data management is one of the most important aspects of an application. Imagine that you can create only applications that store their data in memory. As soon as the user quits, all data is lost, and subsequent launches require reentering of all necessary data. Of course, this would be an impossible situation to work with, which is why the .NET Framework helps you store your data in a persistent way. This can be done in a database by directly using ADO.NET or the Entity Framework. You can also store and request data from an external web service and retrieve the response in JavaScript Object Notation (JSON) or Extensible Markup Language (XML). The .NET Framework helps you execute these requests and parse the returned data.

THIS OBJECTIVE COVERS HOW TO:

§ Retrieve and update data in a database.

§ Consume JSON and XML.

§ Access web services.

Working with a database

In the .NET Framework, you can find various types of data-related classes in the System.Data.dll. The data access code in the .NET Framework is under the umbrella of ADO.NET, which consists of two conceptual parts: connected and disconnected data.

When using the connected parts of ADO.NET, you explicitly connect to a database and use that as the underlying data store. You execute queries by using Structured Query Language (SQL) to create, read, update, and delete data (commonly known as CRUD operations). Those queries are used by the ADO.NET infrastructure and forwarded to your database of choice. When working in the connected fashion, you use, among others, Connection objects, Command objects, and DataReader objects.

In the disconnected world, you work with DataSets and DataTables that mimic the structure of a relational database in memory. You can use them to work with offline data and later sync them to a database when you are online. A DataSet that is created after executing a query against a connected database can then be manipulated in memory, and the changes can be sent back to the data store by using a DataAdapter.

MORE INFO: DISCONNECTED DATA

For more information on working with data in a disconnected fashion, see http://msdn.microsoft.com/en-us/library/zb0sdh0b.aspx.

Providers

In the world around you, there are many different types of databases. You can use Microsoft SQL Server, Oracle, and MySQL, among others. The .NET Framework offers features for working with data in all these types of databases in a standard way.

A .NET Framework data provider is used for connecting to a database, executing commands, and working the resulting data. The .NET Framework data providers provide a thin layer that integrates with a specific database, so you can create programs that can work with different types of databases without having to change any code.

Connecting to a database

When you are working with data in a database, the first step you have to take is create a connection to the specific database. A connection is established by using a connection string, which contains all the information that the .NET Framework needs to know: the type of database, the location of the database, and how to authenticate against the database.

Connections in the .NET Framework all inherit from the DbConnection base class. The SqlConnection can be used to establish a connection to a Microsoft SQL Server database. Because the DbConnection class uses a real, unmanaged database connection, it’s important to make sure that you properly close the connection when you’re finished with it. Because of this, the DbConnection class implements IDisposable, so you can deterministically close the connection and free any associated unmanaged objects. As discussed in Chapter 2, a using clause will call the DbConnectionsDispose method, which automatically closes the connection. Example 4-27 shows how to do this.

Example 4-27. A SqlConnection with a using statement to automatically close it

using (SqlConnection connection = new SqlConnection(connectionString))

{

connection.Open();

// Execute operations against the database

} // Connection is automatically closed.

A typical connection string might look like the following example:

"Persist Security Info=False;Integrated Security=true;Initial Catalog=Northwind;server=(local)"

This connection string describes where the database is located, how it’s named, and some details about how to authenticate against the database. The basic format of a connection string is a series of key/value pairs connected by an equal sign (=), all separated by semicolons (;).

MORE INFO: CONNECTION STRINGS

If you are looking for specific information for creating your own connection string, you can look at http://www.connectionstrings.com/. Here you can find examples of connection strings for different providers with different settings.

When you need to build a connection string dynamically, you can use one of the several DbConnectionStringBuilder classes. For example, you can use OracleConnectionStringBuilder to build a connection string for an Oracle database and SqlConnectionStringBuilder for a Microsoft SQL Server database. Example 4-28 shows how to use the SqlConnectionStringBuilder to create a new connection string programmatically.

Example 4-28. Creating a connection string with SqlConnectionStringBuilder

var sqlConnectionStringBuilder = new SqlConnectionStringBuilder();

sqlConnectionStringBuilder.DataSource = @"(localdb)\v11.0";

sqlConnectionStringBuilder.InitialCatalog = "ProgrammingInCSharp";

string connectionString = sqlConnectionStringBuilder.ToString();

Hard-coding a connection string is usually not the best way to configure an application. When you deploy an application to a testing, staging, or production environment, the connection string often has to change, and most of the time this is done by an IT professional who doesn’t have (and doesn’t want to have!) any programming experience.

Because of this, you can easily store connection strings in an outside configuration file. Depending on the type of application, you use the app.config or web.config file. These files can be used to store all kinds of configuration settings for an application. You can also use other configuration files, which you reference from these files.

An example of a connection string in the app.config file of an application is shown in Example 4-29.

Example 4-29. Putting a connection string in the app.config file

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<connectionStrings>

<add name="ProgrammingInCSharpConnection"

providerName="System.Data.SqlClient"

connectionString="Data Source=(localdb)\v11.0;Initial Catalog=ProgrammingInCSharp;"

/>

</connectionStrings>

</configuration>

If you want to use the connection string in your application, you can use the ConfigurationManager.ConnectionStrings property from the System.Configuration.dll. You can access connection strings both by index and by name. Example 4-30 shows how to access a connection string by name and use it to open a database connection.

Example 4-30. Using a connection string from an external configuration file

string connectionString = ConfigurationManager.

ConnectionStrings["ProgrammingInCSharpConnection"].ConnectionString;

using (SqlConnection connection = new SqlConnection(connectionString))

{

connection.Open();

}

MORE INFO: CONNECTION STRINGS FOR DIFFERENT CONFIGURATIONS

When you want to have different connection strings for different configurations (such as staging, testing, and production) you can use a special transformation syntax that changes your configuration file depending on the configuration you want to build. For more information see http://msdn.microsoft.com/en-us/library/dd465326.aspx.

Connecting to a database is a time-consuming operation. Having a connection open for too long is also a problem because it can lead to other users not being able to connect. To minimize the costs of repeatedly opening and closing connections, ADO.NET applies an optimization that’s calledconnection pooling.

When using SQL Server, a pool of connections is maintained by your application. When a new connection is requested, the .NET Framework checks to see whether there is an open connection in the pool. If there is one, it doesn’t have to open a new connection and do all the initial setup steps. By default, connection pooling is enabled, which can give you a huge performance improvement.

Reading data

After the connection is established, you can start sending queries to the database. Queries are constructed using SQL, which is a special syntax optimized for working with databases.

MORE INFO: SQL

For more information on SQL, see http://msdn.microsoft.com/en-us/library/bb510741.aspx.

Let’s say you are working with a database table similar to the one in Table 4-2.

Table 4-2. Example table structure

Column name

Data type

Nullable

id

int, Primary Key

False

FirstName

varchar(30)

False

MiddleName

varchar(30)

True

LastName

varchar(30)

False

You can create this table with the SQL from Example 4-31.

Example 4-31. Using SQL script to create a table

CREATE TABLE dbo.People

(

[id] INT NOT NULL IDENTITY,

[FirstName] VARCHAR(30) NOT NULL,

[MiddleName] VARCHAR(30) NULL,

[LastName] VARCHAR(30) NOT NULL

);

If you want to select a couple of rows from this table, you first need to define a SQL statement. A SQL query can then be executed by using a SqlCommand object. A SqlCommand can return a SqlDataReader that keeps track of where you are in the result set. It gives you access to each row and the columns it contains.

Example 4-32 shows how you can use this to loop through the rows in the People table and display the results. ADO.NET also supports the new async/await keywords so you can execute queries asynchronously.

Example 4-32. Executing a SQL select command

public async Task SelectDataFromTable()

{

string connectionString = ConfigurationManager.

ConnectionStrings["ProgrammingInCSharpConnection"].ConnectionString;

using (SqlConnection connection = new SqlConnection(connectionString))

{

SqlCommand command = new SqlCommand("SELECT * FROM People", connection);

await connection.OpenAsync();

SqlDataReader dataReader = await command.ExecuteReaderAsync();

while (await dataReader.ReadAsync())

{

string formatStringWithMiddleName = "Person ({0}) is named {1} {2} {3}";

string formatStringWithoutMiddleName = "Person ({0}) is named {1} {3}";

if ((dataReader["middlename"] == null))

{

Console.WriteLine(formatStringWithoutMiddleName,

dataReader["id"],

dataReader["firstname"],

dataReader["lastname"]);

}

else

{

Console.WriteLine(formatStringWithMiddleName,

dataReader["id"],

dataReader["firstname"],

dataReader["middlename"],

dataReader["lastname"]);

}

}

dataReader.Close();

}

}

A SqlDataReader is a forward-only stream of rows. You can’t go back while you’re reading; only forward. You can access the columns of a resulting row both by index and by name. The SqlDataReader offers a couple of methods that can map the value of a column to the corresponding CLRtype. For example, you can call methods like GetInt32(int index), GetGuid(int index), GetString(int index), and so on.

When executing a query, you can also batch multiple SQL statements together. The SqlDataReader returned contains multiple result sets. You can advance to the next result set by calling NextResult or NextResultAsync on your SqlDataReader object. Example 4-33 shows how to execute a query that runs two result sets.

Example 4-33. Executing a SQL query with multiple result sets

public async Task SelectMultipleResultSets()

{

string connectionString = ConfigurationManager.

ConnectionStrings["ProgrammingInCSharpConnection"].ConnectionString;

using (SqlConnection connection = new SqlConnection(connectionString))

{

SqlCommand command = new SqlCommand("SELECT * FROM People;

SELECT TOP 1 * FROM People ORDER BY LastName", connection);

await connection.OpenAsync();

SqlDataReader dataReader = await command.ExecuteReaderAsync();

await ReadQueryResults(dataReader);

await dataReader.NextResultAsync(); // Move to the next result set

await ReadQueryResults(dataReader);

dataReader.Close();

}

}

private static async Task ReadQueryResults(SqlDataReader dataReader)

{

while (await dataReader.ReadAsync())

{

string formatStringWithMiddleName = "Person ({0}) is named {1} {2} {3}";

string formatStringWithoutMiddleName = "Person ({0}) is named {1} {3}";

if ((dataReader["middlename"] == null))

{

Console.WriteLine(formatStringWithoutMiddleName,

dataReader["id"],

dataReader["firstname"],

dataReader["lastname"]);

}

else

{

Console.WriteLine(formatStringWithMiddleName,

dataReader["id"],

dataReader["firstname"],

dataReader["middlename"],

dataReader["lastname"]);

}

}

}

Updating data

Besides selecting data and getting it to the client, you can also update data in the database. You can create new records, update existing ones, or completely remove a record.

You can do it all by using a connection and a command object. But instead of getting back a reader with the resulting rows, you get an integer value back that shows how many rows are affected by your last query. This means that if you execute a stored procedure or chain multiple SQL statements together you will only get the number of records affected by the last query, not across all queries in your procedure. By checking the result you can determine if the correct amount of rows was affected. You use the ExecuteNonQuery or ExecuteNonQuery-Async method to do this, as shown in Example 4-34.

Example 4-34. Updating rows with ExecuteNonQuery

public async Task UpdateRows()

{

string connectionString = ConfigurationManager.

ConnectionStrings["ProgrammingInCSharpConnection"].ConnectionString;

using (SqlConnection connection = new SqlConnection(connectionString))

{

SqlCommand command = new SqlCommand(

"UPDATE People SET FirstName='John'",

connection);

await connection.OpenAsync();

int numberOfUpdatedRows = await command.ExecuteNonQueryAsync();

Console.WriteLine("Updated {0} rows", numberOfUpdatedRows);

}

}

Using parameters

Until now, you have seen only queries that were already completely constructed at compile time. In the real world, however, you often need to use dynamic values when executing a query.

Because a SQL query is nothing more than a simple string, you might be tempted to concatenate multiple strings together to create your query. However, be aware that this is a huge security risk. For example, let’s say that you have a form in which people can fill in their names that you will insert into the People table. Your query will look something like this:

INSERT INTO People VALUES('John', 'Doe', null)

Now, you could read the values that a user enters and then manually construct this string. But what would happen if a user enters the following for a middle name?

'); DELETE FROM People; --

After executing this query, all data in your People table would be deleted. This security hole is known as SQL injection.

MORE INFO: SQL INJECTION

For more information on SQL injection, see http://msdn.microsoft.com/en-us/library/ms161953(v=sql.105).aspx.

To guard against SQL injection, you should never directly use user input in your SQL strings. Instead of manually building the correct SQL query, you can use parameterized SQL statements (see Example 4-35).

Example 4-35. Inserting values with a parameterized query

public async Task InsertRowWithParameterizedQuery()

{

string connectionString = ConfigurationManager.

ConnectionStrings["ProgrammingInCSharpConnection"].ConnectionString;

using (SqlConnection connection = new SqlConnection(connectionString))

{

SqlCommand command = new SqlCommand(

"INSERT INTO People([FirstName], [LastName], [MiddleName]) VALUES(@

firstName, @lastName, @middleName)",

connection);

await connection.OpenAsync();

command.Parameters.AddWithValue("@firstName", "John");

command.Parameters.AddWithValue("@lastName", "Doe");

command.Parameters.AddWithValue("@middleName", "Little");

int numberOfInsertedRows = await command.ExecuteNonQueryAsync();

Console.WriteLine("Inserted {0} rows", numberOfInsertedRows);

}

}

These parameterized queries can be used when running select, update, insert, and delete queries. Besides being much more secure, they also offer better performance. Because the database gets a more generic query, it can more easily find a precompiled execution plan to execute your query.

MORE INFO: EXECUTION PLANS

For more information about execution plans, see http://msdn.microsoft.com/en-us/library/ms175580.aspx.

Using transactions

When working with a database, things can go wrong. Maybe you are executing multiple queries that should be grouped together. If the last one fails, the previous ones are already executed, and suddenly your data can go corrupt.

Because of this, .NET Framework helps you with transactions. A transaction has four key properties that are referred to as ACID:

§ Atomicity. All operations are grouped together. If one fails, they all fail.

§ Consistency. Transactions bring the database from one valid state to another.

§ Isolation. Transactions can operate independently of each other. Multiple concurrent transactions won’t influence each other. It will be as if they were executed serially.

§ Durability. The result of a committed transaction is always stored permanently, even if the database crashes immediately thereafter.

When working with transactions, it’s easiest to use the TransactionScope class. TransactionScope offers an easy way to work with transactions without requiring you to interact with the transaction itself (see Example 4-36).

Example 4-36. Using a TransactionScope

string connectionString = ConfigurationManager.

ConnectionStrings["ProgrammingInCSharpConnection"].ConnectionString;

using (TransactionScope transactionScope = new TransactionScope())

{

using (SqlConnection connection = new SqlConnection(connectionString))

{

connection.Open();

SqlCommand command1 = new SqlCommand(

"INSERT INTO People ([FirstName], [LastName], [MiddleInitial])

VALUES('John', 'Doe', null)",

connection);

SqlCommand command2 = new SqlCommand(

"INSERT INTO People ([FirstName], [LastName], [MiddleInitial])

VALUES('Jane', 'Doe', null)",

connection);

command1.ExecuteNonQuery();

command2.ExecuteNonQuery();

}

transactionScope.Complete();

}

If an exception occurs inside the TransactionScope, the whole transaction is rolled back. If nothing goes wrong, you use TransactionScope.Complete to complete the transaction. It’s important to use the TransactionScope inside a using statement so that it is automatically disposed of when it’s no longer necessary.

A TransactionScope can also be constructed by passing a TransactionScopeOption enum to it. By using this enum, you can define the behavior of the TransactionScope.

A TransactionScope can be constructed with three options:

§ Required. Join the ambient transaction or create a new one if it doesn’t exist. If there is an ambient transaction, that transaction controls when the transaction is completed. This is the default scope.

§ RequiresNew. Start a new transaction.

§ Suppress. Don’t take part in any transaction.

When using TransactionScope, the .NET Framework automatically manages the transaction for you. If your transaction uses nested connections, multiple databases, or multiple resource types, your transaction is promoted to a distributed transaction. The promotion to a distributed transaction can have a huge performance hit if it’s not necessary. If possible, try to avoid distributed transactions. If you need them, .NET Framework manages it for you.

Using an Object Relational Mapper (ORM)

One of the problems with a relational database is that it’s inherently different from the object-oriented structure that you want to use in your applications. You use objects that have relationship to other objects or that inherit from them. The database uses tables with columns and foreign keys to other tables. This is what’s called the object-relational impedance mismatch.

You can create complex applications by manually writing your SQL queries and executing them against the database. But when your application grows, and you want to use an object-oriented design, you start running into problems. Of course, you can map the results of your query to objects and create queries from the changes to your objects, but this is a difficult task. Even if you like the challenge, this is not something that will add value for your customers.

This is where Object Relational Mapping (ORM) software comes into play. One such ORM is Microsoft’s open source Entity Framework, which lessens the burden of the tedious work of mapping your objects, constructing queries, and keeping track of all the changes.

The Entity Framework can be used with a few different approaches:

§ Database First. You want to map an existing database to your object structure.

§ Model First. You want to design your object model with a graphical designer and then create a database that supports it.

§ Code First. You create your object model in code and then generate a database that can store your data.

Using an ORM is a compromise between performance and development speed. You can imagine how the Entity Framework uses reflection to map from SQL query results to objects, which is always slower than doing it by hand. However, using the Entity Framework will increase your development speed immensely. It enables you to create your application quickly by prioritizing the parts of it that need better performance. The Entity Framework is typically good enough for most situations. If not, you can add a simple stored procedure for the queries that are problematic.

Imagine you have a Person class, such as the one shown in Example 4-37.

Example 4-37. A simple Person class

public class Person

{

public int Id { get; set; }

public string Name { get; set; }

}

If you want to use Code First to map this entity to a database, you just need to inherit from DbContext (a base class that offers access to the Entity Framework) with your own custom context and define the sets of entities you want to use. If necessary, you can then modify the custom mapping to support complex types, inheritance, or other mapping scenarios. Example 4-38 shows how this can work in a simple application.

Example 4-38. Using Code First to map a class to the database

public class PeopleContext : DbContext

{

public IDbSet<Person> People { get; set; }

}

using (PeopleContext ctx = new PeopleContext())

{

ctx.People.Add(new Person() { Id = 1, Name = "John Doe" });

ctx.SaveChanges();

}

using (PeopleContext ctx = new PeopleContext())

{

Person person = ctx.People.SingleOrDefault(p => p.Id == 1);

Console.WriteLine(person.Name);

}

The default conventions for the Entity Framework immediately notice that there should be one table for storing people. The id property is used as primary key, and the Name property is mapped to another column. DbContext keeps track of all changes you make to your entities and generates update queries for you. The Entity Framework is growing fast and becomes better with each release. If you use a database in your application, using the Entity Framework properly can really improve your development efforts.

MORE INFO: ENTITY FRAMEWORK

For more information on the Entity Framework, see http://msdn.microsoft.com/en-us/data/ef.aspx.

Using web services

Another option beyond storing data in a relational database or a file is to use an external service to store data. It can be a web service that you created or it can come from a third party.

With such services, you can exchange data between applications in a loosely coupled way. You only need to know the service address and how to make a request to that service. How the service gets its data is completely hidden from you, and you generally don’t have to worry about it.

The .NET Framework has solid support for creating web services. You can build highly flexible web services by using another Microsoft technology called the Windows Communication Foundation (WCF).

Creating a WCF service

In the past, you had many options when building a service. You could use .NET Remoting, XML Web Services (including SOAP-based services), or Microsoft Message Queues, all with their own options and features. WCF replaces all these technologies with a single, unified, extendable programming model that encompasses the technologies of the past.

A WCF service looks like a regular class. It can have methods with return types, constructors, and other members. The only difference with a regular class are the attributes on the class itself and on its methods that inform the .NET Framework you want to expose your class as a service.

Example 4-39 shows an example of a simple WCF service. As you can see, the class is decorated with the ServiceContract attribute and the method with an OperationContract attribute.

Example 4-39. A simple WCF web service

[ServiceContract]

public class MyService

{

[OperationContract]

public string DoWork(string left, string right)

{

return left + right;

}

}

You can create this service by creating a new ASP.NET project and adding a WCF service to it. A WCF service consists of both an .svc file and a code-behind file that contains the actual service code. The .svc file contains instructions for how to host your service in Internet Information Services (IIS), so you can put your .svc file with the associated code file in a website hosted by IIS to make it available to your users.

The code file doesn’t contain any information about how the service can be called—it doesn’t specify an address or a protocol. This is where WCF is superior to previous technologies because it uses the so called ABC model:

§ Address

§ Binding

§ Contract

When building a WCF service, you usually start with the contract, which defines which operations your service exposes. The contract is what the outside world expects of your service. After specifying your contract, you specify the bindings. A binding configures the protocols and transports that can be used to call your service. Maybe your service can be used over HTTP, HTTPS, and a named-pipe connection. You next need to specify the address, which is the endpoint that your service exposes. Doing this ensures that there is a physical network address that can be used to call your service with a specific binding.

When working with a previously created WCF service, you can use Visual Studio to create a service reference for you. It creates a proxy class in your client project that enables you to access the external service. You can see an example of a proxy class for the MyService service in Example 4-40.

Example 4-40. A generated WCF proxy client

namespace Service.Client.ExternalService

{

[System.CodeDom.Compiler.GeneratedCodeAttribute(«System.ServiceModel», «4.0.0.0»)]

[System.ServiceModel.ServiceContractAttribute(

ConfigurationName = «ExternalService.MyService»)]

public interface MyService

{

[System.ServiceModel.OperationContractAttribute(

Action = "http://tempuri.org/MyService/DoWork",

ReplyAction = «http://tempuri.org/MyService/DoWorkResponse»)]

string DoWork(string left, string right);

[System.ServiceModel.OperationContractAttribute(

Action = "http://tempuri.org/MyService/DoWork",

ReplyAction = «http://tempuri.org/MyService/DoWorkResponse»)]

System.Threading.Tasks.Task<string> DoWorkAsync(string left, string right);

}

[System.CodeDom.Compiler.GeneratedCodeAttribute(«System.ServiceModel», «4.0.0.0»)]

public interface MyServiceChannel : Service.Client.ExternalService.MyService,

System.ServiceModel.IClientChannel

{ }

[System.Diagnostics.DebuggerStepThroughAttribute()]

[System.CodeDom.Compiler.GeneratedCodeAttribute(«System.ServiceModel», «4.0.0.0»)]

public partial class MyServiceClient :

System.ServiceModel.ClientBase<Service.Client.ExternalService.MyService>,

Service.Client.ExternalService.MyService

{

public MyServiceClient()

{ }

public MyServiceClient(string endpointConfigurationName) :

base(endpointConfigurationName)

{ }

public MyServiceClient(string endpointConfigurationName, string remoteAddress) :

base(endpointConfigurationName, remoteAddress)

{ }

public MyServiceClient(string endpointConfigurationName,

System.ServiceModel.EndpointAddress remoteAddress) :

base(endpointConfigurationName, remoteAddress)

{ }

public MyServiceClient(System.ServiceModel.Channels.Binding binding,

System.ServiceModel.EndpointAddress remoteAddress) :

base(binding, remoteAddress)

{ }

public string DoWork(string left, string right)

{

return base.Channel.DoWork(left, right);

}

public System.Threading.Tasks.Task<string> DoWorkAsync(

string left,

string right)

{

return base.Channel.DoWorkAsync(left, right);

}

}

}

You can use this client just like every regular class. The only difference is that behind the scenes, the proxy client uses the configuration settings from the app.config file to look for the ABC settings. Example 4-41 shows how to construct the client and call a method on it.

Example 4-41. Using a WCF proxy client

ExternalService.MyServiceClient client = new ExternalService.MyServiceClient();

string result = client.DoWork("John", "Doe");

Console.WriteLine(result); // Displays JohnDoe

By using a WCF service with a client proxy, the result from the DoWork method is automatically converted to a string. However, when working with other services, the result you get back might be in an entirely different format.

MORE INFO: WCF

If you want to know more about WCF and how you can use it in a variety of scenarios, see http://msdn.microsoft.com/en-us/library/ms731082.aspx.

Consuming XML

Extensible Markup Language (XML) is a markup language that consists of a set of rules that define how a document should be formatted. The advantage of XML is that it can be read by both humans and computer programs. It was originally created to be the one and only solution for communication over the Internet between different applications.

Example 4-42 shows an example XML document.

Example 4-42. XML file

<?xml version="1.0" encoding="utf-8" ?>

<people>

<person firstName="John" lastName="Doe">

<contactdetails>

<emailaddress>john@unknown.com</emailaddress>

</contactdetails>

</person>

<person firstName="Jane" lastName="Doe">

<contactdetails>

<emailaddress>jane@unknown.com</emailaddress>

<phonenumber>001122334455</phonenumber>

</contactdetails>

</person>

</people>

As you can see, an XML document consists of several elements. First, there is an optional line that specifies you are looking at XML. This line is called the prolog. It tells something about the encoding that is used and can contain other metadata.

After the prolog comes the content. There should be a single root element that contains the rest of the information. This way, you create a hierarchical tree that defines the relationship between all elements.

Underneath the root element are other child elements. A child element can be a single element that describes some characteristic or several elements that group other elements. Elements can contain attributes, which are name-value pairs associated with an element. An XML document can also contain comments and special processing instructions. The special processing instructions are often contained in a Document Type Definition (DTD) file that is stored externally to the XML and referenced in the prolog.

MORE INFO: XML

For more information on XML and its many capabilities, see http://msdn.microsoft.com/en-us/library/ms256177.aspx.

XML in the .NET Framework

Parsing an XML file as if it were a regular text file is a lot of work. The .NET Framework helps you out by providing classes that can be used to parse, create, and edit XML files—both in memory and on disk.

These classes are found in the System.Xml namespace. The important classes are listed in Table 4-3.

Table 4-3. Classes for working with XML

Name

Description

XmlReader

A fast way of reading an XML file. You can move forward only through the file, and nothing is cached.

XmlWriter

A fast way to create an XML file. Just as with the XmlReader, it’s forward only and noncached.

XmlDocument

Represents an in-memory XML document. It supports navigating and editing a document.

XPathNavigator

Helps with navigating through an XML document to find specific information.

MORE INFO: LINQ TO XML

Another way to work with XML is using LINQ to XML. More info can be found in Objective 4.3, “Query and manipulate data and objects by using LINQ,” later in this chapter.

XmlReader

The XmlReader class offers the fastest option of working with XML data. It is an abstract base class that is inherited from by classes such as XmlTextReader, XmlNodeReader, and XmlValidatingReader.

You create a new instance of XmlReader by using the static Create method. You can pass this method an instance of XmlReaderSettings to configure how the XML should be parsed. This way, you can choose to skip data from your XML file such as white space and comments, or start at a particular position. Example 4-43 shows an example of how to use an XmlReader to parse a string that contains the XML data of Example 4-42.

Example 4-43. Parsing an XML file with an XmlReader

string xml = @"<?xml version=""1.0"" encoding=""utf-8"" ?>

<people>

<person firstname=""john"" lastname=""doe"">

<contactdetails>

<emailaddress>john@unknown.com</emailaddress>

</contactdetails>

</person>

<person firstname=""jane"" lastname=""doe"">

<contactdetails>

<emailaddress>jane@unknown.com</emailaddress>

<phonenumber>001122334455</phonenumber>

</contactdetails>

</person>

</people>";

using (StringReader stringReader = new StringReader(xml))

{

using (XmlReader xmlReader = XmlReader.Create(stringReader,

new XmlReaderSettings() { IgnoreWhitespace = true }))

{

xmlReader.MoveToContent();

xmlReader.ReadStartElement("People");

string firstName = xmlReader.GetAttribute("firstName");

string lastName = xmlReader.GetAttribute("lastName");

Console.WriteLine("Person: {0} {1}", firstName, lastName);

xmlReader.ReadStartElement("Person");

Console.WriteLine("ContactDetails");

xmlReader.ReadStartElement("ContactDetails");

string emailAddress = xmlReader.ReadString();

Console.WriteLine("Email address: {0}", emailAddress);

}

}

As you can see, the XmlReader treats the XML like a hierarchy of nodes. This is easier than working with XML as if it were a flat file. By moving to nodes, child nodes, and attributes, you can parse the document for the content you need.

XmlWriter

When you want to create an XML file you can use the XmlWriter class. This class is created by using the static Create method and it can be configured by using an instance of the XmlWriterSettings class. Example 4-44 shows how to create a new XML file. As you can see, you have methods for writing the individual elements and attributes.

Example 4-44. Creating an XML file with XmlWriter

StringWriter stream = new StringWriter();

using (XmlWriter writer = XmlWriter.Create(

stream,

new XmlWriterSettings() { Indent = true }))

{

writer.WriteStartDocument();

writer.WriteStartElement("People");

writer.WriteStartElement("Person");

writer.WriteAttributeString("firstName", "John");

writer.WriteAttributeString("lastName", "Doe");

writer.WriteStartElement("ContactDetails");

writer.WriteElementString("EmailAddress", "john@unknown.com");

writer.WriteEndElement();

writer.WriteEndElement();

writer.Flush();

}

Console.WriteLine(stream.ToString());

Just as with the XmlReader class, the XmlWriter class is aware of the hierarchical structure of XML. However, you need to make sure that you write both the start and the end tag of each element. You can choose to add attributes or to add elements that have a string value as their content.

XmlDocument

Although XmlReader and XmlWriter are the fastest options, they are definitely not the easiest to use. When you work with relatively small documents, and performance is not as important, you can use the XmlDocument class. Its primary function enables you to edit XML files, and it represents the XML in a hierarchical way in memory, and enables you to easily navigate the document and edit elements in place. After you finish editing the document, you can save it back to a file or stream.

An XmlDocument uses a set of XmlNode objects to represent the various elements comprising the document. XmlDocument inherits from XmlNode and adds specific capabilities for loading and saving documents. XmlNode helps you with reading content and attributes, and gives you methods for adding child nodes so that you can easily structure your document. Example 4-45 shows an example in which both the reading and editing capabilities of XmlDocument are used on the XML found in Example 4-43.

Example 4-45. Using XmlDocument

XmlDocument doc = new XmlDocument();

doc.LoadXml(xml);

XmlNodeList nodes = doc.GetElementsByTagName("Person");

// Output the names of the people in the document

foreach (XmlNode node in nodes)

{

string firstName = node.Attributes["firstName"].Value;

string lastName = node.Attributes["lastName"].Value;

Console.WriteLine("Name: {0} {1}", firstName, lastName);

}

// Start creating a new node

XmlNode newNode = doc.CreateNode(XmlNodeType.Element, "Person", "");

XmlAttribute firstNameAttribute = doc.CreateAttribute("firstName");

firstNameAttribute.Value = "Foo";

XmlAttribute lastNameAttribute = doc.CreateAttribute("lastName");

lastNameAttribute.Value = "Bar";

newNode.Attributes.Append(firstNameAttribute);

newNode.Attributes.Append(lastNameAttribute);

doc.DocumentElement.AppendChild(newNode);

Console.WriteLine("Modified xml...");

doc.Save(Console.Out);

//Displays:

//Name: john doe

//Name: jane doe

//Modified xml...

//<?xml version="1.0" encoding="ibm850"?>

//<people>

// <person firstname="john" lastname="doe">

// <contactdetails>

// <emailaddress>john@unknown.com</emailaddress>

// </contactdetails>

// </person>

// <person firstname="jane" lastname="doe">

// <contactdetails>

// <emailaddress>jane@unknown.com</emailaddress>

// <phonenumber>001122334455</phonenumber>

// </contactdetails>

// </person>

// <person firstname="Foo" lastname="Bar" />

//</people>

One nifty feature for navigating through a document is XPath, a kind of query language for XML documents. XmlDocument implements IXPathNavigable so you can retrieve an XPathNavigator object from it. The XPathNavigator offers an easy way to navigate through an XML document. You can use methods similar to those in an XmlDocument to move from one node to another, or you can use an XPath query. This enables you to select elements or attributes with certain values, similar to the way SQL selects data in a database. Example 4-46 shows an example of how to use an XPath query to select a Person by name.

Example 4-46. Using an XPath query

XmlDocument doc = new XmlDocument();

doc.LoadXml(xml); // Can be found in Listing 4-43

XPathNavigator nav = doc.CreateNavigator();

string query = "//People/Person[@firstName='Jane']";

XPathNodeIterator iterator = nav.Select(query);

Console.WriteLine(iterator.Count); // Displays 1

while(iterator.MoveNext())

{

string firstName = iterator.Current.GetAttribute("firstName","");

string lastName = iterator.Current.GetAttribute("lastName","");

Console.WriteLine("Name: {0} {1}", firstName, lastName);

}

Consuming JSON

Another popular format used by many web services is JavaScript Object Notation (JSON). Although XML is useful, it is verbose and has many rules regarding a document’s structure. JSON is what’s called the “fat-free” alternative to XML. It has an easier grammar and often carries significantly less weight. The example in Example 4-47 shows how the People XML from the previous examples could be represented using JSON.

Example 4-47. A sample JSON file

{

"People": {

"Person": [

{

"firstName": "John",

"lastName": "Doe",

"ContactDetails": { "EmailAddress": "john@unknown.com" }

},

{

"firstName": "Jane",

"lastName": "Doe",

"ContactDetails": {

"EmailAddress": "jane@unknown.com",

"PhoneNumber": "001122334455"

}

}

]

}

}

When working with XML, you use classes such as XmlWriter, XmlReader, and XmlDocument; JSON does not have classes like them. Normally, when working with JSON, you use a serialization library that helps you convert objects to JSON, and vice versa. One such popular library is Newtonsoft.Json, which is available at http://json.codeplex.com/.

JSON is most often used in asynchronous JavaScript and XML (AJAX) scenarios. In reality, AJAX should be called AJAJ because the XML is swapped for JSON now. AJAX is a technology that allows a website to execute background calls to a web server. This is the technique that enables a website to update itself without doing a full page refresh. JSON is particularly useful for this because all JavaScript engines can parse a JSON string into an object by a relatively simple command.

MORE INFO: SERIALIZING JSON

For more info on serializing JSON, see Objective 4.4, “Serialize and deserialize data,” later in this chapter.

THOUGHT EXPERIMENT

Exchanging data

You are designing a new application that stores data about weather conditions throughout the world. Your business model relies on selling this data to customers so they can use it in their own applications. You use a relational database to store your data, and you access your data through web services that can be accessed by authorized users. You also create regular XML dumps for users that want a local copy of the data.

1. What are the advantages and disadvantages of using the Entity Framework?

2. Which techniques do you plan to use for your web services?

3. How will you expose your data as XML?

Objective summary

§ ADO.NET uses a provider model that enables you to connect to different types of databases.

§ You use a DbConnection object to create a connection to a database.

§ You can execute queries that create, update, read, and delete (CRUD) data from a database.

§ When creating queries it’s important to use parameterized queries so you avoid SQL injection.

§ You can consume a web service from your application by creating a proxy for it.

§ You can work with XML by using the XmlReader, XmlWriter, XPathNavigator, and XmlDocument classes.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You want to update a specific row in the database. Which objects should you use? (Choose all that apply.)

a. SqlCommand

b. SqlDataReader

c. SqlConnection

d. TransactionScope

2. You are planning to build an application that will use an object-oriented design. It will be used by multiple users at the same time. Which technology should you use?

a. XML files

b. Entity Framework

c. ADO.NET

d. Web service

3. You need to process a large number of XML files in a scheduled service to extract some data. Which class should you use?

a. XmlReader

b. XmlDocument

c. XmlWriter

d. FileStream

Objective 4.3: Query and manipulate data and objects by using LINQ

In .NET 3.5, a new feature was added to C#: LINQ. LINQ enables you to work with data in an extremely powerful way. It consists of a set of standard, easily learned patterns that enable you to query data. The beautiful thing is that LINQ can be extended to work against different types of data. This enables you to write powerful queries that can be easily understood and maintained.

THIS OBJECTIVE COVERS HOW TO:

§ Use the standard LINQ operators.

§ Use LINQ in the most optimal way.

§ Use LINQ to XML.

Language features that make LINQ possible

To support LINQ, a few language features have been added to C#. These features are useful on their own, but combined together to form LINQ, they shine. Some of these language features help you to construct queries in a nice and elegant way, but other features are sometimes mandatory to even be able to create a query.

Those language constructs are as follows:

§ Implicitly typed variables

§ Object initialization syntax

§ Lambda expressions

§ Extension methods

§ Anonymous types

Implicitly typed variables

When working with C#, most of the time you use the static typing system of C#. This means that the compiler knows the type of a certain variable and that it sees whether you use it in the correct way.

For example, the following code will lead to a compile error:

int i = 42;

Stream m = new MemoryStream();

string s = i + m; // This line gives a compile error

What you are seeing here is called explicit typing. You tell the compiler explicitly what the type of each variable is. In C# 3, a new feature was added: implicit typing. When using implicit typing, the compiler infers the type of variable for you and then strongly types your variable to that type. Implicitly typed variables can be used only for local variables. You can use implicit typing by using the var keyword:

var i = 42;

var m = new MemoryStream();

//string s = i + m; // This line still gives a compile error

In this code, the compiler infers for you that the type of i is int, and m is MemoryStream. The types are still strongly typed, but you don’t specify the type yourself.

Why is this necessary in LINQ? Sometimes when using a LINQ query, the return type is determined at compile time, so you can’t specify the return explicitly because you don’t know it. You will see an example of this shortly when you get to your first LINQ query.

Besides being mandatory in these situations, implicit typing can also improve the readability of your code. If a user of your code can easily see the type of your variable, implicit typing can be used to avoid repetition. In the following case, var can improve readability:

Dictionary<string, IEnumerable<Tuple<Type, int>>> data =

new Dictionary<string, IEnumerable<Tuple<Type, int>>>();

var implicitData = new Dictionary<string, IEnumerable<Tuple<Type, int>>>();

In this case, you avoid the repetition of the large type declaration. A reader of your code will immediately see that the type of implicitData is a Dictionary. When the type of your variable is unclear, it’s better to avoid implicit typing:

var whatsMyType = GetData();

In this case, a reader of your code would have to inspect the GetData method or use IntelliSense in Visual Studio to see what the type of whatsMyType is.

Object initialization syntax

Before C# added object initializers, you had to split creating a new object and setting its properties, as shown in Example 4-48.

Example 4-48. Creating and initializing an object

public class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

}

// Create and initialize a new object

Person p = new Person();

p.FirstName = "John";

p.LastName = "Doe";

The new object initialization syntax enables you to combine creating a new object and setting its properties in one statement. Example 4-49 shows this new syntax.

Example 4-49. Using an object initializer

// Create and initialize a new object in one step

Person p = new Person

{

FirstName ="John",

LastName = "Doe"

};

Although not strictly necessary, it can improve the readability of your code when you use object initializers (see Example 4-50). The same syntax can be used when creating collections.

Example 4-50. Using a collection initializer

var people = new List<Person>

{

new Person

{

FirstName = "John",

LastName = "Doe"

},

new Person

{

FirstName = "Jane",

LastName = "Doe"

}

};

Most of the time, objection initialization syntax is some nice syntactic sugar, but when working with anonymous types it is actually required. By using the object initialization syntax, you define the properties that an anonymous type has.

Lambda expressions

To understand what a lambda expression is, it’s important that you first know what an anonymous method is. Anonymous methods were introduced in C# 2.0 to enable you to create a method inline in some code, assign it to a variable, and pass it around. Example 4-51 shows how to create an anonymous method.

Example 4-51. Using an anonymous method

Func<int, int> myDelegate =

delegate(int x)

{

return x * 2;

};

Console.WriteLine(myDelegate(21)); // Displays 42

The Func<T,T> is one of the built in types of the .NET Framework. It’s a shorthand notation for a delegate that takes an int and returns an int. You can also use Action<...> for specifying a delegate that doesn’t have a return value.

Lambdas introduce a shorthand notation for creating anonymous functions. The same code can be written by using a lambda:

Func<int, int> myDelegate = x => x * 2;

Console.WriteLine(myDelegate(21)); // Displays 42

As you can see, the result is the same, but the notation is a lot shorter. Lambdas use the special => notation, which you can read as “becomes” or “for which.” When working with LINQ, you often have to pass a Fun<> delegate to a method. By using lambdas, you can streamline your code and save yourself a lot of typing. The syntax gets a little more verbose when you work with multiple arguments or when your lambda contains multiple statements, but it’s still a lot more compact than when using an anonymous method.

Extension methods

Extension methods can be used to extend an existing type with new behavior without using inheritance. An extension method is defined in a static class and it uses the special this keyword to mark itself as an extension method. Example 4-52 shows how to create an extension method for int.

Example 4-52. Using an extension method

public static class IntExtensions

{

public static int Multiply(this int x, int y)

{

return x * y;

}

}

int x = 2;

Console.WriteLine(x.Multiply(3)); // Displays 6

LINQ is entirely based on extension methods. They are defined in the System.Linq.Enumerables class and they enable you to call LINQ functions on all enumerable types.

MORE INFO: EXTENSION METHODS

For more information on extension methods, see Chapter 2.

Anonymous types

When you create anonymous types, you use both object initializers and implicit typing. An anonymous type is a type that is shaped at compile time without having a formal class definition. The compiler helps you by creating a type for you and gives a default implementation by overriding the virtual members of System.Object.

You create an anonymous type by using the var keyword as the type and by using the new operator without specifying a type, as shown in Example 4-53.

Example 4-53. Creating an anonymous type

var person = new

{

FirstName = "John",

LastName = "Doe"

};

Console.WriteLine(person.GetType().Name); // Displays "<>f__AnonymousType0`2"

Here you see how object initializers can be used to define the properties of an anonymous type. Anonymous types are used in LINQ when you create a so-called projection. This means that you select certain properties from a query and form a specific type for it.

Using LINQ queries

When working with data, be it in memory, from a database, an XML file, or another store, your queries always have the following three steps:

1. Obtain the data.

2. Create a query.

3. Execute the query.

Example 4-54 shows an example of a simple LINQ query that selects some numbers from an array.

Example 4-54. A LINQ select query

int[] data = { 1, 2, 5, 8, 11 };

var result = from d in data

where d % 2 == 0

select d;

foreach (int i in result)

{

Console.WriteLine(i);

}

// Displays 2 8

As you can see, the query uses a special syntax: query syntax. The CLR that underpins the .NET Framework has no notion of what to do with a query in query syntax. The compiler translates the query syntax into a set of method calls. The previous query can also be written in method syntax:

var result = data.Where(d => d % 2 == 0);

Of course, there is no Where method on an array of integers. This is where the extension methods are used. The LINQ operators are extension methods on IEnumerable<T>. You also see how a lambda is used as an argument to the Where method. In this case, Where expects an argument of type Func<int,bool>, meaning that it expects a bool for each given int.

You can choose whether you want to use method or query syntax. Often, for smaller queries, the query syntax is easier to read. However, not all LINQ operators are supported in query syntax, so sometimes you are forced to use the method-based syntax. You can also mix the two approaches. The compiler always transforms your query syntax into method syntax.

Standard LINQ query operators

LINQ has a couple of standard query operators that you can use when working with your data. A LINQ provider maps your query to a specific data store, such as LINQ to XML, LINQ to Entities, or LINQ to Objects. Every LINQ provider is encouraged to implement the standard query operators so you can always use them. This means that you can use these standard operators on almost all data sources, providing a consistent experience.

MORE INFO: LINQ SAMPLES

For examples of how to use all the LINQ operators, see http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b.

The standard query operators are: All, Any, Average, Cast, Count, Distinct, GroupBy, Join, Max, Min, OrderBy, OrderByDescending, Select, SelectMany, Skip, SkipWhile, Sum, Take, Take-While, ThenBy, ThenByDescending, and Where.

The most basic query that you can create is shown in Example 4-55.

Example 4-55. LINQ Select operator

int[] data = { 1, 2, 5, 8, 11 };

var result = from d in data

select d;

Console.WriteLine(string.Join(", ", result)); // Displays 1, 2, 5, 8, 11

In this case, the query is not doing anything special. You could have used the data array directly and it would have produced the same results. However, this query shows how LINQ works. First, you start with a from statement and then a data source. This helps the compiler because now it immediately knows what you’re working with. Because of this, it can give IntelliSense on the next line. This is different from what you are used to in regular SQL, in which you start with a SELECT clause and then a FROM clause.

You can now make the query somewhat more interesting by adding a filter. This is done by adding a where statement. This statement should return a Boolean value that represents if the value should be included in the final result. Example 4-56 shows how to filter your list for items that are greater than 5.

Example 4-56. LINQ where operator

int[] data = { 1, 2, 5, 8, 11 };

var result = from d in data

where d > 5

select d;

Console.WriteLine(string.Join(", ", result)); // Displays 8, 11

Another useful operator is orderby, which can be used to sort your collection on a specific value. You can use orderby to sort your data in ascending or descending order. Example 4-57 shows how to sort the data in descending order.

Example 4-57. LINQ orderby operator

int[] data = { 1, 2, 5, 8, 11 };

var result = from d in data

where d > 5

orderby d descending

select d;

Console.WriteLine(string.Join(", ", result)); // Displays 11, 8

You can also combine data from multiple sources. Let’s say you have two arrays both containing numbers and you want to multiply them with each other. In your LINQ query, you use multiple from statements to combine the data, as shown in Example 4-58.

Example 4-58. LINQ multiple from statements

int[] data1 = { 1, 2, 5 };

int[] data2 = { 2, 4, 6};

var result = from d1 in data1

from d2 in data2

select d1 * d2;

Console.WriteLine(string.Join(", ", result)); // Displays 2, 4, 6, 4, 8, 12, 10, 20, 30

When you are working with some more interesting types LINQ shows its power. Let’s say you are working with an Order class that has some OrderLine objects that point to a Product (see Example 4-59).

Example 4-59. A sample Order class for LINQ queries

public class Product

{

public string Description { get; set; }

public decimal Price { get; set; }

}

public class OrderLine

{

public int Amount { get; set; }

public Product Product { get; set; }

}

public class Order

{

public List<OrderLine> OrderLines { get; set; }

}

Now, let’s say you want to know the average number of OrderLines for a set of Orders. You can use a LINQ query to easily calculate this value:

var averageNumberOfOrderLines = orders.Average(o => o.OrderLines.Count);

As you can see, there is no query syntax for the Average method, which is why you need to use the method syntax. Try writing this query without LINQ. It’s not hard, but it definitely has a lot more code.

Other useful LINQ operations are projection and grouping. When using projection, you select another type or an anonymous type as the result of your query. You project your results into it to focus only on the properties you really need. When using grouping, you group your data by a certain property and then work with that result. An example where this is useful is when you want to know how many items of each product you have sold. You can use the query from Example 4-60 to calculate this.

Example 4-60. Using group by and projection

var result = from o in orders

from l in o.OrderLines

group l by l.Product into p

select new

{

Product = p.Key,

Amount = p.Sum(x => x.Amount)

};

Another standard operator is the join operator. Join can be used to combine data from two or more sources. When using the join operator, you have to specify the property that needs to be equal, as you can see in Example 4-61.

Example 4-61. Using join

string[] popularProductNames = { "A", "B" };

var popularProducts = from p in products

join n in popularProductNames on p.Description equals n

select p;

When you are working with a large data set, you probably want to implement paging. When using paging, you don’t show all data at once to the user. Instead, you load it one page at a time. When data comes from an external resource such as a database, this can yield a significant performance gain. To implement paging, you can use the Skip and Take operators. There is no query syntax for them, so you need to use the methods as you can see in Example 4-62.

Example 4-62. Using Skip and Take to implement paging

var pagedOrders = orders

.Skip((pageIndex - 1) * pageSize)

.Take(pageSize);

MORE INFO: LINQPAD

LINQPad is an interactive way of testing your queries. You can use it to quickly test a query, convert from SQL to LINQ or just to learn how LINQ works. You can find LINQPad at http://www.linqpad.net/.

How does LINQ work?

Now that you have experimented somewhat with a few queries, you are probably wondering how it all works. Let’s say that you wanted to implement the Where query operator yourself.

If you look at the source code, you will see that the method definition of Where is the following:

public static IEnumerable<TSource> Where(

this IEnumerable<TSource> source,

Func<TSource, bool> predicate)

If you want to create your own implementation, you need to create an extension method with this signature. By removing the using statement for System.Linq you can then use your own method.

A basic implementation for this method is shown in Example 4-63. This omits error checking and handling.

Example 4-63. Implementing Where

public static class LinqExtensions

{

public static IEnumerable<TSource> Where<TSource>(

this IEnumerable<TSource> source,

Func<TSource, bool> predicate)

{

foreach (TSource item in source)

{

if (predicate(item))

{

yield return item;

}

}

}

}

The magical keyword in this code listing is the yield return statement. Because the yield statement is an implementation of the iterator pattern, the code is not executed until the first call to MoveNext is made. This is called deferred execution. A LINQ query is not executed until it is iterated, until that moment the query does nothing. A lot of errors when working with LINQ queries happen because people forget when their query is executed.

This is particularly important when you are working with one of the other LINQ providers that work against a database, such as LINQ to Entities or LINQ to SQL (LINQ providers like LINQ to Entities parse the query and transform it to SQL). The query won’t be sent to the database until the result is iterated over. That also means that executing a query multiple times hits the database multiple times. Because of this, it is better to save the results of a query in a local variable and use that variable when working with the data.

Iterating can happen when you call a method like ToList or when you iterate over the results in a for each statement (which calls the MoveNext method on the iterator). Of course, other methods are more complex, but they all follow the same idea.

MORE INFO: YIELD

For more information on yield and the iterator pattern, see Chapter 2.

Using LINQ to XML

One other provider that is part of the .NET Framework is LINQ to XML. Normally, you work with XML files by using the XmlWriter, XmlReader, and XmlDocument classes. The advantage of LINQ to XML is that you can use the same query experience that you use in LINQ to Objects or with other LINQ providers.

LINQ to XML helps you create, edit, and parse XML files. If you just have to get some information from an XML file, LINQ to XML offers you an easy query experience. If you need more capabilities, LINQ helps you write powerful queries that are more compact than other XML classes.

Querying XML

If you want to query an XML file with LINQ to XML, you can use the XDocument class to load a file or a string containing XML into memory. The XDocument class works with objects of type XNode. XNode is an abstract class that represents the idea of some segment of content that a document holds. You can use XDocument.Nodes to access the nodes that form an XML file, or you can use XDocument.Descendants or XDocument.Elements to search for a specific set of nodes. One of the nice features of the XDocument class is that it represents the XML file in a hierarchical way. Because of this, you can move from a node to child nodes and back to the parent node. Attributes are not considered nodes; instead, they are key/value pairs that belong to a node.

Example 4-64 shows some sample XML containing a set of people who all have some attributes and contact information.

Example 4-64. Sample XML

String xml = @"<?xml version=""1.0"" encoding=""utf-8"" ?>

<people>

<person firstname=""john"" lastname=""doe"">

<contactdetails>

<emailaddress>john@unknown.com</emailaddress>

</contactdetails>

</person>

<person firstname=""jane"" lastname=""doe"">

<contactdetails>

<emailaddress>jane@unknown.com</emailaddress>

<phonenumber>001122334455</phonenumber>

</contactdetails>

</person>

</people>";

You can use LINQ to XML to execute a query that loads the names of the people from the string of XML. To use LINQ to XML, you have to add a reference to the System.Xml.Linq namespace. Example 4-65 shows how you can use the Descendants method and the Attribute method to load this data.

Example 4-65. Querying some XML by using LINQ to XML

XDocument doc = XDocument.Parse(xml);

IEnumerable<string> personNames = from p in doc.Descendants("Person")

select (string)p.Attribute("firstName")

+ " " + (string)p.Attribute("lastName");

foreach (string s in personNames)

{

Console.WriteLine(s);

}

// Displays:

// John Doe

// Jane Doe

One thing to note is that the Attribute method returns instances of XAttribute. The XAttribute has a Value property of type string, but it also implements explicit operators, so you can cast it to most of the basic types in C#.

MORE INFO: EXPLICIT CAST OPERATORS

For more information on explicit cast operators, see Chapter 2.

Because LINQ to XML supports the standard LINQ operators, you can easily use operators such as Where and OrderBy in your XML queries. Example 4-66 shows how you can filter all people to only those with a phone number and then select their full names in alphabetical order. You also see how easily you can mix both method and query-based syntax.

Example 4-66. Using Where and OrderBy in a LINQ to XML query

XDocument doc = XDocument.Parse(xml);

IEnumerable<string> personNames = from p in doc.Descendants("Person")

where p.Descendants("PhoneNumber").Any()

let name = (string)p.Attribute("firstName")

+ " " + (string)p.Attribute("lastName")

orderby name

select name;

Creating XML

Besides querying XML, LINQ to XML can also help you with creating XML in a nice and fluent syntax. You use the class XElement for creating your own XML. You can use the Add method to construct an XML hierarchy or you can use the XElement constructor that takes an array of objects that form the content. This syntax is nice and trim. Example 4-67 shows an example of how to create some XML.

Example 4-67. Creating XML with the XElement class

XElement root = new XElement("Root",

new List<XElement>

{

new XElement("Child1"),

new XElement("Child2"),

new XElement("Child3")

},

new XAttribute("MyAttribute", 42));

root.Save("test.xml");

//Outputs:

//<Root MyAttribute="42">

// <Child1 />

// <Child2 />

// <Child3 />

//</Root>

By using object initializers and collection initializers, you can create your XML documents in an easy way.

Updating XML

When you want to modify a piece of XML, you typically load it into memory, modify the XML by removing and inserting nodes, or changing the content of existing nodes. After you’re finished, you save the XML back to the file.

LINQ to XML uses another approach called functional construction. Functional construction treats modifying data as a problem of transformation rather than as a detailed manipulation of data. Transformation can take more processor power, but it’s easier to write and maintain, and in most situations, these benefits outweigh the costs.

Let’s say you are working with the XML from Example 4-64 and you need to add a phone number for Joe and an IsMale attribute to all people.

You can use procedural code to do this, as shown in Example 4-68.

Example 4-68. Updating XML in a procedural way

XElement root = XElement.Parse(xml);

foreach (XElement p in root.Descendants("Person"))

{

string name = (string)p.Attribute("firstName") + (string)p.Attribute("lastName");

p.Add(new XAttribute("IsMale", name.Contains("John")));

XElement contactDetails = p.Element("ContactDetails");

if (!contactDetails.Descendants("PhoneNumber").Any())

{

contactDetails.Add(new XElement("PhoneNumber", "001122334455"));

}

}

You can use functional construction to transform this tree into a new one that adds the new attribute and elements by using a LINQ to XML query. Example 4-69 shows how you can do it.

Example 4-69. Transforming XML with functional creation

XElement root = XElement.Parse(xml);

XElement newTree = new XElement("People",

from p in root.Descendants("Person")

let name = (string)p.Attribute("firstName") + (string)p.Attribute("lastName")

let contactDetails = p.Element("ContactDetails")

select new XElement("Person",

new XAttribute("IsMale", name.Contains("John")),

p.Attributes(),

new XElement("ContactDetails",

contactDetails.Element("EmailAddress"),

contactDetails.Element("PhoneNumber")

?? new XElement("PhoneNumber", "112233455")

)));

It depends on the difficulty of your modification whether you use the regular procedural or functional way. Especially when the structure of the XML document changes, the functional way can have a lot of benefits.

THOUGHT EXPERIMENT

Switching to LINQ

You are starting a new project in which you can use LINQ for the first time. You have never worked with LINQ before, but you have studied it on your own time and you see its advantages.

You see possibilities for using LINQ to Entities, LINQ to Objects, and LINQ to XML in your projects and you try to introduce them in your company.

However, some of your coworkers are having some doubts. Will LINQ be fast enough? Is it easy to maintain? Do we need to use the method or query syntax?

Try to help your colleagues by answering these questions for them:

1. Does LINQ have any performance problems? If so, should it be avoided?

2. Is LINQ easy to maintain?

3. What are the differences between method and query syntax? Which should be used?

Objective summary

§ LINQ, which stands for Language Integrated Query, is a uniform way of writing queries against multiple data sources.

§ Important language features when working with LINQ queries are implicit typing, object initialization syntax, lambdas, extension methods, and anonymous types.

§ You can use LINQ with a method-based syntax and the query syntax.

§ LINQ queries are deferred-execution, which means that the query executes when it is first iterated.

§ You can use LINQ to XML to query, create, and update XML.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You have a list of dates. You want to filter the dates to the current year and then select the highest date. Which query do you use?

a. DateTime result = dates.Where(d => d == DateTime.Now).OrderBy(d => d).First();

b. DateTime result = dates.Where(d => d.Year == DateTime.Now.Year).OrderByDescending(d => d).FirstOrDefault();

c. DateTime result = dates.Where(d => d.Year == DateTime.Now.Year).OrderByDescending(d => d).First();

d. DateTime result = dates.Where(d => d.Year == DateTime.Now.Year).OrderByDescending(d => d).Single();

2. You are trying to use a LINQ query, but you are getting a compile error that the Where method cannot be found. What should you do? (Choose all that apply.)

a. Add a using System.Linq statement.

b. Check that you are using a type that implements IEnumerable.

c. Change your query from query to method syntax.

d. Change the type of your query to var.

3. You are using the following LINQ to Entities query:

4. var query = from p in myContext.Products

5. where p.Price < 50

6. select p;

7. int numberOfItems = query.Count();

var products = query.ToList();

You are suffering performance problems. How can you improve your query? (Choose all that apply.)

a. Avoid hitting the database multiple times.

b. Don’t execute ToList() on the query.

c. Use paging.

d. Change the query to method syntax.

Objective 4.4: Serialize and deserialize data

When building your applications, you will often exchange data with other applications. When sending data to a web service or over a network stream, you first have to transform your data to a flat or binary form. When you receive data, you have to transform the flat or binary data to the objects you want to work with. This process is called serialization and deserialization.

THIS OBJECTIVE COVERS HOW TO:

§ Use serialization and deserialization.

§ Use different types of serializers.

§ Configure your objects for serialization.

Using serialization and deserialization

Serialization is the process of transforming an object or object graph that you have in-memory into a stream of bytes or text. Deserialization is the opposite. You take some bytes or text and transform them into an object.

You use serialization when you need to exchange data with another application. This exchange can be done over a network or when you store data into a database or file.

Serialization can become a complex process, especially when you are dealing with a graph of objects that can have cyclic references (a series of references in which the last object references the first). It’s not a trivial task to serialize an object.

Another thing to keep in mind is that serialization serializes only the data that an object stores. Methods are not serialized. When you deserialize an object, you need access to the original class definition or you will end up with an object that only stores data. When you want to optimize the amount of data that you have to serialize, you can create a custom data transfer object (DTO) that contains only the specific data you need.

The .NET Framework offers classes to help with serializing your object that can be found in the System.Runtime.Serialization and System.Xml.Serialization namespaces. These classes can help you with serializing and deserializing an object, but also with configuring your own objects so they can be serialized.

The .NET Framework offers three serialization mechanisms that you can use by default:

§ XmlSerializer

§ DataContractSerializer

§ BinaryFormatter

Using XmlSerializer

The XmlSerializer was created with the idea of Simple Object Access Protocol (SOAP) messaging in mind. SOAP is a protocol for exchanging information with web services. It uses XML as the format for messages. XML is readable by both humans and machines, and it is independent of the environment it is used in.

The XmlSerializer is loosely coupled to your objects. If you add new properties or methods to your objects, the XmlSerializer won’t notice it. With some simple configuration changes, you can map XML nodes to properties in your objects so both can be modified independently. But theXmlSerializer doesn’t have the highest performance; it also doesn’t maintain the object references you have and it can’t work with private fields.

When working with the XmlSerializer, it’s important that you mark your types with the [Serializable] attribute, part of the SerializableAttribute class. This informs the .NET Framework that your type should be serializable. It will check your object and all the objects it references to make sure that it can serialize the whole graph. If that’s not possible, you will get an exception at runtime.

Example 4-70 shows an example of how you can serialize an object and then restore it from XML. As you can see, the Person class is marked with Serializable. All members of the type are automatically serialized if they don’t opt out.

Example 4-70. Serializing an object with the XmlSerializer

[Serializable]

public class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

public int Age { get; set; }

}

XmlSerializer serializer = new XmlSerializer(typeof(Person));

string xml;

using (StringWriter stringWriter = new StringWriter())

{

Person p = new Person

{

FirstName = "John",

LastName = "Doe",

Age = 42

};

serializer.Serialize(stringWriter, p);

xml = stringWriter.ToString();

}

Console.WriteLine(xml);

using (StringReader stringReader = new StringReader(xml))

{

Person p = (Person)serializer.Deserialize(stringReader);

Console.WriteLine("{0} {1} is {2} years old", p.FirstName, p.LastName, p.Age);

}

// Displays

//<?xml version="1.0" encoding="utf-16"?>

//<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

// xmlns:xsd="http://www.w3.org/2001/XMLSchema">

// <FirstName>John</FirstName>

// <LastName>Doe</LastName>

// <Age>42</Age>

//</Person>

//John Doe is 42 years old

You can configure how the XmlSerializer serializes your type by using attributes. These attributes are defined in the System.Xml.Serialization namespace. The following are the important attributes that you will use most of the time:

§ XmlIgnore

§ XmlAttribute

§ XmlElement

§ XmlArray

§ XmlArrayItem

By default, each member is serialized as an XmlElement. This means that they end up as node in your XML. By using XmlAttribute, you can map a member to an attribute on its parent node. XmlIgnore can be used to make sure that an element is not serialized. XmlArray and XmlArrayItem are used when serializing collections. Example 4-71 shows an example in which all these attributes are used.

Example 4-71. Using the XML attributes to configure serialization

[Serializable]

public class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

public int Age { get; set; }

}

[Serializable]

public class Order

{

[XmlAttribute]

public int ID { get; set; }

[XmlIgnore]

public bool IsDirty { get; set; }

[XmlArray("Lines")]

[XmlArrayItem("OrderLine")]

public List<OrderLine> OrderLines { get; set; }

}

[Serializable]

public class VIPOrder : Order

{

public string Description { get; set; }

}

[Serializable]

public class OrderLine

{

[XmlAttribute]

public int ID { get; set; }

[XmlAttribute]

public int Amount { get; set; }

[XmlElement("OrderedProduct")]

public Product Product { get; set; }

}

[Serializable]

public class Product

{

[XmlAttribute]

public int ID { get; set; }

public decimal Price { get; set; }

public string Description { get; set; }

}

When you serialize this code, you have to make sure the .NET Framework knows that you are using an inheritance hierarchy. Example 4-72 shows a sample VIPOrder and how you can serialize and deserialize it.

Example 4-72. Serializing a derived, complex class to XML

private static Order CreateOrder()

{

Product p1 = new Product { ID = 1, Description = "p2", Price = 9 };

Product p2 = new Product { ID = 2, Description = "p3", Price = 6 };

Order order = new VIPOrder

{

ID = 4,

Description = "Order for John Doe. Use the nice giftwrap",

OrderLines = new List<OrderLine>

{

new OrderLine { ID = 5, Amount = 1, Product = p1},

new OrderLine { ID = 6 ,Amount = 10, Product = p2},

}

};

return order;

}

XmlSerializer serializer = new XmlSerializer(typeof(Order),

new Type[] { typeof(VIPOrder) });

string xml;

using (StringWriter stringWriter = new StringWriter())

{

Order order = CreateOrder();

serializer.Serialize(stringWriter, order);

xml = stringWriter.ToString();

}

using (StringReader stringReader = new StringReader(xml))

{

Order o = (Order)serializer.Deserialize(stringReader);

// Use the order

}

Using binary serialization

The XmlSerializer outputs human-readable text. You can open it in Notepad, for example, to inspect and edit it. But the human readability of the file also adds to its size. By using a binary format, you get a smaller result. You can also serialize data that is not suitable for an XML format such as an image.

In essence, using binary serialization looks like using the XmlSerializer. You need to mark an item with the SerializableAttribute and then you use instance of the binary serializer to serialize an object or an object graph to a Stream. The important namespaces are System.Runtime.Serializationand System.Runtime.Serialization.Formatters.Binary. Example 4-73 shows how you can take an object, serialize it to a file, and then deserialize it to an object.

Example 4-73. Using binary serialization

[Serializable]

public class Person

{

public int Id { get; set; }

public string Name { get; set; }

private bool isDirty = false;

}

Person p = new Person

{

Id = 1,

Name = "John Doe"

};

IFormatter formatter = new BinaryFormatter();

using (Stream stream = new FileStream("data.bin", FileMode.Create))

{

formatter.Serialize(stream, p);

}

using (Stream stream = new FileStream("data.bin", FileMode.Open))

{

Person dp = (Person)formatter.Deserialize(stream);

}

Binary serialization creates a compact stream of bytes. One thing that’s different compared with XML serialization is that private fields are serialized by default. Another thing is that during deserialization, no constructors are executed. You have to take this into account when working with binary serialization.

Just as with the XmlSerializer, you can prevent fields from being serialized. You do this by using the [NonSerialized] attribute. Suppose that you don’t want to serialize the IsDirty field from the Person class. You can do this by using the code in Example 4-74.

Example 4-74. Using attributes to control serialization

[Serializable]

public class Person

{

public int Id { get; set; }

public string Name { get; set; }

[NonSerialized]

private bool isDirty = false;

}

Binary serialization is more strict than XML serialization. When the XML serializer can’t find a specific field, it won’t throw an exception; it will just set the property to its default value. The binary serializer is not that forgiving. So it’s important to be able to influence the serialization process. You can use the OptionalFieldAttribute to make sure that the binary serializer knows that a field is added in a later version and that earlier serialized objects won’t contain this field, for example.

You can influence the serialization and deserialization process in four specific phases, namely when starting and finishing an action. You can do this by using the following four attributes:

§ OnDeserializedAttribute

§ OnDeserializingAttribute

§ OnSerializedAttribute

§ OnSerializingAttribute

You add these attributes to methods inside your class that take a StreamingContext as a parameter. Example 4-75 shows how you can add these methods to the Person class.

Example 4-75. Influencing serialization and deserialization

[Serializable]

public class Person

{

public int Id { get; set; }

public string Name { get; set; }

[NonSerialized]

private bool isDirty = false;

[OnSerializing()]

internal void OnSerializingMethod(StreamingContext context)

{

Console.WriteLine("OnSerializing.");

}

[OnSerialized()]

internal void OnSerializedMethod(StreamingContext context)

{

Console.WriteLine("OnSerialized.");

}

[OnDeserializing()]

internal void OnDeserializingMethod(StreamingContext context)

{

Console.WriteLine("OnDeserializing.");

}

[OnDeserialized()]

internal void OnDeserializedMethod(StreamingContext context)

{

Console.WriteLine("OnSerialized.");

}

}

When serializing and deserializing a Person object, you will get the following output:

OnSerializing..

OnSerialized..

OnDeserializing..

OnSerialized..

One thing that’s important to keep in mind is that a serialized object could expose private data that is security sensitive. Everyone who has permissions to deserialize the file can access your sensitive data. If you have a sensitive class, you should implement the ISerializable interface. When implementing this interface, you have control over which values are serialized. You could choose to not serialize sensitive data or possibly encrypt prior to serialization. Example 4-76 shows how to implement this interface.

Example 4-76. Implementing ISerializable

[Serializable]

public class PersonComplex : ISerializable

{

public int Id { get; set; }

public string Name { get; set; }

private bool isDirty = false;

public PersonComplex() { }

protected PersonComplex(SerializationInfo info, StreamingContext context)

{

Id = info.GetInt32("Value1");

Name = info.GetString("Value2");

isDirty = info.GetBoolean("Value3");

}

[System.Security.Permissions.SecurityPermission(SecurityAction.Demand,

SerializationFormatter = true)]

public void GetObjectData(SerializationInfo info, StreamingContext context)

{

info.AddValue("Value1", Id);

info.AddValue("Value2", Name);

info.AddValue("Value3", isDirty);

}

}

As you can see, implementing ISerializable consists of two important parts. The first is the GetObjectData method. This method is called when your object is serialized. It should add the values that you want to serialize as key/value pairs to the SerializationInfo object that’s passed to the method. One thing that’s important is that you should mark this method with a SecurityPermission attribute (you can find this attribute in the System.Security.Permissions namespace) so that it is allowed to execute serialization and deserialization code.

The other important step is adding a special protected constructor that takes a SerializationInfo and StreamingContext. This constructor is called during deserialization, and you use it to retrieve the values and initialize your object. As you can see, you are free in choosing the names for the values that you add to the SerializationInfo.

It’s important to implement security checks in your constructor. This way, you can make sure that no one has tampered with the serialized data.

Using DataContract

You have now seen XML and binary serialization. Another type of serialization is used when you use WCF. When your types are used in WCF, they are serialized so they can be sent to other applications. The Data Contract Serializer is used by WCF to serialize your objects to XML or JSON.

The most noticeable difference is that you use DataContractAttribute instead of SerializableAttribute. Another important difference is that members are not serialized by default. You have to explicitly mark them with the DataMember attribute.

As with binary serialization, you can use OnDeserializedAttribute, OnDeserializingAttribute, OnSerializedAttribute, and OnSerializingAttribute to configure the four phases of the serialization and deserialization process.

Example 4-77 shows how you can create a DataContract for the Person class. The isDirty field is ignored, and both the Id and Name property will be serialized.

Example 4-77. Using a DataContract

[DataContract]

public class PersonDataContract

{

[DataMember]

public int Id { get; set; }

[DataMember]

public string Name { get; set; }

private bool isDirty = false;

}

You can use the DataContractSerializer from the System.Runtime.Serialization namespace in the same way you used the XmlSerializer and BinarySerializer. You need to specify a Stream object that has the input or output when serializing or deserializing an object. Example 4-78 shows an example.

Example 4-78. Using the DataContractSerializer

PersonDataContract p = new PersonDataContract

{

Id = 1,

Name = "John Doe"

};

using (Stream stream = new FileStream("data.xml", FileMode.Create))

{

DataContractSerializer ser = new DataContractSerializer(typeof(PersonDataContract));

ser.WriteObject(stream, p);

}

using (Stream stream = new FileStream("data.xml", FileMode.Open))

{

DataContractSerializer ser = new DataContractSerializer(typeof(PersonDataContract));

PersonDataContract result = (PersonDataContract)ser.ReadObject(stream);

}

Using JSON serializer

JSON is a special format that is specifically useful when sending small amounts of data between a web server and a client by using Asynchronous JavaScript and XML (AJAX). Normally, your data is automatically serialized for you when you use a WCF AJAX endpoint or ASP.NET WebApi. When you want to execute this serialization manually, you can use the DataContractJsonSerializer. Example 4-79 shows how to serialize an object manually to JSON.

Example 4-79. Using the DataContractJsonSerializer

[DataContract]

public class Person

{

[DataMember]

public int Id { get; set; }

[DataMember]

public string Name { get; set; }

}

Person p = new Person

{

Id = 1,

Name = "John Doe"

};

using (MemoryStream stream = new MemoryStream())

{

DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person));

ser.WriteObject(stream, p);

stream.Position = 0;

StreamReader streamReader = new StreamReader(stream);

Console.WriteLine(streamReader.ReadToEnd()); // Displays {"Id":1,"Name":"John Doe"}

stream.Position = 0;

Person result = (Person)ser.ReadObject(stream);

}

THOUGHT EXPERIMENT

Choosing the correct serialization

You need to serialize some data to a file. The file can then be processed by another .NET application. Your data consists of personal records that store important information such as names, addresses, logon credentials, and contact details.

You are wondering which serialization would be best. You think about XML binary, JSON or using a Data Contract.

1. To which format should you serialize the data?

2. Which serializer should you use?

3. Do you need to implement any specific serialization methods on your type?

Objective summary

§ Serialization is the process of transforming an object to a flat file or a series of bytes.

§ Deserialization takes a series of bytes or a flat file and transforms it into an object.

§ XML serialization can be done by using the XmlSerializer.

§ You can use special attributes to configure the XmlSerializer.

§ Binary serialization can be done by using the BinaryFormatter class.

§ WCF uses another type of serialization that is performed by the DataContractSerializer.

§ JSON is a compact text format that can be created by using the DataContractJsonSerializer.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You need to store a large amount of data, and you want to do this in the most optimal way. Which serializer should you use?

a. XmlSerializer

b. BinaryFormatter

c. DataContractSerializer

d. DataContractJsonSerializer

2. You are serializing some sensitive data to a binary format. What should you use? (Choose all that apply.)

a. XmlSerializer

b. ISerializable

c. DataContractSerializer

d. BinaryFormatter

3. You want to serialize some data to XML, and you need to make sure that a certain property is not serialized. Which attribute should you use?

a. XmlElement

b. XmlAttribute

c. XmlIgnore

d. NonSerialized

Objective 4.5: Store data in and retrieve data from collections

When you are working with some groups of data, you often use a collection. Collection objects are useful when you want to work with data in a flexible way. You might need to add and remove elements from a collection, you might have special performance needs, or you might need to access items. This is why the .NET Framework offers you an extensive set of collections that you can use.

THIS OBJECTIVE COVERS HOW TO:

§ Use the collections that are part of the .NET Framework.

§ Choose which collection to use.

§ Implement your own collections.

Using arrays

The most basic type that you can use to store a group of entities is an array. Arrays are useful when you are working with a fixed number of objects that all have the same type.

You declare an array by using a special syntax: type[] arrayName. The square brackets denote the type as being an array. When creating an array, you are required to specify the number of items it will contain.

Example 4-80 shows how you can declare an array, fill it, and iterate over it.

Example 4-80. Using an array

int[] arrayOfInt = new int[10];

for (int x = 0; x < arrayOfInt.Length; x++)

{

arrayOfInt[x] = x;

}

foreach (int i in arrayOfInt)

{

Console.Write(i); // Displays 0123456789

}

As you can see, the array is created with a fixed size. Arrays are zero-based, which means that the first element can be found at index 0 and the last element at the length of the array–1. You can loop through an array by using the Length property and a for loop. Arrays are reference types that inherit from the Array class. An array implements IEnumerable, so you can use it in a foreach loop.

An array can also be initialized directly. The compiler then will check the length for you and create an appropriate array.

int[] arrayOfInt = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

Besides declaring single-dimensional arrays, you can also create multidimensional and jagged arrays.

A two-dimensional array, for example, means that the array has a certain number of rows and columns. You declare it by using a comma (,) in your array declaration. The number of rows and columns can be different. Example 4-81 shows how to declare a two-dimensional array.

Example 4-81. Using a two-dimensional array

string[,] array2D = new string[3, 2] { { "one", "two" }, { "three", "four" },

{ "five", "six" } };

Console.WriteLine(array2D[0, 0]); // one

Console.WriteLine(array2D[0, 1]); // two

Console.WriteLine(array2D[1, 0]); // three

Console.WriteLine(array2D[1, 1]); // four

Console.WriteLine(array2D[2, 0]); // five

Console.WriteLine(array2D[2, 1]); // six

A jagged array is an array whose elements are arrays. Because arrays are reference types, the values of a jagged array have a default value of null. Example 4-82 shows how you can create a jagged array by using the initialization syntax.

Example 4-82. Creating a jagged array

int[][] jaggedArray =

{

new int[] {1,3,5,7,9},

new int[] {0,2,4,6},

new int[] {42,21}

};

The biggest problem with arrays is that they are of fixed size. When working with groups of objects, you often want to add or remove items from the collection. This is why the .NET Framework has some other collection types.

Understanding generic versus nongeneric

Most of the collection types have both a generic and a nongeneric version. When you work with objects of one specific type (or base type), use the generic collection. It will improve type safety and performance because there is no casting required.

The nongeneric collections can be found in System.Collections, and generic collections can be found in System.Collections.Generic.

If you use a value type as the type parameter for a generic collection, you need to make sure that you eliminate all scenarios in which boxing could occur. For example, if your value type does not implement IEquatable<T>, your object needs boxing to call Object.Equals(Object) for checking equality. The same is true for the IComparable<T> interface.

When using reference types, you won’t have these issues.

Using List

One collection type you will probably use most often is the generic List<T> collection. The List type offers methods for adding and removing items, accessing items by index, and searching and sorting the list.

The List type makes sure that there is always enough room to store additional items. If necessary, the internal implementation of the List class will increase the size of the array it uses to store its items. List<T> can store reference types and it can have a value of null for an item. It can also store duplicate items.

The class definition for List<T> has the following form:

public class List<T> : IList<T>, ICollection<T>, IList, ICollection,

IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable

List<T> implements quite some interfaces. Of course, it implements IEnumerable<T> and IEnumerable, so you can use it in a foreach pattern without worrying about the implementation. IList and ICollection are the interfaces that define the real usefulness of this collection. IList<T> andICollection<T> can be found in Example 4-83.

Example 4-83. Using IList<T> and ICollection<T>

public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable

{

T this[int index] { get; set; }

int IndexOf(T item);

void Insert(int index, T item);

void RemoveAt(int index);

}

public interface ICollection<T> : IEnumerable<T>, IEnumerable

{

int Count { get; }

bool IsReadOnly { get; }

void Add(T item);

void Clear();

bool Contains(T item);

void CopyTo(T[] array, int arrayIndex);

bool Remove(T item);

}

Example 4-84 shows an example of how you can use a List by adding, removing, searching, and iterating over items.

Example 4-84. Using List<T>

List<string> listOfStrings =

new List<string> { "A", "B", "C", "D", "E" };

for (int x = 0; x < listOfStrings.Count; x++)

Console.Write(listOfStrings[x]); // Displays: ABCDE

listOfStrings.Remove("A");

Console.WriteLine(listOfStrings[0]); // Displays: B

listOfStrings.Add("F");

Console.WriteLine(listOfStrings.Count); // Displays: 5

bool hasC = listOfStrings.Contains("C");

Console.WriteLine(hasC); // Displays: true

Using Dictionary

A List<T> just stores a group of items. It enables duplicates and it quickly finds items.

A Dictionary<TKey,TValue> can be used in scenarios in which you want to store items and retrieve them by key, so it doesn’t allow duplicate keys. It takes two type parameters: one for the type of the key, and the other for the type of the value.

The Dictionary class is implemented as a hash table, which makes retrieving a value very fast, close to O(1). The hash value of a key shouldn’t change during time and it can’t be null. The value can be null (if it’s a reference type).

The class signature of the Dictionary<TKey,TValue> class is as follows:

public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>,

ICollection<KeyValuePair<TKey, TValue>>, IDictionary, ICollection,

IReadOnlyDictionary<TKey, TValue>, IReadOnlyCollection<KeyValuePair<TKey, TValue>>,

IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable, ISerializable,

IDeserializationCallback

The Dictionary class works with KeyValuePair<TKey,TValue> structures. Example 4-85 shows how you can work with a Dictionary.

Example 4-85. Using Dictionary<TKey, TValue>

Person p1 = new Person { Id = 1, Name = "Name1" };

Person p2 = new Person { Id = 2, Name = "Name2" };

Person p3 = new Person { Id = 3, Name = "Name3" };

var dict = new Dictionary<int, Person>();

dict.Add(p1.Id, p1);

dict.Add(p2.Id, p2);

dict.Add(p3.Id, p3);

foreach (KeyValuePair<int, Person> v in dict)

{

Console.WriteLine("{0}: {1}", v.Key, v.Value.Name);

}

dict[0] = new Person { Id = 4, Name = "Name4" };

Person result;

if (!dict.TryGetValue(5, out result))

{

Console.WriteLine("No person with a key of 5 can be found");

}

Using sets

In some languages, such as Java, there is a special set type. In C#, a set is a reserved keyword, but you can use the HashSet<T> if you need one. A set is a collection that contains no duplicate elements and has no particular order.

In mathematics, you often perform operations on a set, such as seeing whether a set is a subset of another set, selecting the elements that two sets have in common or that they don’t have in common, and combining two sets. HashSet implements the ISet<T> interface that has the members you can find in Example 4-86.

Example 4-86. Using the ISet<T> interface

public interface ISet<T> : ICollection<T>, IEnumerable<T>, IEnumerable

{

bool Add(T item);

void ExceptWith(IEnumerable<T> other);

void IntersectWith(IEnumerable<T> other);

bool IsProperSubsetOf(IEnumerable<T> other);

bool IsProperSupersetOf(IEnumerable<T> other);

bool IsSubsetOf(IEnumerable<T> other);

bool IsSupersetOf(IEnumerable<T> other);

bool Overlaps(IEnumerable<T> other);

bool SetEquals(IEnumerable<T> other);

void SymmetricExceptWith(IEnumerable<T> other);

void UnionWith(IEnumerable<T> other);

}

Just as with the other collections, you can easily add new objects to it and iterate over them. Example 4-87 shows an example of how you can use a HashSet<T>.

Example 4-87. Using HashSet<T>

public void UseHashSet()

{

HashSet<int> oddSet = new HashSet<int>();

HashSet<int> evenSet = new HashSet<int>();

for (int x = 1; x <= 10; x++)

{

if (x % 2 == 0)

evenSet.Add(x);

else

oddSet.Add(x);

}

DisplaySet(oddSet);

DisplaySet(evenSet);

oddSet.UnionWith(evenSet);

DisplaySet(oddSet);

}

private void DisplaySet(HashSet<int> set)

{

Console.Write("{");

foreach (int i in set)

{

Console.Write(" {0}", i);

}

Console.WriteLine(" }");

}

Using queues and stacks

A queue is a special type of collection you can use to temporarily store some data. It is a so-called first-in, first-out (FIFO) type of collection, just like a checkout line. You access elements in the same order you added them. By getting an item, you also remove it from the queue. This is why a queue offers temporary storage. You can use a queue, for example, when you need to process incoming messages. Each new message is added to the end of the queue; when you are done processing a message, you get a new one from the start of the queue.

The Queue class has three important methods:

§ Enqueue adds an element to the end of the Queue, equivalent to the back of the line.

§ Dequeue removes the oldest element from the Queue, equivalent to the front of the line.

§ Peek returns the oldest element, but doesn’t immediately remove it from the Queue.

Example 4-88 shows how to enqueue some items to a Queue and then loop through them.

Example 4-88. Using Queue<T>

Queue<string> myQueue = new Queue<string>();

myQueue.Enqueue("Hello");

myQueue.Enqueue("World");

myQueue.Enqueue("From");

myQueue.Enqueue("A");

myQueue.Enqueue("Queue");

foreach (string s in myQueue)

Console.Write(s + " ");

// Displays: Hello World From A Queue

A Queue is a first in, first out (FIFO) collection; a Stack is a last-in, first-out (LIFO) collection. Think of the undo system of an application. The last item added to the undo stack is the first one to be used when a user executes an undo action. Just as with a Queue, items are removed when reading them.

A Stack has the following three important methods:

§ Push. Add a new item to the Stack.

§ Pop. Get the newest item from the Stack.

§ Peek. Get the newest item without removing it.

If you would change the example of Example 4-88 to use a Stack instead of a Queue, you would get the reverse output as you can see in Example 4-89.

Example 4-89. Using Stack<T>

Stack<string> myStack = new Stack<string>();

myStack.Push("Hello");

myStack.Push("World");

myStack.Push("From");

myStack.Push("A");

myStack.Push("Queue");

foreach (string s in myStack)

Console.Write(s + " ");

// Displays: Queue A From World Hello

Choosing a collection

When choosing a collection type, you have to think about the scenarios you want to support. The biggest differences between the collections are the ways that you access elements.

List and Dictionary types offer random access to all elements. A Dictionary offers faster read features, but it can’t store duplicate elements.

A Queue and a Stack are used when you want to retrieve items in a specific order. The item is removed when you have retrieved it.

Set-based collections have special features for comparing collections. They don’t offer random access to individual elements.

Although List can be used in most situations, it pays to see whether there is a more specialized collection that can make your life easier.

MORE INFO: COLLECTION TYPES

The .NET Framework has more specialized collections. If you want to know more about these collection types, see http://msdn.microsoft.com/en-us/library/7y3x785f.aspx.

Creating a custom collection

A basic collection can be implemented by implementing IEnumerable or IEnumerable<T>. That way, you implement the iterator pattern, and your collection can be used in a foreach statement.

If you need more features, you can look at the interfaces that are implemented by the existing collections in .NET. The ones used by the classes that you reviewed in this chapter are these:

§ IList<T>

§ ICollection<T>

§ IDictionary<TKey,TValue>

§ ICollection<TKey,TValue>

§ ISet<T>

Of course, you can also choose to directly inherit from an existing collection. Example 4-90 shows an example of how you can inherit from List<T> to add some specific functionality.

Example 4-90. Inheriting from List<T> to form a custom collection

public class PeopleCollection : List<Person>

{

public void RemoveByAge(int age)

{

for (int index = this.Count - 1; index >= 0; index--)

{

if (this[index].Age == age)

{

this.RemoveAt(index);

}

}

}

public override string ToString()

{

StringBuilder sb = new StringBuilder();

foreach (Person p in this)

{

sb.AppendFormat("{0} {1} is {2}", p.FirstName, p.LastName, p.Age);

}

return sb.ToString();

}

}

You can then use this collection like any other collection. Example 4-91 shows how you can initialize it and then use your custom functions to manipulate it.

Example 4-91. Using a custom collection

public class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

public int Age { get; set; }

}

Person p1 = new Person

{

FirstName = "John",

LastName = "Doe",

Age = 42

};

Person p2 = new Person

{

FirstName = "Jane",

LastName = "Doe",

Age = 21

};

PeopleCollection people = new PeopleCollection { p1, p2 };

people.RemoveByAge(42);

Console.WriteLine(people.Count); // Displays: 1

THOUGHT EXPERIMENT

Choosing the correct collection

You are trying to determine the different collections that .NET uses. You try to come up with a comparison of the different collection types by performance and use case.

1. When should you use a generic or nongeneric collection?

2. What’s the difference between the Dictionary- and List-based collections? When should you use one or the other?

3. What’s the difference among the Stack, Queue, and List collections?

Objective summary

§ The .NET Framework offers both generic and nongeneric collections. When possible, you should use the generic version.

§ Array is the most basic type to store a number of items. It has a fixed size.

§ List is a collection that can grow when needed. It’s the most-used collection.

§ Dictionary stores and accesses items using key/value pairs.

§ HashSet stores unique items and offers set operations that can be used on them.

§ A Queue is a first-in, first-out (FIFO) collection.

§ A Stack is a first-in, last-out (FILO) collection.

§ You can create a custom collection by inheriting from a collection class or inheriting from one of the collection interfaces.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You want to store a group of orders and make sure that a user can easily select an order by its order number. Which collection do you use?

a. List<Order>

b. Dictionary<int,Order>

c. HashSet<Order>

d. Queue<Order>

2. You are using a queue and you want to add a new item. Which method do you use?

a. Push

b. Add

c. Dequeue

d. Enqueue

3. You are working with a large group of family name objects. You need to remove all duplicates and then group them by last name. Which collections should you use? (Choose all that apply.)

a. List<T>

b. Stack<T>

c. Dictionary<string,T>

d. T[]

Chapter summary

§ You can use classes such as Drive, DriveInfo, Directory, DirectoryInfo, File, and FileInfo to work with the file system. All I/O uses Streams, which are an abstraction over a series of bytes.

§ Asynchronous code is important for long-running operations to improve responsiveness and scalability.

§ When working with a database, you can use ADO.NET to establish a connection, execute commands, and retrieve results.

§ The .NET Framework has support for working with XML by using classes such as XmlWriter, XmlReader, and XmlDocument, or by using LINQ to XML.

§ LINQ offers a uniform way of querying different data sources.

§ Serializing and deserializing is the process of transforming an object to a flat file or a series of bytes, and vice versa.

§ The .NET Framework offers a comprehensive set of collections that you can use in different scenarios.

Answers

This section contains the solutions to the thought experiments and answers to the lesson review questions in this chapter.

Objective 4.1: Thought experiment

1. You can use DirectoryInfo and FileInfo classes for searching through folders for the specified files. You can use the Path class for making sure that the locations given to you by the customer are valid.

2. You can use a search pattern when looking for files. By using a search pattern such as “*.docx,” you can find all Word documents in a directory.

3. Yes. You are working with I/O in a desktop application. If you use synchronous code, the user interface appears to crash or become unresponsive each time you iterate over your folders or files. By making the application asynchronous, the user interface remains responsive.

Objective 4.1: Objective review

1. Correct answer: D

a. Incorrect: File.CreateText would create a new file each time. You only want to append some text.

b. Incorrect: FileInfo.Create would return a FileStream to a newly created file. You would need to encode your data to a byte array to write it to the stream.

c. Incorrect: File.Create is the static equivalent of FileInfo.Create. It returns a FileStream that would require encoding to write your log entries.

d. Correct: File.AppendText adds some text to the end of a file.

2. Correct answer: C

a. Incorrect: async/await is not usable when working with a CPU-bound algorithm.

b. Incorrect: Running the code synchronously would make the user interface unresponsive.

c. Correct: Task.Run will run the CPU-bound code on a separate thread. This will free the user interface thread to make sure that the application remains responsive.

d. Incorrect: BackgroundWorker is retired. The new Task Parallel Library replaces it.

3. Correct answer: A

a. Correct: UTF-8 is a general-purpose encoding format that works on many operating systems.

b. Incorrect: UTF-7 is used as a protocol for newsgroup and e-mail. It’s not as secure (see http://en.wikipedia.org/wiki/UTF-7#Security) as the other encodings, requires more space, and it’s slower in encoding/decoding. UTF-8 should be used whenever possible.

c. Incorrect: Because ASCII supports only a limited range of characters, it is inadequate in most cases for international applications.

d. Incorrect: UTF-32 requires a lot of space for storing characters (4 bytes for each character). It is used when applications want to encode Unicode supplementary characters (for example, Chinese characters) as one single glyph. You need this only when the encoded space of such characters is important to you. If you don’t need the Unicode supplementary characters, you can use UTF-8. If you do need them. UTF-16 can be used except when you have strict space requirements.

Objective 4.2: Thought experiment

1. Using the Entity Framework can improve your development speed. You won’t have to think about converting data from your database to objects. It will also manage change tracking for you and convert your LINQ queries to SQL. However, this comes at a (sometimes a significant) performance cost. If you don’t really need an object model to expose your data to your clients, you could use plain SQL queries to achieve more performance.

2. For your web services, you can use WCF.to write regular classes and use some attributes on them to expose them as services. By separating the configuration, you can then expose multiple endpoints for different user types. You can, for example, use a JSON endpoint or an XML endpoint.

3. You can use the XmlWriter class for this. Because you are dealing with a lot of data, XmlDocument can be too slow. When you don’t need to edit data but only output it, XmlWriter is a good choice.

Objective 4.2: Objective review

1. Correct answers: A, C

a. Correct: You need a SqlCommand to execute your update query against the database.

b. Incorrect: A SqlDataReader is used when you select some data from your database. You don’t use it when executing an update command.

c. Correct: You need a SqlConnection to establish a connection to your database. The SqlCommand uses this connection to execute the update query.

d. Incorrect: A Transaction is not necessary when executing only a single command. If an exception occurs, no other queries have to be canceled.

2. Correct answer: B

a. Incorrect: Storing your data in a plain XML file doesn’t allow multiple users to read and update it at the same time.

b. Correct: A relational database that stores the data with the Entity Framework mapping it to your objects helps you with quickly developing your application.

c. Incorrect: A relational database is the best option to store your data. Using plain ADO.NET code would require you to manually map your objects to the database, and vice versa.

d. Incorrect: The dynamic keyword can be used in scenarios in which you want weak typing. It will still throw errors at runtime if an action is not possible.

3. Correct answer: A

a. Correct: XmlReader is the fastest option when processing a lot of data. Because you only have to read it and not make any changes, this is the best choice.

b. Incorrect: XmlDocument is not fast enough when working with a large XML file.

c. Incorrect: The XmlWriter is used to create XML files, not to read them.

d. Incorrect: Using a FileStream would treat the XML file as plain text. You lose the benefits of the hierarchical nature of your document. Furthermore, parsing it as plain text is not a trivial task.

Objective 4.3: Thought experiment

1. Although it is true that LINQ is not always as fast as a manual loop or filter, you get a lot of other benefits. LINQ to Entities translates your LINQ query to SQL, which enables you to create strongly typed queries against your database. LINQ to XML uses it to process an XML file with a more fluent syntax than if you do it by hand. The biggest advantage of using LINQ is that you can use a uniform, strongly typed way of querying against multiple different data sources. Most of the time, development time is more important than premature optimizations. If you do encounter a performance bottleneck, you can measure your code and optimize your queries in a targeted manner.

2. LINQ is strongly typed and easier to read than a handwritten loop or query. LINQ offers operators such as Sum, Average, and GroupBy that can make writing complex queries much easier.

3. Query syntax is translated into method syntax by the compiler. Not all operators are supported in query syntax. If you need a special operator that’s available only in method syntax, you are forced to use method syntax. Query syntax is more readable and should be used whenever possible.

Objective 4.3: Objective review

1. Correct answers: B, D

a. Incorrect: Comparing DateTime.Now to the dates will give you only the dates for today, not for the whole year. Also, using OrderBy instead of OrderByDescending will give you the lowest date, not the highest.

b. Correct: This will return the highest date for the current year. If your filter can’t find a value for the current year, it will return ‘1-1-0001 00:00:00’ (DateTime.Min-Value).

c. Incorrect: If your filter doesn’t return a value, you will get an error. You should use FirstOrDefault instead.

d. Correct: Using Single will throw an exception if there are multiple dates for the current year.

2. Correct answers: A, B

a. Correct: You need to add a using statement for LINQ to make sure that all LINQ extension methods are available.

b. Correct: LINQ is implemented as extension methods on IEnumerable. If your type does not implement this, you can’t use the extension methods.

c. Incorrect: The compiler changes your query syntax to method syntax. Using one or the other doesn’t change anything.

d. Incorrect: Using implicit typing lets the compiler determine the result of your query. It doesn’t help the compiler find the Where method.

3. Correct answers: A, C

a. Correct: Because of the deferred execution nature of LINQ, you execute the query twice—one for getting the number of items, and one for getting all the products. You can change your query to get both these numbers in one call.

b. Incorrect: ToList() is necessary for running the query. If you never iterate the query, you won’t get any results.

c. Correct: Paging can help limit the number of items that you retrieve.

d. Incorrect: Method syntax is compiled to query syntax. It doesn’t make any functional difference.

Objective 4.4: Thought experiment

1. You can use the Binary format, which is the smallest and most efficient. XML and JSON are human-readable, but they result in a larger file, and it’s slower to serialize to it.

2. You can use the BinaryFormatter to serialize your data to a Stream.

3. Because you have some sensitive data (the logon credentials), it’s important that you implement the ISerializable interface so you have full control over these properties. Otherwise, someone could open your binary file and extract all logon credentials from it.

Objective 4.4: Objective review

1. Correct answer: B

a. Incorrect: Although XML is human-readable, it’s not the most optimized format. It will result in larger files then using a binary format.

b. Correct: A binary format is the most efficient for storing a large amount of data.

c. Incorrect: The DataContractSerializer is used by WCF to serialize data to XML.

d. Incorrect: The DataContractJsonSerializer serializes your objects to JSON. JSON is used for communication between a web browser and the server.

2. Correct answers: B, D

a. Incorrect: XmlSerializer outputs XML text, not binary data.

b. Correct: ISerializable should be implemented on types that have some sensitive data.

c. Incorrect: The DataContractSerializer is used by WCF to serialize data to XML.

d. Correct: The BinaryFormatter can be used to serialize data to a binary format.

3. Correct answer: C

a. Incorrect: XmlElement is used to configure how a member is serialized to an XML element.

b. Incorrect: XmlAttribute outputs a member as an attribute on its parent instead of as a separate node.

c. Correct: XmlIgnore makes sure that a member is not serialized.

d. Incorrect: The NonSerialized attribute is used with the BinaryFormatter or Soap-Formatter.

Objective 4.5: Thought experiment

1. A generic collection should be used when you are working with a group of items that all have the same type. It gives you better performance and it enables you to access items without casting. Nongeneric collections can be used when you want to mix objects of different types.

2. Dictionary-based collections have a key for each value they store, so they are very fast at retrieving items. They can’t store duplicate items. A List stores items in no particular order. You can access items by index.

3. Stack and Queue collections can be used when you need to retrieve items in a special order (FILO or FIFO). They discard an item after it is retrieved. A List offers random access to all elements.

Objective 4.5: Objective review

1. Correct answers: B, D

a. Incorrect: A List<Order> offers random access to elements. It’s not fast at selecting specific Order items by id.

b. Correct: By using a Dictionary<int,Order>, you can easily select an Order by id.

c. Incorrect: A HashSet doesn’t offer random access to its items. You need to enumerate the whole set to get to an order.

d. Correct: A Queue offers a FIFO set. You can’t randomly access items, and an item is discarded after retrieving it.

2. Correct answers: B, D

a. Incorrect: Push is used to add items to a Stack.

b. Correct: Add is used on types inheriting from ICollection<T>. A Queue does not inherit from ICollection<T> but from ICollection.

c. Incorrect: Dequeue is used to remove an item from a Queue.

d. Correct: Enqueue is used to add an item to a Queue.

3. Correct answers: A, C

a. Correct: You need a list to store all duplicate family name items.

b. Incorrect: A Stack stores items in a LIFO basis. It’s not suitable for storing the duplicated or nonduplicated items.

c. Correct: The Dictionary<string,T> can be used to store the nonduplicated items on family name.

d. Incorrect: You can’t remove items from a regular array.