【问题标题】:Castle windsor: how to pass arguments to deep dependencies?温莎城堡:如何将参数传递给深度依赖?
【发布时间】:2017-03-20 14:42:11
【问题描述】:

我有以下依赖链:

IUserAppService
IUserDomainService 
IUserRepository
IUserDataContext - UserDataContextImpl(string conn) 

以上所有接口和实现都注册在 Windsor Castle 容器中。当我使用一个连接字符串时,一切正常。

现在我们要支持多个数据库,在UserAppServiceImpl.cs,我们想根据userId得到不同的IUserRepository(不同的IUserDatabaseContext)如下:

// UserAppServiceImpl.cs
public UserInfo GetUserInfo(long userId)
{
   var connStr = userId % 2 == 0 ? "conn1" : "conn2";
   //var repo = container.Resolve<IUserRepository>(....)
}

如何将参数connStr 传递给UserDataContextImpl

【问题讨论】:

  • 在深入挖掘之前:将容器传递给您的服务(或任何其他域类型)被认为是一种不好的做法。

标签: castle-windsor


【解决方案1】:

由于在您的情况下连接字符串是运行时数据,因此不应将其直接注入组件的构造函数中,如 here 所述。然而,由于连接字符串是上下文数据,因此将它与对象图中的所有公共方法一起传递会很尴尬。

相反,您应该将其隐藏在允许您检索当前请求的正确值的抽象后面。例如:

public interface ISqlConnectionFactory
{
    SqlConnection Open();
}

ISqlConnectionFactory 本身的实现可能依赖于允许检索当前用户 ID 的依赖项:

public interface IUserContext
{
    int UserId { get; }
}

因此,此类连接工厂可能如下所示:

public class SqlConnectionFactory : ISqlConnectionFactory
{
    private readonly IUserContext userContext;
    private readonly string con1;
    private readonly string con2;

    public SqlConnectionFactory(IUserContext userContext,
        string con1, string con2) {
        ...
    }

    public SqlConnection Open() {
        var connStr = userContext.UserId % 2 == 0 ? "conn1" : "conn2";            
        var con = new SqlConnection(connStr);
        con.Open();
        return con;
    }
}

这给我们留下了一个IUserContext 实现。这样的实现将取决于我们正在构建的应用程序的类型。对于 ASP.NET,它可能如下所示:

public class AspNetUserContext : IUserContext
{
    public string UserId => int.Parse(HttpContext.Current.Session["UserId"]); 
}

【讨论】:

    【解决方案2】:

    您必须从依赖解析器的开头开始,并将所有派生的依赖解析为“命名”解析。

    Github代码链接:https://github.com/castleproject/Windsor/blob/master/docs/inline-dependencies.md

    例子:

    我有一个用于 MSSQL 的 IDataContext 和另一个用于 MySQL。

    此示例在 Unity 中,但我确信 Windsor 可以做到这一点。

            container.RegisterType(Of IDataContextAsync, dbEntities)("db", New InjectionConstructor())
            container.RegisterType(Of IUnitOfWorkAsync, UnitOfWork)("UnitOfWork", New InjectionConstructor(New ResolvedParameter(Of IDataContextAsync)("db")))
    
            'Exceptions example
            container.RegisterType(Of IRepositoryAsync(Of Exception), Repository(Of Exception))("iExceptionRepository",
                                                                                                         New InjectionConstructor(New ResolvedParameter(Of IDataContextAsync)("db"),
                                                                                                                                  New ResolvedParameter(Of IUnitOfWorkAsync)("UnitOfWork")))
    

    sql容器

    container.RegisterType(Of IDataContextAsync, DataMart)(New HierarchicalLifetimeManager)
            container.RegisterType(Of IUnitOfWorkAsync, UnitOfWork)(New HierarchicalLifetimeManager)
    
            'brands
            container.RegisterType(Of IRepositoryAsync(Of Brand), Repository(Of Brand))
    

    控制器代码: 无需在控制器级别进行任何更改。

    结果: 我现在可以让我的 MSSQL 上下文完成它的工作,让 MySQL 完成它的工作,而无需任何开发人员了解我的容器配置。开发者只需使用正确的服务,一切就都实现了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多