IoC、工厂和构造函数参数

发布于 2024-10-19 14:11:02 字数 1544 浏览 3 评论 0原文

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

一种场景可能是将连接实现更改为另一种具有更多跟踪功能的连接实现。

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

我想我可以对 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());

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

清音悠歌 2024-10-26 14:11:02

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

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 方法

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

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

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.

椒妓 2024-10-26 14:11:02

我对 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 处理确保没有两个线程共享连接。

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

希望有帮助。

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.

优雅的叶子 2024-10-26 14:11:02

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文