Section 5.5. LINQ to IEnumerable


5.5. LINQ to IEnumerable

One of the elegant design aspects of LINQ is that queries can be executed against any enumerable data source. If an object implements IEnumerable, then LINQ can access the data behind that object. For example, suppose we need to search the current user's My Documents folder (and sub-folders) for all non-system files modified in the last hour. Using LINQ we do this as follows:

 using SIO = System.IO; string[] files; string   mydocs; mydocs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); files  = SIO.Directory.GetFiles(mydocs, "*.*", SIO.SearchOption.AllDirectories); var query = from file in files             let lasthour = DateTime.Now.Subtract(new TimeSpan(0, 1, 0, 0))             where SIO.File.GetLastAccessTime(file) >= lasthour &&                  (SIO.File.GetAttributes(file) & SIO.FileAttributes.System) == 0             select file; 

Notice the presence of the let statement, which allows for the definition of values local to the query; let is used to improve readability and efficiency by factoring out common operations.

This example should not be very surprising, since what we are really doing is iterating across an array of filenames ("files"), not the file system itself. But this is a design artifact of the .NET Framework, not a limitation of LINQ. A similar example is the searching of a file, which is easily done in LINQ by iterating across the lines of the file:

 string filename = ...;  // file to search var lines = from line in SIO.File.ReadAllLines(filename)             where line.Contains("class")             select line; 

In this example, we are reading all the lines into an array and then searching the array to select those lines containing the character sequence "class."

Need to search the Windows event log? An event log is a collection of EventLogEntry objects, and is accessed in .NET by creating an instance of the EventLog class. For example, here's how we gain access to the Application event log on the current machine:

 using SD = System.Diagnostics; SD.EventLog applog = new SD.EventLog("Application", "."); 

Suppose we need to find all events logged by our application for a particular user. This is easily expressed as a LINQ query:

 string appname  = ...;   // name of our application, e.g. "SchedulingApp" string username = ...;   // login name for user, e.g. "DOMAIN\\hummel" var entries = from entry in applog.Entries               where entry.Source == appname &&                     entry.UserName == username               orderby entry.TimeWritten descending               select entry; 

Interestingly, while this query makes perfect sense, it does not compile. The issue is that EnTRies is a pre-2.0 collection, which means it implements IEnumerable and not IEnumerable<T>. Since IEnumerable is defined in terms of object and not a specific type T, the C# type inference engine cannot infer the type of objects the query expression is working with. The designers of LINQ provide an easy solution in the form of the standard query operator Cast. The Cast operator wraps a generic enumerable object with a type-specific one suitable for LINQ:

 var entries = from entry in applog.Entries.Cast<SD.EventLogEntry>()               where entry.Source == appname &&                     entry.UserName == username               orderby entry.TimeWritten descending               select entry; 

The Cast operator allows LINQ to support .NET 1.x collections.

As a final example, let's apply LINQ to the world of Visual Studio Tools for Office (VSTO). To search a user's Outlook contacts, the basic query is as follows:

 Outlook.MAPIFolder folder = this.ActiveExplorer().Session.   GetDefaultFolder( Outlook.OlDefaultFolders.olFolderContacts ); var contacts = from contact in folder.Items.OfType<Outlook.ContactItem>()                where ...  // search criteria, e.g. contact.Email1Address != null                select contact; 

We take advantage of LINQ's OfType query operator, which (a) wraps pre-2.0 collections and (b) filters the collection to return only those objects of the desired type. Here's a query to collect all distinct email addresses from a user's Outlook contacts:

 var emails = (               from contact in folder.Items.OfType<Outlook.ContactItem>()               where contact.Email1Address != null               select contact.Email1Address              )              .Distinct(); 

Finally, given collections of email addresses from different folders or users, we can use LINQ to perform various set operations over these collections:

 var union        = emails.Union(emails2); var intersection = emails.Intersect(emails2); var difference   = emails.Except(emails2); 



LINQ[c] The Future of Data Access in C# 3. 0
LINQ[c] The Future of Data Access in C# 3. 0
ISBN: N/A
EAN: N/A
Year: 2006
Pages: 25

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net