Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#

LinFu.IOC 2.0 in Five Minutes (Part 1 of n): Fun With Attributes

Rate me:
Please Sign up or sign in to vote.
4.98/5 (36 votes)
9 Dec 2008LGPL324 min read 95K   454   74   33
The first article in a series of articles that describes how you can use the LinFu.IOC 2.0 container to extend your application(s).

LinFu.png

Note: To access the latest sources for the LinFu v2.0 Framework directly from the SVN, click here.

Introduction

In this first part of the series, I'll show you how you can use attributes with LinFu.IoC to add your custom services to the container, and how you can use LinFu.IOC to quickly add dependency injection capabilities to your application in the fewest lines of code possible. I'll also show you how you can use LinFu's IOC container to instantiate and customize nearly any .NET type, including primitive types and types that your application does not own.

Overview

A Change in Style?

In contrast to my previous articles on LinFu, the goal of each one of the articles in this series is to focus more on the practical use of LinFu and dependency injection rather than go deep into the theory behind Inversion of Control, and dependency injection itself. I won't bore you with theoretical explanations--instead, we'll dive straight into the code, and I'll keep these articles as short and informative as possible.

Why Use LinFu?

Developers New to Inversion of Control

One of the barriers to understanding how Inversion of Control works is that a lot of Inversion of Control frameworks are so massive (or sparsely documented, or both) that it's hard for someone new to step through the said framework code and figure out how it all works. That's not the case with LinFu. Every class, method, and property in LinFu.IOC v2.0 (regardless of whether or not it is public, private, or internal) is heavily-documented, and the code is simple enough that you don't have to worry about burying yourself in someone else's terminology. Furthermore, LinFu.IOC has an almost flat class hierarchy, meaning that you won't have to sift through layers of inheritance to see exactly what is happening in the code, and if that isn't enough, the Unit Test cases will show you exactly how to use each feature, so you can be sure that everything will work as advertised.

Existing IOC Container Users

If you're already using another IOC container such as Castle, Ninject, StructureMap, or AutoFac (and even Spring.NET), you might be wondering how LinFu.IOC compares to the other containers. You can think of LinFu as the "Swiss Army knife" of IOC containers. It is not only an IOC container (per se), it is also a general implementation of the Factory Pattern on steroids, and in this series, I'll show you how truly useful LinFu.IOC can be. LinFu's IOC container can create and configure nearly any type of object, and for those who are already satisfied with the current IOC framework they're using, that feature should at least pique your interest, if only for the sake of your own curiosity.

Note: To see how LinFu stacks up compared to the other .NET Dependency Injection/Inversion of Control frameworks, click here.

The Quick and Dirty Overview

In a nutshell, LinFu's IOC container is simple enough that you can learn it in five minutes, and it's also flexible enough that it supports various coding styles without forcing you to follow a specific style of development. It supports the following features:

  • Additional Service Arguments. Although seemingly trivial, this feature allows you to pass additional arguments to LinFu's container when you instantiate any given service. Many DI/IOC containers pass these arguments directly to the implementing type's constructor; LinFu, in contrast, allows you to take those arguments and add custom commands to the container as if those commands were part of LinFu's container in the first place. For example, you can use this to do things like turn LinFu.IOC into a controller in an MVC architecture, or you can use these same features and turn it into a simple calculator--yes, it's that flexible.
  • Available Service Enumeration. LinFu is the only IOC container (so far) that can actually give you a list of services that currently reside in the container. You can actually query a container for the services that it supports, and you can even instantiate any one (or all) of them, if necessary.
  • Automatic Field, Method, and Property Injection. Similar to its Ninja-like cousin, LinFu supports injecting service dependencies into fields, properties, and methods that are marked with a particular custom attribute. If LinFu's built-in InjectAttribute class doesn't suit your needs, you can always substitute it with your own custom attribute to keep LinFu separate from your domain model.
  • Contextual Binding. Like other containers, LinFu allows you to dynamically determine which service implementation should be provided for a given service type. LinFu allows you to bind a particular service instance to any property, method, field, or constructor. However, unlike other containers, LinFu supports a special form of contextual binding that allows you to decide which concrete service type should be created depending on the additional parameters passed to LinFu.IOC during a service request. It's an incredibly useful feature, and this is one you really have to see to believe.
  • Constructor Injection. LinFu can automatically inspect a given type and choose the constructor with the most resolvable parameter types. This means that if you have a constructor and if one or more of its parameters is a service type that resides inside the container at the time you request that service type, LinFu will be able to fill in all the constructor arguments and use that particular constructor to instantiate your service type. In addition, LinFu supports covariant constructor arguments, meaning that it's smart enough to determine which services should be injected into the constructor arguments even if those arguments don't exactly match the services currently available in the container. It can also distinguish between a nearly infinite number of constructors declared on a target type, so you'll almost never have to worry about running into constructor ambiguity issues when you create your services.
  • Declarative/Attribute-Based Registration. A single attribute declaration is all LinFu needs to inject your services into the container. With LinFu.IOC's attribute support, you don't have to worry about maintaining a large XML file or updating your binding code to keep your service dependencies up to date. All you have to do is drop your updated binaries into a target directory, and LinFu will make the application practically update itself instantaneously.
  • Extensible Instantiation Pipeline. If you love extending frameworks like I do, then you're going to love all the extension points LinFu.IOC has to offer for creating service instances. LinFu lets you control everything from determining the factory object that will instantiate your services to adding custom steps to the pipeline so you can modify, intercept, and even replace the service instances that come out of the container. The best part about all this is that like the attributed service types, all you need to do to extend the container is "drop in" the extension DLL into the target container directory and the container will automatically add your extension to the container itself. It can't get any more simple than that.
  • Fluent Registration. If you're a fan of using explicit interfaces to describe which dependencies the container should use, then LinFu.IOC might just be what you need. LinFu allows you to use fluent interfaces so that you can keep your container binding code all in one place without sacrificing the readability in your code.
  • Factory Lambda Support. Like AutoFac, LinFu takes advantage of C# 3.0's lambda syntax to make it easier for you to register your service instances without having to create the actual service instances themselves. Once you plug in your own lambda factory methods into the container, the container will behave as if your factory method was always part of the container in the first place, and in this series, you'll see just how useful these factory lambdas can be.
  • IEnumerable Service Injection for Fields, Methods, Properties, and Constructors. LinFu also has the ability to automatically inject a list of currently available services of a specific type into a type's constructors, methods, properties, and fields. You can even instantiate all of them at once with a single lambda expression.
  • Interceptors. LinFu.IOC supports service interception right out of the box, and LinFu.Proxy (a.k.a. LinFu.DynamicProxy v2.0) has been fully integrated into the container so that you can define your own "drop-in" service interceptors using a single attribute declaration. Needless to say, this feature makes it easy to transparently extend your service instances with other features such as transaction management and logging, all without having to modify your concrete service implementations.
  • Open Generics Injection and Registration. LinFu allows you to reduce the number of services that reside in a container by letting you register generic type definitions to handle a whole family of generic types. For example, you can tell the container to instantiate a List<T> type every time a user tries to instantiate an IEnumerable<T> service instance, and LinFu.IOC will automatically provide that list for you no matter what type T might be.
  • Scoped Objects. LinFu can automatically keep track and dispose of any IDisposable instance that it creates within a given using block or scope.
  • Support for Instantiating POCO objects, Primitive Types, and Third-Party Objects. LinFu can create almost any type, regardless if that type is a primitive type (such as System.Int32), a string, or a reference type that you do not control (such as a .NET BCL class or an NHibernate ISession object). You can literally customize the construction of any type to your heart's content, and in this article, I'll show you how you can use these features to turn LinFu.IOC into a configuration string reader.
  • Unregistered Resolution. LinFu can also perform dependency injection on types that don't reside in the container, and that means that you can instantiate types with the same automatic injection semantics that are typically reserved for types that are already registered in the container itself. In layman's terms, this means that if you have a type that can be injected, LinFu.IOC has the power to inject it, regardless of where that object resides.
  • XCOPY Deployment. One of LinFu.IOC's most useful features is that you can change its configuration simply by "dropping in" any additional DLLs into a target directory of your choice and telling LinFu.IOC to rescan that directory. A few lines of code (plus an XCOPY command) is all you need to extend the container with your own service implementations, interceptors, factories, and container plug-ins, and you'll be able to do this without recompiling your application.

As you can see, we have a lot of ground to cover, but you don't need to know how to use all these features in order to use LinFu. Each article in the series is going to cover at least one of these features that I mentioned above, and in this particular article, I'll show you how you can use attributes with LinFu to get your application up and running in no time. LinFu.IOC is packed with a whole slew of goodies, and this holiday season, it's time to give back to all the other developers out there on CodeProject that have made this site the great place that it is--and with that in mind, let's get started!

Background

As promised, this article won't talk much about theory, but I certainly won't leave my readers in the dark without some resources to help you get up to speed on the theory behind Inversion of Control and dependency injection. For those of you who are new to Inversion of Control, here are a few resources to get you started:

  • Martin Fowler on Dependency Injection. This page teaches the basics of Inversion of Control containers, service locators, and using interfaces to make your application easier to maintain. It even shows you how to perform manual constructor injection, and knowing it is going to come in handy once you start dealing with LinFu.IOC's automatic constructor injection features.
  • My Article on Inversion of Control and Dependency Injection. The article talks about LinFu.IOC's predecessor, Simple.IOC, and explains some of the basic elements of an Inversion of Control container. Once you get familiar with how the attributes are being used in that article, then you'll have no problem understanding the concepts that I'll be talking about when it comes to LinFu.IOC.
  • Dependency Injection Books on Amazon. If you have deep pockets and if you want to further dive headlong into understanding Inversion of Control, then this might be the place for you. If your pockets aren't so deep (like mine), then the used books section has the same books for half the price, and this is the place to be if you're on a tight budget.

Using the Code

In general, there are only a few things that you need to know to use the basic functions of LinFu.IOC. They are:

All Roads Lead to the ServiceContainer Class

The ServiceContainer class is the heart of LinFu.IOC, and it's the only class that you have to create to get started:

C#
// This is where it all begins and ends
var container = new ServiceContainer();

// Tell the container to configure itself 
container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); 

Aside from the LoadFrom method call, the code above is unequivocally self-explanatory. The LoadFrom method tells the container to configure itself using the contents of all the DLL assemblies in the application base directory. Believe it or not, this is the only method call that you need to make to get the container to load all your services. Now that we're done going over the basics of loading LinFu's container, I'll show you how you can use it to create types in the next section.

Instantiating Your Types

Getting Some Service

The ServiceContainer.GetService() method is the only method you'll ever need to use to create types with LinFu.IOC. There are several overloads for this particular method, but for now, you only need to concern yourself with the following overloads for the GetService method:

Strong and Weakly-Typed GetService Methods

For your convenience, LinFu.IOC provides untyped and strongly-typed overloads for the GetService method. If I wanted to instantiate a service type named ISomeService (or even a primitive type) for example, the following overloaded method calls perform exactly the same function:

C#
 // Strongly-typed GetService method call
var myService = container.GetService<ISomeService>();
 
// Weakly-typed GetService method call
myService = (ISomeService)container.GetService(typeof(ISomeService));

Intellisense, of course, will intelligently sense that there are far more generic and non-generic overloads of the GetService method available for you to use than the two methods calls that I showed you in the above examples. However, if you're only going to use LinFu.IOC to create simple objects or services, those two methods will be the only versions of GetService that you will ever need.

Named Services

Unfortunately, a developer's life is rarely ever simple, and there's a good chance that you'll run into different scenarios where you need to create two different types depending on the name of the service that you request from the container. For example, let's suppose that you wanted the container to give you the values of two separate configuration strings. Here's how you do it:

C#
// Get the connection string for an Oracle connection
var serviceName = "OracleConnectionString";
var connectionString = container.GetService<string>(serviceName);

// Get the connection string for SQL Server 2005
serviceName = "Sql2k5ConnectionString";
connectionString = container.GetService<string>(serviceName);

As you can see from the example above, I actually used the serviceName variable to distinguish between two service instances of the same type (in this case, I used a string). Again, it's very straightforward, and the important thing to remember here is that you can use service names with LinFu.IOC to specify different configurations. The example above might seem trivial, given that I used a string type, but another important thing to remember is that you can do the same thing to any other .NET type, regardless of whether or not it is a value or a reference type. Most major .NET DI/IOC frameworks support named services in one fashion or the other, and LinFu is no exception.

Additional Service Arguments

There might be times where you might want to pass a certain set of arguments to the container so that the container can take those same arguments and pass it along to the constructor of the concrete service type. For example, let's suppose that I have a service named IGreeter with a concrete Greeter implementation:

C#
public class Greeter : IGreeter
{
    private readonly string _name;
    public Greeter(string name)
    {
        _name = name;
    }
    public void Greet()
    {
        Console.WriteLine("Hello, {0}!", _name);
    }
}

Since the Greeter class itself doesn't have a default constructor, there doesn't seem to be an immediate way to instantiate the IGreeter service type. This is where LinFu's support for additional parameters comes in handy:

C#
// This is equivalent to:
// IGreeter greeter = new Greeter(myName);
// NOTE: I'm using a null parameter for the service name parameter
// since I don't need a particular named service instance
var myName = "Me";
var greeter = container.GetService<IGreeter>(null, myName);

// Say "Hello, Me!"
greeter.Greet();

In the example above, LinFu.IOC uses the additional myName argument to fill in the missing parameter in the constructor arguments. Whenever you try to instantiate a service type with missing constructor arguments, LinFu will use the additional arguments given to the GetService method call and match those arguments to the constructor with the most compatible method signature. What makes this even more interesting is the fact that LinFu can distinguish between any number of constructor signatures regardless of the number of parameters or the parameter types that might be embedded in a particular constructor signature. In fact, LinFu's constructor resolution accuracy actually goes up with every additional parameter you pass to the GetService call. In other words, you'll never have to worry about getting an incompatible constructor call with LinFu. It just works.

Registering Your Types

Manual Registration

LinFu.IOC wouldn't be useful if it wasn't at least symmetrical, and for every GetService method overload, there is an equal and opposite AddService method that can register your types with the container itself. Here's how you can use the AddService method to register the unnamed ISomeService type from the previous example:

C#
var someServiceInstance = new SomeService();

// Strongly-typed registration
container.AddService<ISomeService>(someServiceInstance);

// Weakly-typed registration
container.AddService(typeof(ISomeService), someService);

If you need to use named services, here's how you can register those connection strings with the container:

C#
// Register the Oracle connection string
var connectionString = "Driver={Oracle in OraHome92};
    Server=myServerAddress;Dbq=myDataBase;Uid=myUsername;Pwd=myPassword;";
container.AddService("OracleConnectionString", connectionString);

// Register the SQL Server 2005 connection string
connectionString = "Data Source=myServerAddress;
    Initial Catalog=myDataBase;User Id=myUsername;Password=myPassword;";
container.AddService("SQL2k5ConnectionString", connectionString);

There are quite a few more overloaded versions of the AddService method, of course, but the ones that I used in the examples above should be all you need to register your types manually. However, since the purpose of this article is to show you how to do automatic registration with LinFu, I'll show you how to forgo using the AddService method altogether in the next section.

Having Your MEF, and Eating it Too

There are other quasi-Inversion of Control frameworks that allow you to essentially "export" your types from a given assembly, and their corresponding container implementations will, in turn, take all these "exported" dependencies and bind them all together at runtime. These dependencies (a.k.a. service types and their concrete implementations) are all defined using custom attributes which tell the container how to bind the concrete service types with the type being "exported" (that is, the service type that is being implemented by the concrete type). In other words, they use attributes to bind your code together, and I'll show you how LinFu can do the same thing without forcing you to wade through any new jargon or terminology. With that in mind, here's how you can use attributes with LinFu:

What They 'Export', I Shall Implement

LinFu.IOC can register each one of your types into the container using a single [Implements] attribute declaration. For example, here's how you can use the [Implements] attribute declaration to register the Greeter class to implement the IGreeter service type:

C#
[Implements(typeof(IGreeter))]
public class Greeter : IGreeter
{
    private readonly string _name;
    public Greeter(string name)
    {
        _name = name;
    }
    public void Greet()
    {
        Console.WriteLine("Hello, {0}!", _name);
    }
}
Loading Your Services into the Container

Assuming that your Greeter class is located in an assembly named Greeter.dll and that assembly is located in the application directory, this is all you need to do to add the IGreeter dependency to the container:

C#
// Load the greeter library
container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "Greeter.dll");

The code pretty much speaks for itself. At runtime, LinFu.IOC will scan the target directory for your dependencies and automatically add them to the container. It's really that simple.

Registering Primitive and Third-Party Types

Although the AddService method allows you to register any .NET type instance (including primitive types), there might be times where you need to have more control about how a type is constructed. For example, you might need to tell LinFu.IOC to pull specific strings out of a config file or the system Registry; or, there might even be times when you want LinFu to instantiate (and thus customize) a specific type of object (such as an NHibernate ISession object) but you don't have the luxury of defining the [Implements] attribute on that type since you don't own the source code for that library. In such cases, we're going to need something more robust, and that's where LinFu.IOC's custom factories fit into the picture.

Custom Factories

LinFu.IOC allows you to create your services by providing your own implementation of the IFactory interface. Here's what it looks like:

C#
public interface IFactory
{
    object CreateInstance(IFactoryRequest request);
}

The IFactoryRequest interface, in turn, is defined as:

C#
public interface IFactoryRequest
{
    IServiceContainer Container { get; set; }
    string ServiceName { get; }
    Type ServiceType { get; }
    object[] Arguments { get; }
}

LinFu.IOC uses custom IFactory instances internally to instantiate every service type available in the container. The IFactoryRequest interface describes the context of the service request, and all you have to do to create primitive types (or third party types) is to add your own custom IFactory implementation to the container, parse the request itself, and return your new custom service instance in the CreateInstance method. For example, let's suppose that I wanted to make a ConfigurationStringFactory that pulls strings out of an app.config file.

The IFactory implementation would be:

C#
// Tell LinFu.IOC that the factory can create string types
[Factory(typeof(string))]
public class ConfigurationStringFactory : IFactory
{
    public object CreateInstance(IFactoryRequest request)
    {
        // Use the name of the service as the
        // key name in the config file
        var keyName = request.ServiceName;
        
        // Grab the key value
        return ConfigurationManager.AppSettings[keyName];
    }
}

Here's the client code that uses GetService to get a particular configuration string out of the config file:

C#
// Get the "key1" configuration string.
// NOTE: Notice that the client doesn't even know where the config string comes from
var keyName = "key1";
var keyValue = container.GetService<string>(keyName);

The [Factory] attribute declaration in the examples above will tell LinFu.IOC that the ConfigurationStringFactory is capable of instantiating string types. Once that factory has been automatically loaded into the container and the GetService method has been called, the container, in turn, will call ConfigurationStringFactory.CreateInstance and return the custom configuration string. The ConfigurationStringFactory is wholly responsible for how the configuration strings are created, and by extension, the same principle applies to any .NET type you create with your own custom IFactory instances. LinFu.IOC's custom factories allow you to decide which services to create as well as decide how a service type should be created. Creating value types isn't an issue because LinFu makes no distinction between creating value types and reference types. No matter which type you decide to use, the process for creating any type using LinFu.IOC is one and the same, and that's one of the reasons that makes LinFu so flexible.

Additional Parameters Galore

While the ServiceName and ServiceType properties in the IFactoryRequest interface are self-explanatory, the Container and Arguments properties need a bit more explanation. The Container property holds a reference to the actual container that made the service request, and the Arguments property holds the additional arguments given to the GetService method once the service request was made. In other words, the Container property allows custom factories to use the services provided by their host container, as well as read the additional parameters that were passed in during the GetService request. This allows you to do some pretty interesting things that would be difficult to do with other containers. For example, I can turn LinFu.IOC's container into a "quasi-calculator" of sorts by providing a custom factory named AdditionFactory which takes two integers and adds them together:

C#
[Factory(typeof(int), ServiceName="Add")]
public class AdditionFactory : IFactory
{
    public object CreateInstance(IFactoryRequest request)
    {
        // For the sake of brevity, argument type/count checking has been omitted
        int a = (int)request.Arguments[0];
        int b = (int)request.Arguments[1];
        
        return a + b;
    }
}

The client code would look something like this:

C#
// Add the two numbers together
int result = container.GetService<int>("Add", 1, 1);

Needless to say, the amount of flexibility that LinFu's additional argument support provides can be quite staggering--but this gets even better. Since the IFactoryRequest.Container property is exposed to each and every IFactory implementation, you'll be able to access the host container along with all the other custom factories that have been loaded into that same container at runtime. The services provided by other factories inside the same container will already be available to you without having to know how those services are being created. Every IFactory instance is isolated from all the other factories in the container. What makes this interesting, however, is the fact that each one of these factories can access each other's services through the Container property as if those factories were never isolated in the first place. In layman's terms, this means that you can add multiple factories to LinFu.IOC, and every one of those factories will act as one--and that brings us to the last section: factory layering.

Factory Layering, Ad Infinitum

Having all IFactory instances isolated from each other and yet still accessible through the IFactoryRequest.Container property has some interesting possibilities. You can group related factories together into logically-related assemblies, and you can think of each one of these assemblies as a "layer" of factories in your application. For example, let's suppose that I created a very simple "connection" layer that relies on the connection strings provided by the ConfigurationStringFactory class. Here's what the SqlConnectionFactory class would look like:

C#
// Generate Sql2k5Connections by default
[Factory(typeof(IDbConnection))]
public class SqlConnectionFactory : IFactory
{
    public object CreateInstance(IFactoryRequest request)
    {
        var container = request.Container;
        
        // Use the connection string provided by the ConfigurationStringFactory
        var connectionString = container.GetService<string>("Sql2k5ConnectionString");
        var connection = new SqlConnection(connectionString);
        
        // NOTE: You can probably cache the connection here, but for the
        // sake of simplicity, we'll just return the connection
        
        return connection;
    }
}

As you can see in the example above, there was no actual reference to the ConfigurationStringFactory itself, and yet, the ConnectionFactory was still able to access the services provided by the ConfigurationStringFactory class. The ConnectionFactory is only aware of the container's existence, and yet, the IFactoryRequest.Container property allows every other factory to access all the other factories as if they have all been aggregated into the container at once. In other words, you can "drop in" (or replace) these assemblies at runtime without having to worry about making any breaking changes in your app. For example, here's the code to load the two assemblies named ConfigurationStringFactory.dll, and ConnectionFactory.dll:

C#
// Load all the factories into the container
container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*Factory.dll"); 

Aside from the "*Factory.dll" wildcard pattern that tells LinFu to load the factory assemblies into the container, the example above should be immediately familiar--it's the same example that I used to load the container in the beginning of this article. Using that single line of code, you can effectively use the attributes I mentioned in this article to bend LinFu to your will. LinFu.IOC has always been about giving you the flexibility to extend your applications without forcing you to follow any specific paradigm or coding convention (aside from DI/IOC, perhaps), and hopefully, this article will get you started in the right direction. Enjoy!

Points of Interest

Generics Support for the Generically Inclined

One thing that I mentioned in the beginning of this article is LinFu.IOC's support for instantiating open generic service types. As it turns out, you can actually instantiate a whole family of types that derive from (or implement) a generic type definition using LinFu. Take a look at this example:

C#
[Implements(typeof(IList<>))]
public class MyCustomList<T> : List<T>
{
    // ...
}

The ImplementsAttribute declaration will tell LinFu.IOC to route all requests for any IList<T> instance back to a MyCustomList<T> instance, regardless of the type parameter being used to instantiate the list type. In contrast, if you want to handle only a specific type of IList<T> type, you could declare the same class as follows:

C#
// Implement IList<> for string and integer types
[Implements(typeof(IList<string>))]
[Implements(typeof(IList<int>))]
public class MyCustomList<T> : List<T>
{
    // ...
}

Either way, the choice of how you want to design your application is up to you, and the power of choice is ultimately what LinFu.IOC offers.

Coming Up in the Next Article

In Part II of this series, I'll show you how can use LinFu.IOC's built-in dependency injection features to automatically inject your dependencies into constructors, methods, properties, and even fields. I'll even show you how you can combine constructor injection with LinFu.IOC's additional parameters so that you can actually "fill in" the missing constructor parameters when instantiating a concrete service type. Here's an example:

C#
public interface IWeapon {}
public interface IWarrior
{
    void Attack(string target);
    string Name { get; set; }
}

[Implements(typeof(IWarrior), ServiceName="Samurai")]
public class SamuraiWarrior : IWarrior
{
    private IWeapon _weapon;
    public SamuraiWarrior(IWeapon weapon, string warriorName)
    {
        _weapon = Weapon;
        Name = warriorName;
    }
    public void Attack(string target)
    {
        Console.WriteLine("{0}: Attacking '{1}' with weapon '{2}", 
                Name, target, _weapon.GetType().Name);
    }
    
    public string Name { get; set; }
}

// Inject the SamuraiSword by default
[Implements(typeof(IWeapon))]
public class SamuraiSword : IWeapon
{
}

As you can see, most of the code above is self-explanatory. What we need to do is somehow instantiate the Samurai class every time a IWarrior service named "Samurai" is requested from the container, and the container has to automatically inject the IWeapon instance into the constructor so that it can instantiate the Samurai type. In addition to the dependency injection, we also need to give the warrior a name. Here's the client code that does it:

C#
// Load the container
var container = new ServiceContainer();
container.LoadFrom(AppDomain.CurrentDomain.BaseDirectory, "*.dll");

// Create the Samurai
var name = "Musashi";
var warrior = container.GetService<IWarrior>("Samurai", name);

// NOTE: The output will be: "Musashi: Attacking 'Ninja' with SamuraiSword"
warrior.Attack("Ninja");

What makes this example particularly interesting is that LinFu.IOC was smart enough to automatically inject both the IWeapon dependency and the warrior name value into the Samurai class constructor without any form of intervention on your part. This makes it easy to do automatic dependency injection (as well as mixed constructor injection) in your client code because LinFu handles all the low-level details of deciding which constructors to use as well as which parameter values should be used in invoking your constructors. LinFu.IOC also supports automatic method, property, and field injection, but I'll save that example for the next article--so stay tuned, because this will definitely be worth the wait!

Special Thanks

A special thanks goes out to my colleague Bernhard Richter who really helped me put LinFu.IOC v2.0 through its paces. LinFu never would have been as robust as it is today without his support!

History

  • 12.11.2008 - Article posted.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior) Readify
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 3 Pin
Pat Kujawa25-Oct-10 5:59
Pat Kujawa25-Oct-10 5:59 
GeneralGetService on a concrete class type Pin
Philippe Bouteleux25-Feb-10 4:54
Philippe Bouteleux25-Feb-10 4:54 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux25-Feb-10 7:56
Philippe Bouteleux25-Feb-10 7:56 
AnswerRe: GetService on a concrete class type Pin
Philip Laureano25-Feb-10 10:32
Philip Laureano25-Feb-10 10:32 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux25-Feb-10 22:58
Philippe Bouteleux25-Feb-10 22:58 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux2-Mar-10 0:08
Philippe Bouteleux2-Mar-10 0:08 
GeneralRe: GetService on a concrete class type Pin
Philip Laureano2-Mar-10 0:27
Philip Laureano2-Mar-10 0:27 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux2-Mar-10 1:46
Philippe Bouteleux2-Mar-10 1:46 
GeneralRe: GetService on a concrete class type Pin
Philip Laureano2-Mar-10 3:06
Philip Laureano2-Mar-10 3:06 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux2-Mar-10 3:58
Philippe Bouteleux2-Mar-10 3:58 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux3-Mar-10 3:44
Philippe Bouteleux3-Mar-10 3:44 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux3-Mar-10 6:11
Philippe Bouteleux3-Mar-10 6:11 
GeneralRe: GetService on a concrete class type Pin
Philip Laureano3-Mar-10 9:29
Philip Laureano3-Mar-10 9:29 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux3-Mar-10 23:08
Philippe Bouteleux3-Mar-10 23:08 
GeneralRe: GetService on a concrete class type Pin
Philip Laureano4-Mar-10 7:43
Philip Laureano4-Mar-10 7:43 
GeneralRe: GetService on a concrete class type Pin
Philippe Bouteleux4-Mar-10 0:07
Philippe Bouteleux4-Mar-10 0:07 
GeneralRe: GetService on a concrete class type [modified] Pin
Philippe Bouteleux3-Mar-10 23:43
Philippe Bouteleux3-Mar-10 23:43 
GeneralAwful performance. [modified] Pin
Alex Simkin12-Mar-09 10:31
Alex Simkin12-Mar-09 10:31 
GeneralRe: Awful performance. Pin
Philip Laureano14-Mar-09 13:51
Philip Laureano14-Mar-09 13:51 
GeneralRe: Awful performance. Pin
Alex Simkin16-Mar-09 8:14
Alex Simkin16-Mar-09 8:14 
GeneralRe: Awful performance. Pin
AlexeyYakovlev27-Mar-09 3:47
professionalAlexeyYakovlev27-Mar-09 3:47 
NewsCommon Service Locator Adapter for LinFu Now Available Pin
Philip Laureano28-Feb-09 22:50
Philip Laureano28-Feb-09 22:50 
GeneralGreat article and ... Pin
cazzolo281-Feb-09 2:32
cazzolo281-Feb-09 2:32 
NewsLinFu is still LGPL, Not GPL Pin
Philip Laureano26-Jan-09 15:46
Philip Laureano26-Jan-09 15:46 
GeneralVersion Pin
FabioMaulo22-Dec-08 9:45
FabioMaulo22-Dec-08 9:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.