关于c#:控制/依赖注入和转换运算符的反转

Inversion of Control / Dependency Injection and conversion operators

我有一个用户模型类,它被许多MVC应用程序使用,我正试图将其转换为使用IOC:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UserModel
{
    public string Login { get; set; }

    [DisplayName("Display Name")]
    public string DisplayName { get; set; }

    public static explicit operator UserModel(string userLogin)
    {
        IKernel IoC; // Where do I get this from?
        var ActiveDirRepo = IoC.Get<IActiveDirectoryRepository>();

        var User = ActiveDirRepo.FindById<ActiveDirectoryUser>(userLogin.ToUpper());

        return new UserModel()
        {
            Login = userLogin,
            DisplayName = User == null ? userLogin.ToUpper() : User.Name
        };
    }
}

它有一个从stringUserModel的显式转换运算符。以前我只是使用了一个ActiveDirectoryRepository,但是我想开始使用ioc/di。

我现在遇到的问题是,如何引用我的IOC容器(IKernel)?

我不能把它从外部注入到EDCOX1 1中,因为操作符就是创建它的东西。


从思想上讲,IOC容器应该只存在于应用程序的复合根中。考虑到这一点,需要弄清楚类的依赖性,并通过构造函数注入它们(如果您不能进行构造函数DI,那么许多IOC容器都会提供其他注入方法)。

在这里,您需要访问IActiveDirectoryRepository才能创建一个用户模型。但是,您的创建方法explicit operator UserModel是一个静态方法,不能访问实例变量。不幸的是,这限制了您使用依赖项注入来利用IOC的能力,所以这里有一些想法:

  • 直接:使用服务定位器模式。使用服务定位器获取EDCOX1 OR 0的实例。这将很好地直接适应当前的实现(将内核行交换到服务定位器)。但是,这种模式也有一些缺陷;最重要的是,你将EDCOX1的0源代码内化,这将使单元测试变得不那么有效。

  • 传统:让你的UserModel创建者进入一个工厂,通过它的建设者接受IActiveDirectoryRepository。然后,工厂创建的任何UserModel使用提供的IActiveDirectoryRepository

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public sealed class UserFactory
    {
        private readonly IActiveDirecotryRepository repository;
        public UserFactory(IActiveDirectoryRepository repository)
        {
            this.repository = repository;
        }

        public UserModel CreateNewUser(string userLogin)
        {
            var User = repository.FindById<ActiveDirectoryUser>(userLogin.ToUpper());
            return new UserModel()
            {
                Login = userLogin,
                DisplayName = User == null ? userLogin.ToUpper() : User.Name
            }
        }
    }
  • 花式:在IActiveDirectoryRepository上创建一个闭包,并将创建者作为一级函数传递。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    static Func<string, UserModel> CreateFactory(IActiveDirectoryRepository repository)
    {
        return (userLogin) =>
        {
            var User = repository.FindById<ActiveDirectoryUser>(userLogin.ToUpper());
            return new UserModel()
            {
                Login = userLogin,
                DisplayName = User == null ? userLogin.ToUpper() : User.Name
            }  
        }
    }

这种转换应该很简单,不需要外部依赖。如果需要注入某种依赖项,那么忘记了转换,就没有解决方案。

足够简单,将ikernel传递到构建方法:

1
2
3
4
5
6
7
8
9
10
11
12
public static UserModel Build(string userLogin, IKernel IoC)
{
    var ActiveDirRepo = IoC.Get<IActiveDirectoryRepository>();

    var User = ActiveDirRepo.FindById<ActiveDirectoryUser>(userLogin.ToUpper());

    return new UserModel()
    {
        Login = userLogin,
        DisplayName = User == null ? userLogin.ToUpper() : User.Name
    };
}

然后,您可以问自己,这个方法是否属于ikernel接口:

1
2
3
4
interface IKernel
{
    UserModel Build(string userLogin);
}


国际奥委会没有什么魔力。有时,您仍然需要一个起点,从该起点可以访问应用程序的IOC容器,如下所示:

1
var ioc = Application.Instance.Container.Get<IActiveDirectoryRepository>();

您不需要从任何地方访问IOC容器,因为通过容器创建的对象已经注入了它们的依赖项,但至少一些根对象需要获得对容器的访问权。


如果ninject不提供实现,则可以使用服务定位器模式表单microsoft.practices.servicelocation i。如果不这样做,就直接开始实施。

不管您能用这段代码做什么,我强烈建议您不要像以前那样实现显式转换运算符。这是一种不好的做法。