【问题标题】:Dependency Injection Unity - Conditional Resolving依赖注入 Unity - 条件解析
【发布时间】:2015-08-30 11:45:41
【问题描述】:

条件解析是我目前不明白的最后一件事。

假设我们有一个接口IAuthenticate

public interface IAuthenticate{
    bool Login(string user, string pass);
}

现在我有两种类型的身份验证。

推特认证

public class TwitterAuth : IAuthenticate
{
  bool Login(string user, string pass)
{
   //connect to twitter api
}

}

Facebook 身份验证

public class FacebookAuth: IAuthenticate
{
  bool Login(string user, string pass)
{
   //connect to fb api
}

}

在统一配置中注册类型:

unityContainer.RegisterType<IAuthenticate, TwitterAuth>();
unityContainer.RegisterType<IAuthenticate, FacebookAuth>();

在我们的控制器中通过 DI 注入对象:

private readonly IAuthenticate _authenticate;

public AuthenticateController(IAuthenticate authenticate)
{
    _authenticate = authenticate;
}



// login with twitter
public virtual ActionResult Twitter(string user, string pass)
{
    bool success =
            _authenticate.Login(user, pass);
}



// login with fb
public virtual ActionResult Facebook(string user, string pass)
{
    bool success =
            _authenticate.Login(user, pass);
}



// login with google
public virtual ActionResult Google(string user, string pass)
{
    bool success =
            _authenticate.Login(user, pass);
}

unity 究竟如何知道它必须为不同类型的身份验证解析哪个对象?在这种情况下如何进行条件解析?

我和我的朋友谈过,如果出现这种情况,他解释说是设计错误,但这只是使用的工厂模式。

【问题讨论】:

  • 你的朋友可能是对的。如果您没有违反Liskov Substitution Principle,请仔细查看。如果IAuthenticate 的实现不能相互互换(这意味着像AuthenticateController 这样的消费者如果得到错误的实现将无法正常工作),那么您就违反了LSP。这种情况下的解决方案通常是为每个实现提供自己的抽象。
  • @Steven 我认为 LSP 是关于使用 'child' 而不是 'parent',而不是使用 'child1' 而不是 'child2' (或者“每个可证明的关于 'parent' 的属性都应该是对每个孩子都为真”意味着“关于 'child1..n' 的每个可证明的属性都应该对 'child1..n' 为真”?)

标签: c# dependency-injection inversion-of-control unity-container


【解决方案1】:

解决此问题的一种简单方法是使用strategy pattern。请注意,您可以在不更改设计的情况下添加或删除登录提供程序 - 您只需更改 DI 配置。

接口

public interface IAuthenticate{
    bool Login(string user, string pass);
    bool AppliesTo(string providerName);
}

public interface IAuthenticateStrategy
{
    bool Login(string providerName, string user, string pass);
}

验证提供者

public class TwitterAuth : IAuthenticate
{
    bool Login(string user, string pass)
    {
        //connect to twitter api
    }
    
    bool AppliesTo(string providerName)
    {
        // I used the type name for this example, but
        // note that you could use any string or other
        // datatype to select the correct provider.
        return this.GetType().Name.Equals(providerName);
    }
}

public class FacebookAuth: IAuthenticate
{
    bool Login(string user, string pass)
    {
        //connect to fb api
    }

    bool AppliesTo(string providerName)
    {
        return this.GetType().Name.Equals(providerName);
    }
}

策略

public class AuthenticateStrategy: IAuthenticateStrategy
{
    private readonly IAuthenticate[] authenticateProviders;
    
    public AuthenticateStrategy(IAuthenticate[] authenticateProviders)
    {
        if (authenticateProviders == null)
            throw new ArgumentNullException("authenticateProviders");
            
        this.authenticateProviders = authenticateProviders;
    }

    public bool Login(string providerName, string user, string pass)
    {
        var provider = this.authenticateProviders
            .FirstOrDefault(x => x.AppliesTo(providerName));

        if (provider == null)
        {
            throw new Exception("Login provider not registered");
        }

        return provider.Login(user, pass);
    }
}

统一注册

// Note that the strings used here for instance names have nothing 
// to do with the strings used to select the instance in the strategy pattern
unityContainer.RegisterType<IAuthenticate, TwitterAuth>("twitterAuth");
unityContainer.RegisterType<IAuthenticate, FacebookAuth>("facebookAuth");
unityContainer.RegisterType<IAuthenticateStrategy, AuthenticateStrategy>(
    new InjectionConstructor(
        new ResolvedArrayParameter<IAuthenticate>(
            new ResolvedParameter<IAuthenticate>("twitterAuth"),
            new ResolvedParameter<IAuthenticate>("facebookAuth")
        )
    ));

用法

private readonly IAuthenticateStrategy _authenticateStrategy;

public AuthenticateController(IAuthenticateStrategy authenticateStrategy)
{
    if (authenticateStrategy == null)
        throw new ArgumentNullException("authenticateStrategy");
        
    _authenticateStrategy = authenticateStrategy;
}



// login with twitter
public virtual ActionResult Twitter(string user, string pass)
{
    bool success =
            _authenticateStrategy.Login("TwitterAuth", user, pass);
}



// login with fb
public virtual ActionResult Facebook(string user, string pass)
{
    bool success =
            _authenticateStrategy.Login("FacebookAuth", user, pass);
}

unity.config

您可以在 unity.config 上执行此操作,而不是“Unity 注册”

<register type="IAuthenticate" mapTo="TwitterAuth" name="twitterAuth" />
<register type="IAuthenticate" mapTo="FacebookAuth" name="facebookAuth" />
<register type="IAuthenticateStrategy" mapTo="AuthenticateStrategy" />

【讨论】:

  • 能否请您使用web.config 为我提供等效的unity 注册,因为我们不应该每次都更改代码/unityconfig.cs 并计划在不更改配置的情况下更改配置构建/部署
  • 我为上述请求创建了separate thread,欢迎您提供解决方案
  • 你如何用 .net core 做到这一点?
  • 统一注册有一点错误。它在策略的构造函数中注册了 2 个要注入的数组。这应该是: unityContainer.RegisterType( new InjectionConstructor( new ResolvedArrayParameter( new ResolvedParameter("twitterAuth"), new ResolvedParameter("facebookAuth") ) ));
【解决方案2】:

Unity 离不开您的帮助。您可以在注册 IAuthenticate 类型时提供名称:

unityContainer.RegisterType<IAuthenticate, TwitterAuth>("Twitter");
unityContainer.RegisterType<IAuthenticate, FacebookAuth>("Facebook");

您将不再希望将 IAuthenticate 实例直接注入到您的 AuthenticateController 中。您可以根据不统一的条件(服务定位器样式)获得所需的实例:

myContainer.Resolve<IAuthenticate>("Twitter");

或者您将注入一个为您执行此操作的工厂(如果您喜欢严格的 DI 风格)。

【讨论】:

    猜你喜欢
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    相关资源
    最近更新 更多