Throw Out Your Dependency Injection Container

Dependency Injection

Dependency injection containers (aka inversion of control containers) seem to be common in most modern applications.  There is no argument against the value dependency injection, but I do have a couple arguments against using a dependency injection container.  Like many other design patterns and practices, over time the development community seems to forget the original problem the pattern or practice was solving.

Constructor & Setter Injection

Passing your dependencies via the constructor is generally a better way of injecting dependencies instead of  setters. Constructor injection gives you a much clearer definition of what dependencies are needed in order to construct the object into a valid state.  My dependencies fields are usually readonly, to prevent them from being externally set after object creation.

Regardless if you use constructor or setter injection with your container of choice, there is often one injection method that is seemingly rarely used or mentioned.  Pass your dependency to the method that requires it.

How often have you seen a constructor take a pile of dependencies via it’s constructor, to which those dependencies were used in a limited amount of methods.  I often see this when looking at CQRS Command Handlers.

class InventoryHandler
{
	private readonly IRepository _repository;
	private readonly IBus _bus;
	private readonly IValidateInventory _validation;
	
	public InventoryHandler(IRepository repository, IBus bus, IValidateInventory validation)
	{
		_repository = repository;
		_bus = bus
		_validation = validation;
	}

	public void Handle(ReceiveInventory cmd)
	{
		if (!_validation.IsAvailable(cmd.ProductId)) {
			throw new InvalidOperationException("Cannot receive inventory");
		}

		// Receive Product into Inventory
		var inventory = _repository.GetById(cmd.ProductId);
		inventory.Receive(cmd.Quantity, cmd.UnitCost);
	}

	public Handle(RemoveInventory cmd)
	{
		// Remove product from inventory
		var inventory = _repository.GetById(cmd.ProductId);
		inventory.RemoveInventory(cmd.Quantity);

		// Pub/Sub to notify other bounded context of inventory removal
		_bus.Send(new InventoryRemovedMessage(cmd.InventoryId));
	}
}

In this simplified example, IRepository is used in both Handle() methods. However, IBus & IValidateInventory are both only used in one method each.

Instead of passing the dependencies via constructor, pass the required dependencies to the method that requires it.

class InventoryHandler
{
	private readonly IRepository _repository;
	
	public InventoryHandler(IRepository repository)
	{
		_repository = repository;
	}

	public void Handle(IValidateInventory validation, ReceiveInventory cmd)
	{
		if (!validation.IsAvailable(cmd.ProductId)) {
			throw new InvalidOperationException("Cannot receive inventory");
		}

		// Receive Product into Inventory
		var inventory = _repository.GetById(cmd.ProductId);
		inventory.Receive(cmd.Quantity, cmd.UnitCost);
	}

	public Handle(IBus bus, RemoveInventory cmd)
	{
		// Remove product from inventory
		var inventory = _repository.GetById(cmd.ProductId);
		inventory.RemoveInventory(cmd.Quantity);

		// Pub/Sub to notify other bounded context of inventory removal
		bus.Send(new InventoryRemovedMessage(cmd.InventoryId));
	}
}

 

Nested Dependencies

The primary reason why I prefer not to use a dependency injection container is because it potentially masks a code smell.  Nested dependencies or over abstraction can add complexity by adding unnecessary layers.

Have you seen something similar to this before?

public class MyService(IRepository repostiory) { ... }
public class Repository(IUnitOfWork unitOfWork) { ... }
public class UnitOfWork(IDbContext dbContext) { ... }
public class DbContext(IDbConnection dbConnection) { ... }
public class DbConnection(string connectionString) { ... }

Code SmellIf you are using a dependency injection container, you may not have noticed the excessive and unneeded abstraction layers in parts of you application.  If you had to manually wire up up these nested dependencies through all the layers, it would be really painful.

Masking the pain by using a dependency injection container is making your application more complex.

Take a look at your application and see if you can remove your dependency injection container.   If you can, then what benefits did it provide?  If you can’t, ask yourself if you really need all those layers.

Add a build number to your Assembly Version

TeamCity

Including the build number in your assembly version number can be a very useful feature.  Using reflection you can retrieve you assembly version number and display it appropriately in your app. Here is how to add a build number to your Assembly Version using TeamCity continuous integration server.

AssemblyInfo Patcher

TeamCity is a continuous integration server developed by JetBrains.  If you are looking at trying a new build server, I highly recommend giving it a try.   It should take no longer than a hour or two to install, configure, and build your project.

There is a built-in build feature that allows you to modify the AssemblyInfo.cs during the build process.  This feature works by scanning for all AssemblyInfo files (.cs, .vb, .cpp, .fs) in their usual file locations and replaces the AssemblyVersion, AssemblyFileVersion and AssemblyInformationVersion attributes with the values you define.

 Add Build Feature

Under your build configuration settings, add a new Build Feature.

TeamCity

Select the AssemblyInfo patcher.  Here you will be able to specify the version format by using parameters or static text.  In my example below, I’m including the %build.counter% parameter in the version format.

TeamCity

 

That’s it!  After the build is complete, the assembly outputted now contains the file version with build number.

TeamCity

Visual Studio Online Check-In Policies

Visual Studio Online Check-In Policies

Want to produce better code and more efficient development group? Start using Visual Studio Online check-in policies within your team project.

Check-in Policies are rules you can define at a Visual Studio Online team project which are enforced when a developer attempts to check-in their source code.

Note: You must be using Team Foundation Version Control (TFVC) with your project in order to use the check-in policies.  Although VSO now supports Git as a version control system for your team project, check-in policies are not supported.

One of the main reasons I started using check-in policies was to enforce associated work items to a changeset.  Having all changsets associated to work enabled me to automate the creation of a change lot during the build process.  If you are looking for better way to visualize your Visual Studio Online work items, take a look at my LeanKit Visual Studio Online Integration blog post.

Source Control Settings

From the Team Explorer, access the settings menu and select Source Control under the Team Project heading.

Visual Studio Online Check-In Policies

 

Under the Check-in Policy tab, click the Add button to select the policy you want to add.

Visual Studio Online Check-In Policies

Without any extension or power tools, Visual Studio Online provides four team project check-in policies that you can specify:

Builds
Requires that the latest build was successful for each affected continuous integration build definition.

Changeset Comments Policy
Requires the developer to provide comments with the check-in.  These comments will be associated with the changeset.

Code Analysis
Requires the developer to run Code Analysis from Visual Studio prior to check-in.

Work Items
Requires the developer to associate at least one work item with the check- in.

Code Review Policy

There is a great policy written by Colin Dembovsky (@colindembovsky) on Visual Studio Gallery that provides Code Review Policy.

Once the extension is installed you will now have a new option available to add.

Visual Studio Online Check-In Policies

Visual Studio Online Check-In Policies

 

Once this policy is added, you will be unable to check-in until a Code Review has been requested, closed, and has no “Needs Work” response.

CheckIn5