IoC,工厂和构造函数参数

时间:2011-03-01 23:56:55

标签: c# .net inversion-of-control autofac

我是IoC和DI的初学者。我希望能够使用autofac(或任何其他合适的.NET IoC工具)动态解析连接和连接工厂。

一个场景可能是将连接实现更改为另一个具有更多跟踪设施等的实现。

当我将DI和IoC应用于下面的代码时,我在构造函数等中得到了一堆namedParameter。连接工厂返回一个带有唯一端口的新连接(愚蠢的例子,只是为了表明我需要保持某种状态在工厂)

我想我可以为IP和端口范围使用属性注入,但是这样,我不能保证连接将具有IP或端口,这是构造函数的要点。 此外,命名参数也使我依赖于参数的名称。

想法,模式,IoC指针非常感谢!

更新

更具体:如何将连接类更改为可注入?我应该选择房产吗?或者我能做的任何技巧都可以通过构造函数参数获得更加类型安全的解析?

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++);
    }
}

现在,用法:

//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());

3 个答案:

答案 0 :(得分:6)

在resolve-time传递构造函数参数的另一种方法是在注册函数中对这些参数进行编码:

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

只要需要创建连接工厂实例,Autofac就会使用该功能。

由于我们将ConnectionFactory配置为SingleInstance,因此它将在依赖IConnectionFactory的所有组件之间共享。这意味着ConnectionFactory需要在调用CreateConnection之间保持自己的状态:

public class ConnectionFactory : IConnectionFactory
{
    private int _fromPort;

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

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

如果您有一次性ConnectionFactory,例如,使用不同的IP,您可以使用命名注册:

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

当您希望组件使用该特定工厂而不是默认工厂时,您可以使用ResolveNamed method

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

这是一种方便的技术,可以多种方式配置类型并在特定位置使用它们。

答案 1 :(得分:2)

我对Autofac没有经验,但我在Unity中解决了一个非常类似的问题。这是一个片段:

当我配置我的容器时,我会做这样的事情(甚至可以在配置文件中完成):

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

稍后,我可以通过以下方式建立连接:

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

或指定依赖项属性:

[Dependency]
IConnection Connection {get;set;}

或作为构造函数注入的可注入参数:

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

我离开PerThreadLifetimeManager指出我让IoC处理确保没有两个线程共享连接。

尽管问题中样本的差异很小,但它确实会从每个实例化中删除参数,这些参数允许您将其作为依赖项属性或作为注入构造函数的一部分。

希望有所帮助。

答案 2 :(得分:1)

灵活的方法可能是创建“AppConfigConnectionFactory”(或WebConfig,DBConfig等...)并通过配置外部化这些属性。我从来没有感觉到直接从DI框架加载配置数据。