Take the 2-minute tour ×

I have a DB that contains a list of paths to files. I want to build a routine to cleanup the folders, removing files in the directories if there is not a db record for it (for temp ajax file uploads, in cases where the user doesn't complete the form, etc...).

I'm thinking something like this:

var dbFiles = db.allPaths();
var allFiles = Directory.EnumerateFiles(path);

foreach (var f in allFiles) {
  if (!dbFiles.Contains(f) {

Any "Gotchas" waiting for me? The routine will be set to run once a week at first, more often if temp files become a problem. It will be run during a time when there are nearly no users on, so performance - while important - is not paramount.

Thanks in advance!


Wow, lots of great answers. This bit of code is turning into something "share" worthy. ;D My code above was just a simple, quick placeholder bit... but it's transformed into solid code. Thank you!

share|improve this question
Why do you use "var"? This rather new language construct is powerful for use with LINQ, but I regard it an anti-pattern to use var throughout your whole source code where it is not necessary. –  chiccodoro Jun 22 '10 at 6:17
I'd say var is another step towards making code more change-friendly. So long as you keep methods small and your naming good, var is convenient and legible, while still being strongly typed. As C# becomes more dynamic, I think var will just become more useful. So while it may come down to preference, I think anti-pattern is over the top. –  McMuttons Jun 22 '10 at 7:33

4 Answers 4

up vote 1 down vote accepted

To combine all the suggestions into one:

// canonicalize paths
var dbFiles = db.allPaths().Select(Path.GetFullPath);
var allFiles = Directory.EnumerateFiles(Path.GetFullPath(path))

foreach (var file in allFiles.Except(dbFiles, StringComparer.OrdinalIgnoreCase))
    try {
    } catch (IOException) {
        // handle exception here
share|improve this answer
Very nice, thank you Gabe. –  Chaddeus Jun 22 '10 at 7:21
Now with Jon's method group improvements. –  Gabe Jun 22 '10 at 8:21

Looks okay, but you can make it simpler:

foreach (var file in allFiles.Except(dbFiles))

You've got to make sure that the paths are in exactly the same format though. If one list has relative files and the other has absolute files, or if one uses "/" and the other uses "\" you'll end up deleting things you don't expect to.

Ideally you'd canonicalise the files explicitly first, but I can't see a nice way of getting a canonical file name in .NET...

EDIT: Note that Path.GetFullPath does not canonicalize. It fixes slashes and makes it absolute, but it doesn't address case: "c:/users" becomes "c:\users", but "c:/Users" becomes "c:\Users".

This could be fixed by using a string comparer in the call to Except:

var dbFiles = db.AllPaths().Select(Path.GetFullPath));
var allFiles = Directory.EnumerateFiles(path).Select(Path.GetFullPath));

foreach (var file in allFiles.Except(dbFiles, StringComparer.OrdinalIgnoreCase))

Now that's ignoring case - but in an "ordinal" manner. I don't know what the Windows file system really does in terms of its case sensitivity.

share|improve this answer
You would want to use Path.GetFullPath(p) to canonicalize the paths. Maybe allFiles.Select(p => Path.GetFullPath(p)).Except(dbFiles.Select(p => Path.GetFullPath(p))) would do what you're looking for. –  Gabe Jun 22 '10 at 6:30
@Jon That's nice... love making things simpler. –  Chaddeus Jun 22 '10 at 6:34
@Gabe Man, I totally forgot about LINQ in this scenario. Very nice. –  Chaddeus Jun 22 '10 at 6:36
Chad: I combined Jon's answer, my comment, and Jeffrey's answer all into one in my answer. –  Gabe Jun 22 '10 at 6:47
@Gabe: Path.GetFullPath doesn't really canonicalize. See my edit. Oh, and you can use a method group conversion to make the Select method slightly simpler :) –  Jon Skeet Jun 22 '10 at 6:57

Looks good to me; however I've never deleted files within C#, just VB. However, you might want to throw that into a Try/Catch loop, as if the file isn't able to be deleted (read-only, currently in use, no longer exists, etc.), it will throw an exception.

EDIT: How are the paths stored? Remember, in C# you need to escape out paths "//" instead of using "\" IIRC.

EDIT 2: Scratch that last edit out lol.

share|improve this answer
Escaping is only needed for literals, not when reading strings from somewhere. Note that escaping concerns the C# language, not the internal representation of a string. –  chiccodoro Jun 22 '10 at 6:15
I don't even know what escape out paths "//" would even mean. –  Gabe Jun 22 '10 at 6:18
Ah. Whenever I've programmed with C# I've just escaped out the paths. Probably since I've hardcoded the paths - thank you for the clarification. –  Jeffrey Kern Jun 22 '10 at 6:18
@Gabe, if you hardcode a path in C#, you would need to use // instead of \ for file paths. E.g., String foo = "c://foo.txt" instead of "c:\foo.txt" –  Jeffrey Kern Jun 22 '10 at 6:20

I think it's alright in spirit, though it would be closer to:

List<string> dbFiles = db.allPaths();
string[] allFiles = Directory.GetFiles(path);

foreach (string f in allFiles)
    if (!dbFiles.Contains(f))
share|improve this answer
Directory.EnumerateFiles is fine - it's part of .NET 4. Other than that, as far as I can see you've only replaced "var" with explicit typing, and removed braces. –  Jon Skeet Jun 22 '10 at 6:30
That would explain it... –  Reinderien Jun 22 '10 at 6:45

Your Answer


