简体   繁体   中英

Dependency injection with a .Net Class Library?

I have a Class library that does a lot of File IO. Its a bit difficult to test so I wanted to start using the System.IO.Abstractions package. It has an interface that you can implement with either the real file system, or a mocked one.

So when the code is running in production I want the real file system but when testing I want to mock it. My class doing IO stuff looks like this.

private readonly IFileSystem _fileSystem;

public Service(){
   _fileSystem = new FileSystem();  //In test we want Mock file system here
}

internal bool Run(){
   string[] sourceFilePaths = _fileSystem.Directory.GetFiles(_sourceDirectory, "*.xml", SearchOption.AllDirectories);
}

Now I want to use Dependency Injection in order to populate the _fileSystem instance in order to determine what to use. The issue is that I do not know how to do this in a Class Library. I have found tutorials but it seems that the consumer of the library have to do the injection. My problem is that this class library is packaged and uploaded to a platform. This platform cannot do anything with the package before using it. So somehow the library has to figure out itself what instance to use.

One way I can think of is using the debug notation,

#if DEBUG  #endif

But of course its messy and spreads the configuration around.

What is the proper way to use DI in a Class Library?

This is your problem:

public Service(){
   _fileSystem = new FileSystem();  //In test we want Mock file system here
}

Your missing the injection part of dependency injection. This should look like this:

public Service(IFileSystem fileSystem){
   _fileSystem = fileSystem;  //In test we want Mock file system here
}

Why?

Now your class now longer a has a hard dependency on FileSystem , it now has a soft dependency on the interface for FileSystem , ( IFileSystem ).

Now when testing this class your can mock the interface:

//example using nsubstitue (I prefer moq but I've been using this lately)
var mockIFileSystem = Substitute.For<IFileSystem>();
Service testInstance = new Service(mockIFileSystem);

In production code you now have two choices. You could just inject all the dependencies yourself:

Service testInstance = new Service(new FileSystem);

this becomes clunky fast. Or you could use a injection framework (I recommend SimpleInjector though this is just my opinion):

Dependency injection framework (with simple injector)

With a DI framework you register all your dependencies and allow the framework to resolve them for you:

var container = new SimpleInjector.Container();

// Registrations here
container.Register<IFileSystem, FileSystem>();
container.Register<IService, Service>();

// Request instance
IService services = container.GetInstance<IService>();

Note: I never create an instance of IFileSystem above, the DI framework resolves it for me.

for more information I'd suggest you have a look at similar questions, such as What is dependency injection?

Class libraries

There is nothing special about class libraries here. All you need to do is ensure that the dependency registration is called and that any consumer gets the initial dependency from your registration, or allow them to inject their own registration. There are multiple patterns to this and the correct solution depends on your goals.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM