Autofac共享对象需要每个控制器的不同的登记,但InstancePerApiController

2019-10-20 11:47发布

正如详细InstancePerApiControllerType不工作 ,我无法使用InstancePerApiControllerType来配置我的解决方案。 答案只要有工作,只要我直接注入ConnectionContext到控制器,或以其他方式知道,一个类只能使用特定的控制器。 不幸的是,是不是在我的情况的情况下:

ControllerA -> EngineA -> RepositoryA -> GenericEntityAccessor

ControllerB -> EngineB -> RepositoryB -> GenericEntityAccessor

问题是,当我们通过进来ControllerAGenericEntityAccessor需要“字符串A”和ControllerB需要“字符串B”。

当然,真实的情况是有点复杂,它有一些不好的做法,如代码,直接“新闻” -最多一个ConnectionContext (它的遗留代码)。 我目前正在探索提供提供使用懒惰通过Autofac注入和配置控制器的连接字符串的另一个组成部分,但糟糕的做法,也有导致问题(即一旦我开始改变事物的接口,所有的多米诺骨牌开始翻倒,我结束了15个班后来知道我是如何到达那里)。

是否有解决这种类型的事情的任何模式,技术等? 我无法想象这一切都让少见。

更新:

为了提供一些更多的细节,因为我有一些麻烦这个工作,一般我们我申请以下层次结构,显示的作用域

Controller -> InstancePerApiRequest()
I*Repository -> ?
I*Manager -> ?
I*Builder -> ?
I*Adapter -> ?
ISqlServerConnectionContext -> ?
IConnectionContextCache -> InstancePerApiRequest()

我有许多组件是直接拿ISqlServerConntectionContext,我试图提供像这样:

container.Register(c =>
{
    var connectionContextCache = c.Resolve<IConnectionContextCache>();
    var connection = (ISqlServerConnectionContext)connectionContextCache.CurrentConnectionContext;

    return connection;
}).As<ISqlServerConnectionContext>().InstancePerDependency();

不幸的是,在这一点上我得到了CurrectConnectionContext空。 我在这一点上的猜测是我有没有被从控制器植根一些成分,我目前正在经历的依赖手工试图找到它(据我所知的是不是为我找出触发该对象的方式Autofac试图提供ISqlServerConnectionContext当我调试)。

更新2:

事实证明,我确实有一些问题,我在那里注册不当的东西,创造的依赖关系ISqlServerConnectionContextDocumentController ,即使它没有一个(这是通过委托创造的东西,它并依赖于)。

现在我有一个循环引用,我敢肯定,我已经创建了自己的注册:

container.Register(x =>
{
    if (x.IsRegistered<HttpRequestMessage>())
    {
        var httpRequestMethod = x.Resolve<HttpRequestMessage>();

        var tokenHelper = x.Resolve<ITokenHelper>();
        var token = tokenHelper.GetToken(httpRequestMethod);

        return token ?? new NullMinimalSecurityToken();
    }

    return new NullMinimalSecurityToken();
}).As<IMinimalSecurityToken>().InstancePerApiRequest();

container.Register(c =>
{
    var connectionContextCache = c.Resolve<IConnectionContextCache>();
    var token = c.Resolve<IMinimalSecurityToken>();
    var connection = (ISqlServerConnectionContext)connectionContextCache.CurrentConnectionContext;

    connection.Token = token;

    return connection;
}).As<ISqlServerConnectionContext>().InstancePerApiRequest();

问题是ISqlServerConnectionContext有一个类型的属性IMinimalSecurityToken是可选的,而当绝对不使用ISqlServerConnectionContext被用于查找IMinimalSecurityToken ,这取决于ISqlServerConnectionContext通过ITokenHelper

更新3:

为了完整起见,为了解决我的循环引用的问题,我需要使用命名服务,并使用SqlServerConnectionContext这并没有IMinimalSecurityToken属性集IOAuthTokenManager登记。 现在我越来越可怕

不带标签的匹配范围“AutofacWebRequest”可见

错误,但我认为值得一个新的问题,如果我不能够解决这个问题。

container.Register(c =>
{
    var productId = WellKnownIdentifierFactory.Instance.GetWellKnownProductIdentifier(WellKnownProductIdentifiers.RESTSearchService);
    var connectionString = ConfigurationManager.AppSettings[AppSettingsNames.DatabaseConnection];

    var newConnectionContext = new SqlServerConnectionContext(connectionString) { ProductID = productId };
    newConnectionContext.Open();

    return newConnectionContext;
}).Named<ISqlServerConnectionContext>("OAuthTokenConnectionContext").InstancePerApiRequest();
container.Register(c => new SqlServerBuilderFactory(c.ResolveNamed<ISqlServerConnectionContext>("OAuthTokenConnectionContext"))).Named<IBuilderFactory>("OAuthTokenBuilderFactory").InstancePerApiRequest();
container.Register(c =>new OAuthTokenManager(c.ResolveNamed<IBuilderFactory>("OAuthTokenBuilderFactory"))).As<IOAuthTokenManager>().InstancePerApiRequest();

Answer 1:

这可以使用对象图一辈子作用域AutoFac的支持来解决。

  1. 缓存当前SqlServerConnectionContext对象中的作用域为您的控制器的寿命。
  2. 内的SqlServerConnectionContext工厂类型,一旦连接被创建将其分配给当前的寿命范围的高速缓存的支持字段
  3. 然后在控制器的寿命范围之内的任何范围的类型可以访问通过缓存与该控制器相关联的连接

我能想到的唯一的复杂性是:

  1. 如果控制器实际上不是所有类型的与特定连接上的依赖性的寿命范围的根。 也就是说,如果它们不属于控制器的使用寿命之外。
  2. 如果有任何依赖关系登记为单个实例。 在这种情况下,他们将不能够解决高速缓存,因为它是目前实现的,因为它是PerApiRequest。

例如:

public interface ISqlServerConnectionContextCache
{
    ISqlServerConnectionContext CurrentContext { get; set; }
}

public class SqlServerConnectionContextScopeCache : ISqlServerConnectionContextCache
{
    public ISqlServerConnectionContext CurrentContext { get; set; }
}

public interface ISqlServerConnectionContextFactory
{
  ISqlServerConnectionContext Create();
}

// The factory has the cache as a dependancy
// This will be the first use of the cache and hence
// AutoFac will create a new one at the scope of the controller
public class SqlServerConnectionContextFactory : ISqlServerConnectionContextFactory
{
  private string _connectionString;
  private ISqlServerConnectionContextCache _connectionCache;

  public SqlServerConnectionContextFactory(ISqlServerConnectionContextCache connectionCache,
    string connectionString)
  {
    _connectionCache = connectionCache;
    _connectionString = connectionString;  
  }

  public ISqlServerConnectionContext Create()
  {
    var connectionContext = new SqlServerConnectionContext(_connectionString);
    connectionContext.Open();
    _sqlServerConnectionContextProvider.CurrentContext = connectionContext;
    return connectionContext;
  }
}

public class MyController : ApiController
{
  private ISqlServerConnectionContext _sqlServerConnectionContext;

  public MyController(Func<string, ISqlServerConnectionContextFactory> connectionFactory)
  {
    _sqlServerConnectionContext = connectionFactory("MyConnectionString");
  }
}

// As the cache is lifetime scoped it will receive the single instance
// of the cache associated with the current lifetime scope
// Assuming we are within the scope of the controller this will receive
// the cache that was initiated by the factory
public class MyTypeScopedByController
{
    public MyTypeScopedByController(ISqlServerConnectionContextCache connectionCache)
    {
        var sqlServerConnectionContext = connectionCache.CurrentContext;
    }
}

// AutoFac wiring
builder.RegisterType<SqlServerConnectionContextScopeCache>()
    .As<ISqlServerConnectionContextCache>()
    .InstancePerApiRequest();
builder.RegisterType<SqlServerConnectionContextFactory>()
    .As<ISqlServerConnectionContextFactory>()
    .InstancePerDependency();


文章来源: Autofac shared objects require different registrations per controller but InstancePerApiControllerType won't work