【问题标题】:Simple Injector: Cyclic Graph Error简单注射器:循环图错误
【发布时间】:2016-12-10 02:32:56
【问题描述】:

注册表:

container.Register<IAuthenticationHandler, AuthenticationHandler>(Lifestyle.Transient);
container.Register<IUserHandler, UserHandler>(Lifestyle.Transient);    

第 1 类:

public UserHandler(IAuthenticationHandler authenticationHandler)
{
    _authenticationHandler = authenticationHandler;
} 

第 2 类:

public AuthenticationHandler(IUserHandler userHandler)
{
    _userHandler = userHandler;
}

我了解循环问题是什么。当 UserHandler 初始化时,它正在注入 AuthenticationHandler 实现,然后尝试创建 UserHandler 实例,循环开始......

我的问题是如何在(简单注射器)这种情况以及我需要像这样注射的其他情况下解决这个问题?

谢谢!

更新:

function AddUser(User user){ // User Handler
    _authenticationHandler.GenerateRandomSalt();
    string hashedPassword = _authenticationHandler.HashPassword(user.Password.HashedPassword, salt);
}

function Authenticate(string username, string password){ // Authentication Handler
    _userHandler.GetUserByUsername(username?.Trim());
}

基本上我需要在AuthenticationHandler中调用UserHandler来获取用户并验证是否有用户。

我需要调用 UserHandler 中的 AuthenticationHandler 来获取对密码进行加盐和哈希处理的函数。

我想我可以调用存储库来获取用户,但我不应该通过服务处理程序,以防在用户服务中完成更多事情

【问题讨论】:

  • 需要更多示例来展示如何打破循环。我可以肯定地说的一个提示是,您选择的 DI 容器在这里并不重要。

标签: c# .net dependency-injection simple-injector


【解决方案1】:

循环依赖通常是由违反SOLID 原则引起的,因为具有过于宽泛的接口和过多功能的类更有可能需要彼此的功能。

我相信您的情况也是如此,因为UserHandler.AddUser 功能取决于AuthenticationHandler.GenerateRandomSaltHashPassword 功能,而与AuthenticationHandler(即Authenticate)不同的功能取决于来自UserHandler 的另一个功能。这强烈表明IAuthenticationHandler 抽象实际上违反了Interface Segregation Principle,并且它的实现违反了Single Responsibility Principle

解决方案是将IAuthenticationHandler 及其实现拆分为多个独立的部分。比如

interface IPasswordUtilities {
    // NOTE: I believe GenerateRandomSalt is an implementation detail;
    // it should not be part of the interface
    string HashPassword(string plainPassword);
}

interface IAuthenticationHandler {
    void Authenticate(string username, string password);
}

class PasswordUtilities : IPasswordUtilities {
    // implementation
}

class AuthenticationHandler : IAuthenticationHandler {
    public AuthenticationHandler(IUserHandler userHandler, IPasswordUtilities utilities) { 
        ... 
    }
}

class UserHandler : IUserHandler {
    public UserHandler(IPasswordUtilities utilities) { ... }

    public void AddUser(User user) {
        string hashedPassword = _utilities.HashPassword(user.Password.HashedPassword);
    }
}

这将优雅地解决您的问题,因为您:

  • 通过将部分逻辑提取到更小、更集中的类中来消除循环依赖
  • 通过修复 SRP 和 ISP 违规,您可以使您的代码库更易于维护。

结束图将如下所示:

new AuthenticationHandler(
    new UserHandler(
        new PasswordUtilities()),
    new PasswordUtilities());

【讨论】:

  • 谢谢!我会将散列拆分为另一个处理程序。仍在学习最佳实践。再次感谢!
【解决方案2】:

处理此问题的一种方法是实现一种方法,让其中一个或另一个创建其依赖项的实例。

您可以在这里使用身份验证处理程序的抽象工厂,

public interface IAuthenticationHandlerFactory
{
    IAuthenticationHandler Create(IUserHandler userHandler);
}

public class AuthenticationHandlerFactory : IAuthenticationHandlerFactory
{
    public IAuthenticationHandler Create(IUserHandler userHandler)
    {
        return new AuthenticationHandler(userHandler);
    }
}

UserHandler改成依赖工厂,

public class UserHandler : IUserHandler
{
    private IAuthenticationHandler _authenticationHandler;

    public UserHandler(IAuthenticationHandlerFactory authenticationHandler)
    {
        _authenticationHandler = authenticationHandler.Create(this);
    }
}

然后像往常一样在容器中注册,

container.Register<IAuthenticationHandlerFactory, AuthenticationHandlerFactory>(Lifestyle.Singleton);

这绝对确实将工厂的具体实现耦合到身份验证处理程序。我会保持这个相对简单,这样它就不会过于复杂。


另一种方法是使用委托类型。这样,您可以在组合根中保留对具体实现的引用。

UserHandler 类将是

public class UserHandler : IUserHandler
{
    private IAuthenticationHandler _authenticationHandler;

    public UserHandler(Func<IUserHandler, IAuthenticationHandler> authenticationHandler)
    {
        _authenticationHandler = authenticationHandler(this);
    }
}

Func&lt;,&gt; 的注册是

container.Register<Func<IUserHandler, IAuthenticationHandler>>(() => u => new AuthenticationHandler(u), Lifestyle.Singleton);

【讨论】:

  • 我明天试试这个。谢谢!
猜你喜欢
  • 2017-02-05
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2021-09-19
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 2012-10-27
相关资源
最近更新 更多