6

I'm a beginner struggling with IoC and DI. I'd like to be able to resolve the connection and connection factory dynamically using autofac (or any other suitable .NET IoC tool).

A scenario could be changing the connection implementation to another one with more facilities for tracing etc.

When I apply DI and IoC to the code below, I get a mess of namedParameter in constructors etc. The connection factory returns a new connection with a unique port (silly example, just to show I need to keep some sort of state in the factory)

I figure I could use property injection for the IP and port range, but that way, I wouldn't be guaranteed that the connections would have an IP or port, which is the point of a constructor. Also, the named parameters make me dependent on the names of the arguments as well.

Ideas, patterns, IoC pointers are much appreciated!

Update:

More specific: How could I change the connection class to be injectable? Should I go with property injection? Or any tricks I could do get a more type-safe resolving with constructor arguments?

public interface IConnection {
     void Open();
     void Close();
     string Execute(string command);
}

public interface IConnectionFactory {
     IConnection CreateConnection();
}

public class Connection : IConnection {
   ...
   public Connection(String ip, int port) {
     _ip = ip;
     _port = port;
   }

   public string Execute() {}   
   public void Open() {}
   public void Close() {}
}


public class ConnectionFactory : IConnectionFactory {
    //How would I resolve this?
    public ConnectionFactory(string ip, int fromPort) {
        ...
    }
    public IConnection CreateConnection()  {
        //How would I resolve this? 
        return new Connection(ip, fromPort++);
    }
}

Now, the usage:

//Register
builder.RegisterType<Connection>().As<IConnection>();
builder.RegisterType<ConnectionFactory>().As<IConnectionFactory>().SingleInstance();
...

var connection = container.Resolve<IConnectionFactory>(
      new NamedParameter("ip", "127.0.0.1"), 
      new NamedParameter("fromPort", 80).CreateConnection());
Larsbj
  • 28
  • 4
  • 14
  • Perhaps you might want to clarify your actual question/problem with the code above or whether you're just after documentation links/samples for a specific IoC framework. – Reddog Mar 02 '11 at 00:07
  • Thank you for the comment, I've tried to be more specific! – Larsbj Mar 02 '11 at 00:51
  • I would go ahead with the property injection and first resolve the obect and then pass the properties. – sajoshi Mar 02 '11 at 09:02

3 Answers3

6

An alternative to passing the constructor arguments at resolve-time is to encode those arguments in the registration function:

builder
.Register(c => new ConnectionFactory("127.0.0.1", 80))
.As<IConnectionFactory>()
.SingleInstance();

Autofac will use that function whenever it needs to create the connection factory instance.

Since we configured ConnectionFactory as SingleInstance, it will be shared amongst all components which depend on IConnectionFactory. This means ConnectionFactory needs to keep its own state between calls to CreateConnection:

public class ConnectionFactory : IConnectionFactory
{
    private int _fromPort;

    public ConnectionFactory(string ip, int fromPort)
    {
        ...
        _fromPort = fromPort;
    }

    public IConnection CreateConnection()
    {
        return new Connection(ip, _fromPort++);
    }
}

If you have a one-off ConnectionFactory which, say, uses a different IP, you can use a named registration:

builder
.Register(c => new ConnectionFactory("192.168.0.1", 80))
.Named<IConnectionFactory>("AlernateConnectionFactory")
.SingleInstance();

When you want a component to use that particular factory instead of the default one, you can use the ResolveNamed method:

builder.Register(c => new Foo(c.ResolvedNamed<IConnectionFactory>("AlernateConnectionFactory")));

This is a handy technique to configure a type in multiple ways and use them in specific places.

Bryan Watts
  • 44,911
  • 16
  • 83
  • 88
  • I cannot believe I didn't think about registering an instance instead of the class, the concept is so simple and just what I need. *slaps forehead* Thank you! – Larsbj Mar 02 '11 at 19:51
  • what if the paramaters are not constant? – sebas Jan 02 '13 at 15:15
  • @sebas: I generally organize these kinds of registrations into modules. The modules accept configuration parameters like URLs and ports in their constructors and use them in the registrations. See this answer of mine for the specific approach: http://stackoverflow.com/questions/3587380/dependency-injection-and-appsettings/3592370#3592370 – Bryan Watts Jan 02 '13 at 15:20
2

I am not experienced with Autofac but I solved a very similar problem in Unity. Here's a snippet:

When I configure my container I do something like this (this can even be done in the config file):

string connectionString = ConfigurationManager.ConnectionStrings["XYZ"].ConnectionString;
Container.RegisterType<IConnection, Connection>(new PerThreadLifetimeManager(), 
                                                new InjectionConstructor(connectionString));

Later on, I can build the connections by just saying:

IConnection myConnection = Container.Resolve<IConnection>();

or specifying a dependency property:

[Dependency]
IConnection Connection {get;set;}

or as an injectable parameter for constructor injection:

[InjectionConstructor]
public SomeClassThatUsesConnections(IConnection connection)
{ ... }

I left the PerThreadLifetimeManager to point out that I let the IoC deal with ensuring that no two threads are sharing a connection.

Although the difference with your sample in the question is subtle, it does remove the parameters from each instantiation which allows you to have it as a dependency property or as part of an injection constructor.

Hope that helps.

ale
  • 519
  • 2
  • 11
  • Thank you for your answer, another great idea. I wasn't thinking about that approach at all. – Larsbj Mar 02 '11 at 19:47
1

A flexible approach might be to create an "AppConfigConnectionFactory" (or WebConfig, DBConfig, etc...) and externalize these properties via configuration. I've never felt right loading configuration data straight from the DI framework.

x112358
  • 121
  • 4