Tuesday, October 9, 2007

LINQ to Files

In a very simple example, "files" is IEnumerable of string.


var files = from file in Directory.GetFiles(Environment.CurrentDirectory)
select file;


But that's boring and in no way uses the querying power of LINQ. So, let's spice it up.

var files = from file in Directory.GetFiles(Environment.CurrentDirectory, "tmp*.*")
where File.GetLastWriteTime(file) > DateTime.Now.AddDays(-2) &&
(File.GetAttributes(file) != FileAttributes.System)
orderby File.GetCreationTime(file)
select file;

This query finds non-system files modified within the past 2 days where the name starts with "tmp" where it's been ordered based on the creation date.

This next one works but what is wrong with it?


var files = from file in Directory.GetFiles(Environment.CurrentDirectory)
where new FileInfo(file).CreationTime > DateTime.Now.AddDays(2)
select file;


It's technically ok to new up objects in a LINQ query and sometimes is exactly what you want to do. In this case, it was to get the CreationTime from the FileInfo object. However, since there's the static File.GetCreationTime(f) function that returns a DateTime rather than the heavier FileInfo object, it reduces how much memory is used. Imagine doing this call repeatedly on large directories... it'd sure give the GC something to work on.

Let's say we want to have a query that peers into sub directories as well...


var files = from dir in Directory.GetDirectories( Environment.CurrentDirectory, "DLS*" )
from file in Directory.GetFiles( dir, "*.exe" )
where FileVersionInfo.GetVersionInfo( file ).CompanyName.Contains( "Microsoft" )
select file;


So, that query first gets all the directories that match a naming pattern of "DLS*", then in each one of those directories, it gets all of the exe's and only returns the ones where the CompanyName contains "Microsoft". Notice that there are two from statements (it's like nesting a for loop).

The function "GetDirectories" just returns an array of strings, which of course can be used in LINQ queries.

Then, you just do whatever you want to with the results:


foreach (string file in files)
{
File.Delete( file );
}

No comments: