抽象工厂和控制反转在运行时解决

SOf*_*tic 4 c# asp.net-mvc dependency-injection ioc-container abstract-factory

我有以下类和接口结构,我很难尝试让代码完成我需要的工作.

public interface IUserManager
{
    int Add(User user);
}

public class UserManagerA : IUserManager{}
public class UserManagerB : IUserManager{}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我使用Ninject作为IoC容器,但如果其他容器解决了这个问题,我可以更改它:

这是在我的内心NinjectWebCommon.cs:

void RegisterServices(IKernel kernel)
{
    string userRole = CurrentUser.Role;//this gets the user logged in
    //This is the part I do not how to do
    //I wish I could just type this in:
    kernel.Bind<IUserManager>().To<UserManagerA>()
        .When(userRole == "RoleA"); // this doesn't work obviously
    kernel.Bind<IUserManager>().To<UserManagerB>()
        .When(userRole == "RoleB"); // same doesn't work
}
Run Code Online (Sandbox Code Playgroud)

所有这一切,以便在我的(MVC)控制器中我可以这样做:

public class UserController
{
    private readonly IUserManager _userManager;
    public UserController(IUserManager userManager)
    {
        _userManager = userManager;
    }
    public ActionResult Add(User user)
    {
        //this would call the correct manager
        //based on the userRole
        _userManager.Add(user);
    }
}
Run Code Online (Sandbox Code Playgroud)

我一直在阅读有关抽象工厂的文章,但没有找到解释如何将工厂与IoC容器集成并传递在运行时获得的参数来解决实现的文章.

Rag*_*lly 5

创建一个负责提供正确的类UserManager并将其注入您的控制器:

public class UserManagerProvider : IUserManagerProvider
{
    private readonly IContext _context;

    public UserManagerProvider(IContext context)
    {
        _context = context;
    }

    public IUserManager Create(User currentUser)
    {
        if (currentUser.Role == "User A")
            return _context.Kernel.Get<UserManagerA>();

        if (currentUser.Role == "User B")
            return _context.Kernel.Get<UserManagerB>();

        // Or bind and resolve by name
        // _context.Kernel.Get<IUserManager>(currentUser.Role);
    }
}
Run Code Online (Sandbox Code Playgroud)

在控制器中:

private readonly IUserManager _userManager;

public UserController(IUserManagerProvider userManagerProvider)
{
    _userManager = userManagerProvider.Create(CurrentUser);
}
Run Code Online (Sandbox Code Playgroud)

另外,作为旁注,您应该CurrentUserProvider负责获取当前用户.依赖静态方法会使单元测试变得困难,并且您实际上在所有引用它的类中隐藏了依赖项:

private readonly IUserManager _userManager;
private readonly User _currentUser;

public UserController(IUserManagerProvider userManagerProvider, ICurrentUserProvider currentUserProvider)
{
    _currentUser = currentUserProvider.GetUser();
    _userManager = userManagerProvider.Create(_currentUser);
}
Run Code Online (Sandbox Code Playgroud)