How can I pull objects from the container that are transient in nature? Do I have to register them with the container and inject in the constructor of the needing class? Injecting everything into the constructor doesn't feel good. Also just for one class I don't want to create a TypedFactory
and inject the factory into the needing class.
Another thought that came to me was "new" them up on need basis. But I am also injecting a Logger
component (through property) into all my classes. So if I new them up, I will have to manually instantiate the Logger
in those classes. How can I continue to use the container for ALL of my classes?
Logger injection: Most of my classes have the Logger
property defined, except where there is inheritance chain (in that case only the base class has this property, and all the deriving classes use that). When these are instantiated through Windsor container, they would get my implementation of ILogger
injected into them.
//Install QueueMonitor as Singleton
Container.Register(Component.For<QueueMonitor>().LifestyleSingleton());
//Install DataProcessor as Trnsient
Container.Register(Component.For<DataProcessor>().LifestyleTransient());
Container.Register(Component.For<Data>().LifestyleScoped());
public class QueueMonitor
{
private dataProcessor;
public ILogger Logger { get; set; }
public void OnDataReceived(Data data)
{
//pull the dataProcessor from factory
dataProcessor.ProcessData(data);
}
}
public class DataProcessor
{
public ILogger Logger { get; set; }
public Record[] ProcessData(Data data)
{
//Data can have multiple Records
//Loop through the data and create new set of Records
//Is this the correct way to create new records?
//How do I use container here and avoid "new"
Record record = new Record(/*using the data */);
...
//return a list of Records
}
}
public class Record
{
public ILogger Logger { get; set; }
private _recordNumber;
private _recordOwner;
public string GetDescription()
{
Logger.LogDebug("log something");
// return the custom description
}
}
Questions:
How do I create new
Record
object without using "new"?QueueMonitor
isSingleton
, whereasData
is "Scoped". How can I injectData
intoOnDataReceived()
method?
From the samples you give it is hard to be very specific, but in general, when you inject
ILogger
instances into most services, you should ask yourself two things:1. Do I log too much
You are logging too much, when you have a lot of code like this:
Writing code like this comes from the concern of losing error information. Duplicating these kinds of try-catch blocks all over the place however, doesn't help. Even worse, I often see developers log and continue (they remove the last
throw
statement). This is really bad (and smells like the old VBON ERROR RESUME NEXT
), because in most situations you have simply not enough information to determine whether it is safe continue. Often there is a bug in the code that caused the operation to fail. To continue means that the user often gets the idea that the operation succeeded, while it hasn't. Ask yourself: what is worse, showing the user a generic error message saying that there something gone wrong, or silently skipping the error and letting the user think his request was successfully processed? Think about how the user will feel if he found out two weeks later that his order was never shipped. You’d probably lose a customer. Or worse, a patient’s MRSA registration silently fails, causing the patient not to be quarantined by nursing and resulting in the contamination of other patients, causing high costs or perhaps even death.Most of these kinds of try-catch-log lines should be removed and you should simply let the exception bubble up the call stack.
Shouldn't you log? You absolutely should! But if you can, define one try-catch block at the top of the application. With ASP.NET, you can implement the
Application_Error
event, register aHttpModule
or define a custom error page that does the logging. With Win Forms the solution is different, but the concept stays the same: Define one single top most catch-all.Sometimes however, you still want to catch and log a certain type of exception. A system I worked on in the past, let the business layer throw
ValidationException
s, which would be caught by the presentation layer. Those exceptions contained validation information for display to the user. Since those exceptions would get caught and processed in the presentation layer, they would not bubble up to the top most part of the application and didn't end up in the application's catch-all code. Still I wanted to log this information, just to find out how often the user entered invalid information and to find out whether the validations where triggered for the right reason. So this was no error logging; just logging. I wrote the following code to do this:Looks familiar? Yes, looks exactly the same as the previous code snippet, with the difference that I only caught
ValidationException
s. However, there was another difference, that can't be seen by just looking at the snippet. There was only one place in the application that contained that code! It was a decorator, which brings me to the next question you should ask yourself:2. Do I violate the SOLID principles?
Things like logging, auditing, and security, are called cross-cutting concerns (or aspects). They are called cross-cutting, because they can cut across many parts of your application and must often be applied to many classes in the system. However, when you find you're writing code for their use in many classes in the system, you are most likely violating the SOLID principles. Take for instance the following example:
Here we measure the time it takes to execute the
MoveCustomer
operation and we log that information. It is very likely that other operations in the system need this same cross-cutting concern. You will start adding code like this for yourShipOrder
,CancelOrder
,CancelShipping
, etc. methods end this leads to a lot of code duplication and eventually a maintenance nightmare.The problem here is a violation of the SOLID principles. The SOLID principles are a set of object oriented design principles that help you in defining flexible and maintainable software. The
MoveCustomer
example violated at least two of those rules:MoveCustomer
method does not only move the customer, but also measures the time it takes to do the operation. In other words it has multiple responsibilities. You should extract the measuring into its own class.MoveCustomer
method, which is a violation of the OCP.Besides violating the SOLID principles we definitely violated the DRY principle here, which basically says that code duplication is bad, mkay.
The solution to this problem is to extract the logging into its own class and allow that class to wrap the original class:
By wrapping the decorator around the real instance, you can now add this measuring behavior to the class, without any other part of the system to change:
The previous example did however just solve part of the problem (only the SOLID part). When writing the code as shown above, you will have to define decorators for all operations in the system, and you'll end up with decorators like
MeasuringShipOrderCommandDecorator
,MeasuringCancelOrderCommandDecorator
, andMeasuringCancelShippingCommandDecorator
. This lead again to a lot of duplicate code (a violation of the DRY principle), and still needing to write code for every operations in the system. What's missing here is an common abstraction over use cases in the system. What's missing is anICommandHandler<TCommand>
interface.Let's define this interface:
And let's store the method arguments of the
MoveCustomer
method into its own (Parameter Object) class calledMoveCustomerCommand
:And let's put the behavior of the
MoveCustomer
method in a class that implementsICommandHandler<MoveCustomerCommand>
:This might look strange, but because we now have a general abstraction for use cases, we can rewrite our decorator as follows:
This new
MeasuringCommandHandlerDecorator<T>
looks much like theMeasuringMoveCustomerCommandDecorator
, but this class can be reused for all command handlers in the system:This way it will be much, much easier to add cross-cutting concerns to the system. It's quite easy to create a convenient method in your Composition Root that can wrap any created command handler with the applicable command handlers in the system. For instance:
If your application starts to grow however, it can get painful to bootstrap this all without a container. Especially when your decorators have generic type constraints.
Most modern DI Containers for .NET have fairly decent support for decorators nowadays, and especially Autofac (example) and Simple Injector (example) make it easy to register open generic decorators. Simple Injector even allows decorators to be applied conditionally based on a given predicate or complex generic type constraints, allows the decorated class to be injected as a factory and allows contextual context to be injected into decorators, all of which can be really useful from time to time.
Unity and Castle on the other hand have interception facilities (as Autofac does to btw). Interception has a lot in common with decoration, but it uses dynamic proxy generation under the covers. This can be more flexible than working with generic decorators, but you will pay the price when it comes to maintainability, because you will often loose type safety and interceptors always force you to take a dependency on the interception library, while decorators are type safe and can be written without taking a dependency on an external library.
Read this article if you want to learn more about this way of designing your application: Meanwhile... on the command side of my architecture.
I hope this helps.