MVVM/IoC Should I Wrap Every IO Operation?

Viewed 116

In C# code following IoC standards, should every single IO operation be wrapped within a class handling IO operations? For example, I'm using File.Exists and Directory.Create all over the place -- should I have a class exposing these 2 functions and every single file operation that the whole application uses, to create a layer of abstraction?

What about Path.Combine, or Path.DirectorySeparatorChar, can I use that directly or should I also create wrappers around them?

Returning file info becomes a bit more tricky, I can have a function to return file size, but if I need to access several properties, then I return the FileInfo object -- shouldn't I just rather initialize FileInfo in the code instead of wrapping it?

2 Answers

Found the answer.

Not wrapping IO calls means you can't unit-test the class because it will alter real files instead of running in a sandbox.

That means, yes, every call must be wrapped. Luckily, System.IO.Abstraction provides such abstraction so just plug it into the project and use that.

Then I can create IFileSystemExt to expose common sets of IO operations, such as "ensure the folder of the path exists" and "delete file if it exists".

One can't and shouldn't universally state what must and mustn't be done in the context of Dependency Injection (DI). It really depends on the type of problem one attempts to address. If the only concern is testability, then hiding anything that is non-deterministic, or has side-effects, may be desirable.

Note, though, that that doesn't include Path.Combine or Path.DirectorySeparatorChar. These members are, IIRC, entirely deterministic; they don't access the file system when you invoke them.

A common approach to DI, however, is to apply the Dependency Inversion Principle (DIP). According to this principle, the client code decides and controls the shape of the polymorphic APIs it uses. Once the client code has flushed out the interfaces that it requires, you go and figure out how to implement those interfaces.

Many people attempt to address testability against the Windows file system with something like System.IO.Abstractions, but that completely violates the DIP. Instead of letting clients define the shape of interfaces, it lets an implementation define the API. This is a leaky abstraction, if it's an abstraction at all.

Related