Last week, I had the honour of presenting a live webinar for JetBrains where I gave a presentation about using Dependency Injection in .NET Core and .NET 5.
A big thank you to JetBrains and to Maarten Balliauw for hosting.
Last week, I had the honour of presenting a live webinar for JetBrains where I gave a presentation about using Dependency Injection in .NET Core and .NET 5.
A big thank you to JetBrains and to Maarten Balliauw for hosting.
In this first part of a series of occasional posts, I discuss the thinking behind taking string based HTTP Headers and presenting them to your .NET code via dependency injection as strongly typed objects.
If you have read my previous blog posts or seen my talks, you will be aware that I am a big fan of the configuration binding functionality in .NET Core (now .NET 5) that takes string key/value pairs stored in various ways (such as JSON or environmental variables) and binding them to a typed object that is exposed via the .NET Dependency Injection container.
Recently, I have been giving some thought to other areas in ASP.NET Core that would benefit from this ability to bind string data into object instances, in particular looking at HTTP request headers.
In ASP.NET Core, HTTP request headers are presented as an instance of the IHeaderDictionary interface which is primarily a (dictionary) collection of key/value pairs made up of a string value for the key (the header key) and the header values within a StringValues struct (which itself is a special collection of strings that “represents zero/null, one, or many strings in an efficient way“).
The purpose of the interface is to present a collection of HTTP headers in a consistent manner. HTTP headers can have multiple declarations within the incoming request (see the RFC specification) which need to be parsed and grouped together by key before being presented in the collection.
Whilst the interface provides a consistent manner to access the headers, if you want interrogate the incoming request headers, you have a few hoops to jump through, namely
In other words you need code to get to HttpContext.Request.Headers.
If you have several headers that you want to access, you have to repeat this process for each of the headers you require.
Once you have your value(s) for a header, these are still strings that you may want to convert into some other type such as integer or GUID, which then means having to parse the value(s) which then raises a number of other questions:
In each of these scenarios, there is the potential for an exception to be thrown if assumptions are made about the incoming data and guards are not put in place to handle non-expected scenarios (E.g. using TryParse instead of Parse when converting strings to other primitive types to avoid exceptions when parsing fails).
Assuming all of the above is working correctly and you have managed to get the value(s) you require out of the headers, there is the question of passing the data from the controller action (or middleware) to some domain model or function.
Ideally, your domain logic and entities should avoid any bindings to web related types such as IHeaderDictionary & HttpContext. Avoiding this coupling to web concepts means they can be used in other applications (console, desktop or mobile applications) where the values that are received via the headers in a web application/API service may (or may not) be obtained in other ways (user input, configuration or hard coded).
I recently read Andrew Lock’s blog about primitive obsession where he discusses the danger of passing values around as standard data type (string, integer, GUID et al) as these are interchangeable values that do not have context.
The post goes on to put forward an implementation of wrapping these values into ‘strong types’ that give context to a value. E.g. a customer id value expressed as a GUID is interchangeable with a product id that is also a GUID, but an instance of a CustomerIdentity type is not interchangeable with a ProductIdentity.
After having read Andrew’s series that shows how to create strong types that are struct based, I then went to to read Thomas Levesque’s series that is based on Andrew’s series, but this time implementing using C# 9 record types in .NET 5.
I highly recommend reading both of these.
The principle I want to carry through is that each HTTP header of interest to the application should be mapped into a strongly typed instance to give the value(s) some meaning and context beyond just being an incoming value. In addition, these types should abstract away the need for callers to have any knowledge of HTTP related classes and interfaces.
With all of the above in mind, I have started a library to remove the need to keep writing a lot of the commonly required code. The requirements I have are
The library is still an initial work in progress at time of writing, but if you are interested in how I have approached the above, the code is available in a repository at https://github.com/stevetalkscode/TypedHttpHeaders
As I progress with the library, I plan to have more posts in this series where I will be walking through how the code works in order to achieve the goals above and eventually hope to release a NuGet package.
Following on from Parts 1 and 2, in this final part of the series, I move on to dealing with types that you do not have source control for and therefore cannot change directly to hide the Dispose method using the techniques I have described in the previous posts.
In the previous two parts of this series, I have made the assumption that you are able to amend the source code for types that implement IDisposable.
But what happens, if you don’t have the source code? This is where some well known design patterns come in useful.
When trying to hide disposability from container consumers, the general principle (as shown in the previous parts of this series), is to use an interface that excludes the dispose method from its definition so that the consumer only receives the instance via the interface and not as the concrete class, thus hiding the Dispose method.
If we do not have the source code, we need to create an intermediary between the interface that we want to expose and the type that we want to use.
There are four classic design patterns , each a variation of the intermediary theme, that we can use together to achieve our goal.
I will not go into an in depth description of the patterns here as there are plenty of resources that can be a much better job, however, here is a brief overview.
You may already have another interface, either your own or some other third party that achieves the goal, but for which you do not have the source code or cannot change as it would break other dependencies. In this case, the adapter pattern is used to fill the gaps to make the two interfaces work with each other.
If the desired interface does not already exist, this is becomes the bridge pattern which does the same thing, but the interface design is within your control (whereas adapter uses an interface outside your control)
The core aim of our intermediary is to remove the Dispose method and in effect simplify the interface. If the third party has a number of members that you are not interested in or that need to be brought together into a single method, the Façade pattern can be used.
The proxy pattern is used to prevent direct access to an object. It usually has an identical interface to the class that it represents.
The decorator pattern is similar to the proxy pattern, but it can may additional functionality to enhance behaviour.
For our purposes, you are likely to use an adapter pattern if you do not have control over either interface, but more likely, you will be designing the interface to be used by consumers and therefore will write a custom class that brings together elements of the other three patterns, namely
In the example below we have a class SomeDisposable that we do not have the source code for. The class implements IDisposable and has multiple methods, but only one of interest, namely – DoSomething().
Rather than register the class directly, we want to wrap it with an intermediary that will
Once we have the intermediary class, we can register this with the container in the StartUp class with the façade interface as the service type.
With this in place, we have managed to avoid consumers being able to dispose of the DoSomething singleton instance directly as it is hidden inside the intermediary class but the intermediary class (and in turn the inner instance) is still disposable by the container, but having put a façade in place, the consumer cannot call Dispose() directly.
That is the end of this series on preventing consumers causing problems by disposing of objects that may have a longer lifetime than the consumer when under the control of the .NET DI container.
I hope it has been of use.
In this post I will be discussing the traps that can catch you out by potentially creating memory leaks when registering types that implement the IDisposable interface as services with the out-of-the-box .NET Dependency Injection container.
Typically, a type will implement the IDisposable interface when it holds unmanaged resources that need to be released or to free up memory.
More information about cleaning up resource can be found on Microsoft Docs
To keep things simple for the rest of this post, I will be referring to instances of types that implement IDisposable as “Disposable Objects”.
Outside of dependency injection, if you create an instance of such a type, it is your responsibly to call the Dispose method on the class to initiate the release of unmanaged resources.
This can be done either
The two approaches are documented on the Microsoft Docs site.
As a general rule, if the Dependency Injection container creates an instance of the disposable object, it will clean up when the instance lifetime (transient, scoped or singleton) expires (E.g. for scoped instances in ASP.NET Core, this will be at the end of the request/response lifetime but for singletons, it is when the container itself is disposed).
The following table (based on the table in the Microsoft Docs page) shows which registration methods will trigger the container to automatically dispose of the object.
As you can see from the table above, the three most common methods for adding services, where the container itself is responsible for creating the instance, will automatically dispose of the object at the appropriate time.
However, the last two methods do not dispose of the object. Why? It’s because in these methods, the objects have been directly instantiated with a new keyword and therefore, the container has not been responsible for creating the object.
Whilst they look similar to the third method, the difference is that the instance in that method has been created within the context of a lambda expression which is within the control of the container and therefore in the container’s control.
In the last two methods, the object could be created at the time of registration (by using the new statement) but then again, it may have been created outside these methods (either within the scope of the ConfigureServices method in the StartUp class, or at a class level) and therefore, the container cannot possibly know of where the object has been created, the scope of its reference, and where else it may be used. Without this understanding, it cannot safely dispose of the object as this may throw an ObjectDisposedException if referenced elsewhere in code after the container has disposed of it.
I will come on to dealing with ensuring these objects referenced in these last two methods can be disposed of correctly in Part 2.
The first method in the table above is the simplest way to register a type. Consumers will request an instance of the object and make use of it.
However, if the type implements IDisposable, this means that the Dispose method is available to the consumer to call. This has repercussions depending on the lifetime that the dependency has been registered as.
For transients that have been created specifically to be injected into the consuming class, it is not the end of the world. If dispose is called on a transient, the only place that will suffer is the consuming class (and anything it passes the reference to) as any subsequent references to the object (or to be more specific, members in the type that check the disposed status) are likely to result in an ObjectDisposedException (this will depend on the implementation of the injected class).
For scoped and singleton lifetimes, things become more complicated as the object has a lifetime beyond the consumer class. If the consuming class calls Dispose and another consumer then also makes use of a member on the disposed class, that other consumer is likely to receive an ObjectDisposedException.
Therefore, we want to ensure that the Dispose method on the registered class is somehow hidden from the consumer.
There are several ways of hiding the Dispose method which are considered below
The quick (and dirty) way of hiding the Dispose method that exists on a class is to change the Dispose method’s declaration from a public method to an explicit interface declaration (as shown below) so that it can only be called by casting the object to IDisposable.
It should, however, be recognised that this is just obfuscating the availability of the Dispose method. It does not truly hide it as the consumer may be aware that the type implements IDisposable and explicitly cast the object and call Dispose.
This is where extracting out other interfaces comes to our rescue when it comes to dependency injection.
If we define an interface that has all the public members of our class except for the Dispose method and only make the object available by registering it in the DI container with the limited interface as the service, this will make it harder (but not completely impossible) for the consumer of the object to dispose of the object as the concrete type is only known to the container registration (unless the consumer uses GetType() of course, but that is splitting hairs and in many ways negates the whole point of using the container).
Of course, following the Interface Segregation Principle from SOLID, this interface may be broken down into smaller interfaces which the class registered against.
In Part 2 of this series on IDisposable in Dependency Injection, I will move on to dealing with those objects that the container will not dispose of for you.
With the release of .NET 5.0 and C# 9 coming up in the next month, I have been updating my Dependency Injection talk to incorporate the latest features of the framework and language.
One of the topics of the talk is using the “Gang of Four” Factory pattern to help with the creation of class instances where some of the data is coming from the DI container and some from the caller.
In this post, I walk through using the Factory Pattern to apply the same principles to creating instances of C# 9 records.
Before getting down into the weeds of using the Factory Pattern, a quick overview of how C#9 record types differ from classes.
There are plenty of articles, blog posts and videos available on the Internet that will tell you this, so I am not going to go into much depth here. However, here are a couple of resources I found useful in understanding records vs classes.
Firstly, I really like the video from Microsoft’s Channel 9 ‘On.NET’ show where Jared Parsons explains the benefits of records over classes.
Secondly, a shout out to Anthony Giretti for his blog post https://anthonygiretti.com/2020/06/17/introducing-c-9-records/ that goes into detail about the syntax of using record types.
From watching/reading these (and other bits and bobs around the Internet), my take on record types vs. classes are as follows:
The thing that really impressed me when watching the video is the way that the record syntax is able to replace a 40 line class definition with all the aforementioned boiler plate code with a single declaration
In most cases where you may choose to use a record type over using a class type, you won’t need a factory.
Typically you will have some framework doing the work for you, be it a JSON deserialiser, Entity Framework or some code generation tool to create the boilerplate code for you (such as NSwag for creating client code from Swagger/OpenAPI definitions or possibly the new C#9 Source Generators).
Similarly, if all the properties of your record type can be derived from dependency injection, you can register your record type with the container with an appropriate lifetime (transient or singleton).
However, in some cases you may have a need to create a new record instance as part of a user/service interaction by requiring data from a combination of
You could instantiate a new instance in your code, but to use Steve ‘Ardalis’ Smith‘s phrase, “new is glue” and things become complicated if you need to make changes to the record type (such as adding additional mandatory properties).
In these cases, try to keep in line with the Don’t Repeat Yourself (DRY) principle and Single Responsibility Principle (SRP) from SOLID. This is where a factory class comes into its own as it becomes a single place to take all these inputs from multiple sources and use them together to create the new instance, providing a simpler interaction for your code to work with.
When looking at the properties required for your record type, consider
For example, a Product type may have the following properties
The record is declared as follows
You may have several places in your application where a product can be created, so adhering to the DRY principle, we want to encapsulate the creation process into a single process.
In addition, the only property coming from actual user input is the product name, so we don’t want to drag all these other dependencies around the application.
This is where the factory can be a major help.
I have created an example at https://github.com/stevetalkscode/RecordFactory that you may want to download and follow along with for the rest of the post.
In my example, I am making the assumption that
(You may notice that there is not an implementation of GetCurrentTimeUtc. This is provided directly in the service registration process in the StartUp class below).
In order to make this all work in a unit test, the date/time generation and user identity generation are provided by mock implementations that are registered with the service collection. The service registrations in the StartUp class will only be applied if these registrations have not already taken place by making user of the TryAddxxx extension methods.
As the record itself is a value type, it does not need to know how to obtain the information from these participants as it is effectively just a read-only Data Transfer Object (DTO).
Therefore, the factory will need to provide the following in order to create a record instance:
The factory will have one method Create() that will take a single parameter of productName (as all the other inputs are provided from within the factory class)
Over the years, the Service Locator pattern has come to be recognised as an anti-pattern, especially when it requires that the caller has some knowledge of the container.
For example, it would be easy for the ProductFactory class to take a single parameter of IServiceProvider and make use of the GetRequiredService<T> extension method to obtain an instance from the container. If the factory is a public class, this would tie the implementation to the container technology (in this case, the Microsoft .NET Dependency Injection ‘conforming’ container).
In some cases, there may be no way of getting around this due to some problem in resolving an dependency. In particular, with factory classes, you may encounter difficulties where the factory is registered as a singleton but one or more dependencies are scoped or transient.
In these scenarios, there is a danger of the shorter-lived dependencies (transient and scoped) being resolved when the singleton is created and becoming ‘captured dependencies’ that are trapped for the lifetime of the singleton and not using the correctly scoped value when the Create method is called.
You may need to make these dependencies parameters of the Create method (see warning about scoped dependencies below), but in some cases, this then (mis)places a responsibility onto the caller of the method to obtain those dependencies (and thus creating an unnecessary dependency chain through the application).
There are two approaches that can be used in the scenario where a singleton has dependencies on lesser scoped instances.
The first approach is to adopt the service locator pattern (by requiring the IServiceProvider as described above), but making the factory a private class within the StartUp class.
This hiding of the factory within the StartUp class also hides the service locator pattern from the outside world as the only way of instantiating the factory is through the container. The outside world will only be aware of the abstracted interface through which it has been registered and can be consumed in the normal manner from the container.
The second way of getting around this is to add a level of indirection by using a custom delegate.
The extension methods for IServiceCollection allow for a service locator to be embedded within a service registration by using an expression that has access to the IServiceProvider.
To avoid the problem of a ‘captured dependency’ when injecting transient dependencies, we can register a delegate signature that wraps the service locator thus moving the service locator up into the registration process itself (as seen here) and leaving our factory unaware of the container technology.
At this point the factory may be made public (or left as private) as the custom delegate takes care of obtaining the values from the container when the Create method is called and not when the (singleton) factory is created, thus avoiding capturing the dependency.
Wrapping access to transient registered dependencies with a singleton delegate works as both singleton and transient instances are resolved from the root provider. However, this does not work for dependencies registered as scoped lifetimes which are resolved from a scoped provider which the singleton does not have access to.
If you use the root provider, you will end up with a captured instance of the scoped type that is created when the singleton is created (as the container will not throw an exception unless scope checking is turned on).
Unfortunately, for these dependencies, you will still need to pass these to the Create method in most cases.
If you are using ASP.NET Core, there is a workaround (already partially illustrated above with accessing the User’s identity).
IHttpContextAcessor.HttpContext.RequestServices exposes the scoped container but is accessible from singleton services as IHttpContextAcessor is registered as a singleton service. Therefore, you could write a delegate that uses this to access the scoped dependencies via a delegate. My advice is to approach this with caution as you may find it hard to debug unexpected behaviour.
In the example, the factory class is registered as a singleton to avoid the cost of recreating it every time a new Product is needed.
The three dependencies are injected into the constructor, but as already mentioned the transient values are not captured at this point – we are only capturing the function pointers.
It is within the Create method that we call the delegate functions to obtain the real-time transient values that are then used with the caller provided productName parameter to then instantiate a Product instance.
I’ll leave things there, as the best way to understand the above is to step through the code at https://github.com/stevetalkscode/RecordFactory that accompanies this post.
Whilst a factory is not required for the majority of scenarios where you use a record type, it can be of help when you need to encapsulate the logic for gathering all the dependencies that are used to create an instance without polluting the record declaration itself.
If you have come here from a search engine, I suggest reading Part 1 first to get some background as to the problem that this post solves.
In Part 1, I gave the background as to why you may have a requirement to have a way of resolving instances from a DI container using a string name or some other key.
In this part, I propose a solution that leaves your code ignorant of any DI container and that is easily mockable for unit tests.
A key thing to understand about DI containers is that the service type that is registered is exactly that – a type, not just interfaces and classes. Therefore, you can register delegates as well.
However, for .NET Core, when you search for something like ‘.NET Core named dependency injection’, you tend to only find the answer buried in StackOverflow answers.
That is why I have written this post, so that I can find the answer later myself, and also hopefully help other find it too.
The generic Func type was introducted in C# 3.0 and is a generic delegate included in the
System namespace that has multiple overloads that accept between zero and sixteen input parameters (denoted T1 to T16) and a single output parameter (TResult). These are convenience types so that as a developer you do not need to repeatedly create your own customer delegates.
For the purposes of retrieving an instance of a type from the DI container, the various Func delegates can be used as the type for service registration and as a parameter to the constructors of classes. E.g. Func<string, IServiceType>.
However, I have two reasons for not using this approach.
By using typed delegates, the code that requires the named/keyed dependency is ignorant of the DI container technology that is resolving the instance for it.
This differs from other approaches where either the consuming class itself or an intercepting factory class takes the IServiceProvider as a dependency in its constructor and has logic to resolve the instance itself which defeats the purpose of dependency injection as it is creating a glue between the classes.
By registering the delegate within the DI container registration code, it keeps the codebase (outside of Di registration) ignorant of how the name/key resolution is working.
For the rest of this post, I am going to use a test project (which can be found on GitHub at https://github.com/configureappio/NamedDiDemo) that converts temperatures to the Kelvin scale from four different scales – Centigrade, Fahrenheit, Rankine and Kelvins.
To do this, there are four formulas that are needed
|Celcius(°C) to Kelvin(K)||T(K) = T(°C) + 273.15|
|Fahrenheit(°F) to Kelvin(K)||T(K) = (T(°F) + 459.67) x 5 / 9|
|Rankine to Kelvin(K)||T(K) = T(°R) × 5/9|
|Kelvin(K) to Kelvin(K)||T(K) = T(K)|
Each formula is implemented as a mapper class with an implementation of the IKelvinMapper interface as defined below.
Each of the implementations is registered with the DI container
For the caller to retrieve the correct conversion, we need a delegate that uses the input temperature scale in order to retrieve the correct mapper and return the mapper as the IKelvinMapper interface
Lastly, a lookup function is registered with the DI container against the delegate type as the service type to perform the lookup of the correct mapper. To keep things simple, the lookup is based on the first (capitalised) letter of the input temperature scale (normally as more robust lookup would be used, but I’ve tried to keep it simple so as not to distract from the core purpose of what I am demonstrating).
With the returned interface implementation, a conversation from the input temperature scale and value can be made to a consistent value in the Kelvin scale.
The demo works on a very simple basis of leveraging the DI container to do all the hard work of determining which mapping converter is required. The caller just needs to know that it uses the delegate to take in the input temperature scale and value and return the value in Kelvins.
Depending on the complexity of the type to be instantiated, there are three ways to instantiate the object:
This is where you take advantage of the ability to register a service resolution using a lambda expression as it provides access to the IServiceProvider container instance to retrieve instances from the service provider for (2) and (3) above.
For simple keys, you can use a case statement inside a lambda expression to derive the type required and let the DI container perform the object instantiation for you. (This is the approach I have used in the demo project).
However, if you have many different items or complex keys, you may want to consider other ways of deriving the service type required.
For example, by registering a singleton Dictionary<string, serviceType> to map keys to types, if there are many keys, you may gain a performance boost from the hash lookup algorithm.
Instead of using the out-of-the-box generic dictionary thought, I recommend creating a dedicated class that implements IDictionary<string, Type> for the same reasons that I recommend using custom delegates over the generic Func<T> – it removes any ambiguity if there is (deliberately or inadvertently) more than one registration of the generic dictionary.
Careful thought needs to be given to the lifetime of both the factory delegate and the object being retrieved from the DI container.
Typically, as factory classes have no state, they can usually be registered with a singleton lifetime. The same applies to the delegate being used as the factory or to access the factory class.
Where things get complicated is when the service type that the factory is instantiating for you has been registered in the DI container as having a scoped lifetime.
In this case, the resolution within the factory fails as the DI container cannot resolve scoped dependencies for constructor injection inside a singleton or transient lifetime object (in our case, the singleton factory delegate).
There are two ways to get around this problem.
(i) Register the factory delegate as scoped as well
(ii) Change the singleton factory delegate to take an IServiceProvider as part of the function signature. It is then the caller’s responsibility to pass the correctly scoped IServiceProvider to the delegate. However, this effectively takes us back to a service locator pattern again.
Now that we have abstracted the mapper instance generation into a delegate function, the client just needs to be able to resolve the instance.
For classes that want to use the mapper (and are also registered with the DI container), this is simply a case of making the delegate a constructor parameter.
Hopefully, these two posts have provided some insight into how the limitations of the Microsoft DI container to support named or keyed resolutions can be worked around without having to resort to using a third-party container or custom service locator pattern implementation.
Whilst not part of this two-part blog post, I have written another post about simplifying the demo to work in a more functional way by replacing the interface implementations with direct functions to (a) reduce the amount of code and (b) reduce the number of memory allocations required to create the objects.
If you want to go straight to the solution of how to resolve instances by a name or key without reading the background, go to Part 2 of this post.
Many dependency injection container solutions support the concept of labeling a registered dependency with a name or a key that can be used by the container to resolve the dependency required when presented by a dependent class.
For instance, Autofac supports both named and keyed labeling using a syntax like this to register the dependency:
It provides both an explicit method to resolve the dependency, and an attributed way on the constructor parameters of a class.
However, the container that is provided out of the box with .NET Core does not support named or keyed registrations.
The Microsoft DI Container is a ‘Conforming Container‘ that provides a common abstraction that can be wired up to other containers such as Autofac, Ninject, StructureMap et al.
As a result, it only supports the lowest common denominator across multiple DI solutions, and more accurately, only what Microsoft deemed as necessary for its purposes.
This means that if you want to use features such as named/keyed resolution, you end up having to mix and match both the Microsoft DI Container and your other container of choice.
Now, most of the big name containers support this and provide methods that register the custom container so it can be retrieved in constructor injection and the custom features accessed using that containers methods.
However, the downside to this is that
The first and last of these issues can be mitigated by creating an interface for the functionality you require to abstract your code away from a particular container technology and then writing an adapter.
However, you are still have a service locator being injected into constructors in order to get to the abstracted functionality (in this case, obtaining an instance based on a name or key).
To an extent, I agree that using a name string or key to resolve an instance is effectively a service locator as it is forcing a lookup within the container to retrieve an instance of the required type.
Under normal circumstances, I would be looking at ways to avoid using this way of resolving an instance such as creating alias interfaces.
For example, say I have three different classes FooA, FooB & FooC that all implement an interface IFoo.
I have a class that has needs all three implementations to perform some functionality. I have two choices in how I can inject the classes.
I can (a) either explicitly define the three class types as parameters in the constructor of my class, or (b) I can register all three classes as IFoo and define a single parameter of IEnumerable<IFoo>.
The downside to (a) is that I am binding to the concrete implementations which may limit my unit testing if I am unable to access or mock the concrete instances in my unit test project. It also means that if I want to replace one of the classes with a different implementation I would need to change the constructor signature of my class.
The downside to (b) is that the class receives the implementation instances in the order that the container decides to present them (usually the order of registration) and the class code needs to know how to tell the implementations apart. This effectively is moving the service locator inside my class.
To get around this scenario, I could create three interfaces IFooA, IFooB and IFooC that each directly inherits from IFoo. The three class implementations would then be implementations of the appropriate interface and can be registered with the DI container as such.
The receiving class can then explicitly define IFooA, IFooB and IFooC as parameters to the constructor. This is effectively the same as we had before, but makes the class more testable as the interfaces can be easily mocked in unit testing instead of being bound to the concrete classes.
Alternatively, we can still leave the classes registered as implementations of IFoo and have a constructor that takes IEnumerable as its parameter. However, this will mean that our class has to iterate over the enumeration to identify which classes are implementations of IFooA, IFooB & IFooC and then cast as appropriate. It then also has to know how to handle multiple registrations of the three derived interfaces. E.g. what happens if there are three IFooA implementations? Which should be used? The first? The last? Chain all of them?
With all this said, I recently had a situation where I needed to resolve instances from the container using a string as the key, which is what got me thinking about this in the first place.
Say you have a data source that has child records in multiple different formats. The association is defined by an identifier and a string that indicates the type of data.
In order to correctly retrieve the data and then transform it into a common format suitable for export to another system, you are reliant upon that string to identify the correct mapper to use. In this situation, we need some way of being able to identify a mapper class instance from the identifying string in order to perform the correct mapping work required.
You could create a class mapping factory that maintains a dictionary of the acceptable strings that will be in the data and the type required for that string, then has a method that provides an instance of the type for a particular string. E.g.
The problem here is that your implementation of IFooFactory becomes a service locator as it will need the IServiceProvider passed to its constructor in order to be able to create the instance required.
Ideally, you want your code to be ignorant of IServiceProvider so that you do not have to mock it in your unit tests.
This is the point where you want your DI container to be able to be able to inject by name or key.
This is where I come to the whole point of this post.
We want our classes to be completely ignorant of the DI container being used and just be able to dynamically obtain the instance.
This is where our old friend, the delegate keyword can help us, which I describe in more detail in Part 2.
One of the things discussed was the separation of concerns, where Steve discusses creating an architecture in which you try to break up your application in such a way that hides implementation detail in one project from another consuming project. Instead, the consuming project is only aware of interfaces or abstract classes from shared libraries from which instances are are created by the dependency injection framework in use.
The aim is to try and guide a developer of the consuming project away from ‘new-ing’ up instances of a class from outside the project. To use Steve’s catch phrase, “new is glue”.
I was listening to the podcast on my commute to work and it got me thinking about the project I had just started working on. So much so, that I had to put the podcast on pause to give myself some thinking time for the second half of the commute.
What was causing the sparks to go off in my head was about how dependencies are registered in the Startup class in ASP.Net Core.
By default, when you create a new ASP.Net Core project, the Startup class is created as part of that project, and it is here that you register your dependencies. If your dependencies are in another project/assembly/Nuget package, it means that the references to wherever the dependency is has to be added to the consuming project.
Of course, if you do this, that means that the developer of the consuming project is free to ‘new up’ an instance of a dependency rather than rely on the DI container. The gist of Steve Smith’s comment in the podcast was do what you can to help try to prevent this.
When I got to work, I had a look at the code and pondered about whether the Startup class could be moved out to another project. That way the main ASP.Net project would only have a reference to the new project (we’ll call it the infrastructure project for simplicity) and not the myriad of other projects/Nugets. Simple huh? Yeah right!
So the first problem I hit was all the ASP/MVC plumbing that would be needed in the new project. When I copied the Startup class to the new project, Visual Studio started moaning about all the missing references.
Now when you create a new MVC/Web.API project with .Net Core, the VS template uses the Microsoft.AspNetCore.All meta NuGet package. For those not familiar with meta packages, these are NuGet packages that bundle up a number of other NuGet packages – and Microsoft.AspNetCore.All is massive. When I opened the nuspec file from the cache on my machine, there were 136 dependencies on other packages. For my infrastructure project, I was not going to need all of these. I was only interested in the ones required to support the interfaces, classes and extension methods I would need in the Startup class.
Oh boy, that was a big mistake. It was a case of adding all the dependencies I would actually need one by one to ensure I was not bringing any unnecessary packages along for the ride. Painful, but I did it.
So I made all the updates to the main MVC project required to use the Startup class from my new project and remove the references I previously had to other projects (domain, repository etc) as this was the point of the exercise.
It all compiled! Great. Pressed F5 to run and … hang on what?
After a bit of head scratching, I realised the problem was that MVC could not find the controller? WHY?
At this point, I parked my so-called ‘best practice’ changes as I did not want to waste valuable project time on a wild goose chase.
This was really bugging me, so outside of work, I started to do some more digging.
After reading some blogs and looking at the source code in GitHub, the penny dropped. ASP.Net MVC makes the assumption that the controllers are in the same assembly as the Startup class.
When the Startup class is registered with the host builder, it sets the ApplicationName property in HostingEnvironment instance to the name of the assembly where the Startup class is.
The ApplicationName property of the IHostingEnvironment instance is used by the AddMvc extension to register the assembly where controllers can be found.
Eventually, I found the workaround from David Fowler in an answer to an issue on GitHub. In short, you need to use the UseSetting extension method on the IWebHostBuilder instance to change the assembly used in the ApplicationName property to point to where the controllers are. In my case this was as follows:
Therefore, without this line redirecting the application name to the correct assembly, if the controllers are not in the same assembly as the Startup class, that’s when things go wrong – as I found.
With this problem fixed, everything fell into place and started working correctly.
However, with this up and running, something did not feel right about it.
The solution I had created was fine if all the dependencies are accessible from the new Infra project, either directly within the project or by referencing other projects from the Infra project. But what if I have some dependencies in my MVC project I want to add to the DI container?
This is where my thought experiment broke down. As it stood, it would create a circular reference of the Infra project needing to know about classes in the main MVC project which in turn referenced the Infra project. I needed to go back to the drawing board and think about what I was trying to achieve.
I broke the goal into the following thoughts:
By breaking down the goal, it hit me what is required!
To achieve the goal set out above, a hybrid of the two approaches is needed whereby the Startup and DI container registration remain in the main MVC project, but registration of classes that I don’t want to be directly accessed in the MVC project get registered in the Infra project so access in the MVC project is only through interfaces, serviced by the DI container.
To achieve this, all I needed to do was make the Infra project aware of DI registration through the IServiceCollection interface and extension methods, but create a method that has the IServiceCollection injected into it from the MVC project that is calling it.
The first part of the process was to refactor the work I has done in the Startup class in the Infra project and create a public static method to do that work, taking the dependencies from outside.
The new ConfigureServices method takes an IServiceCollection instance for registering services from within the infrastructure project, and also an IMvcBuilder as well, so that any MVC related infrastructure tasks that I want to hide from the main MVC project (and not dependent on code in the MVC project) can also be registered.
In the example above, I add a custom validation filter (to ensure all post-back check if the ModelState is valid rather than this being done in each Action in the MVC controllers) and add the FluentValidation framework for domain validation.
To make things a bit more interesting, I also added an extension method to use Autofac as the service provider instead of the out of the box Microsoft one.
With this in place, a took the Startup class out of the Infra project and put it back into the MVC project and then refactored it so that it would do the following in the ConfigureServices method:
I ended up with a Startup class that looked like this:
My description of the problem and my solution above only really scratches the surface, but hopefully it is of use to someone. It is probably better to let the code speak for itself, and for this I have created a Git repo with three versions of an example project which show the three different approaches to the problem.
First is the out-of-the-box do everything in the main project
Then there is the refactoring to do all the registration in the Infra project
Lastly, there is the hybrid where the Startup is in the main project, but delegates registration of non-MVC classes to the Infra project.
The example projects cover other things I plan on blogging about, so are a bit bigger that just dealing with separating the Startup class.
The repo can be found at https://github.com/configureappio/SeparateStartup
For details of the projects, look at the Readme.md file in the root of the repo.
In answer to the question posed in the title of this post, my personal view is that the answer is – “No” … but I do think that extracting out a lot of plumbing away from the Startup into another assembly does make things cleaner and achieves the goal of steering developers away from creating instances of classes and instead, relying on using the DI container to do the work it is intended for. This then helps promote SOLID principles.
Hopefully, the discussion of the trials and tribulations I had in trying to completely move the Startup.cs class show how painful this can be and how a hybrid approach is more suitable.
The underlying principle of using a clean code approach is sound when approached the correct way, by thinking through the actual goal rather than concentrating on trying to fix or workaround the framework you are using.
The lessons I am taking away from my experiences above are: