10

我有一些使用 ninject 注入依赖项的代码,这些依赖项是实际的字符串。例如,这是注入字符串而不是创建新对象的反模式吗?

即我想注入用户名和密码,创建一个名为凭据的小类并注入用户名和密码这两个属性实际上会更好吗?

将字符串注入构造函数可以通过

kernel.Bind<IUser>().To<User>()
      .WithConstructorArgument(@"username", configuration.Username)
      .WithConstructorArgument(@"password", configuration.Password);

这个代码有味道吗?

对我正在做的事情有什么想法或改进吗?

4

6 回答 6

8

我更喜欢在ToMethod()这里使用:

kernel.Bind<IUser>()
      .ToMethod(ctx => new User(configuration.Username, configuration.Password));

如果User构造函数有其他依赖项,那么我会遵从@jgauffin 的回答。

您仍然可以使用ToMethod()with Kernel

kernel.Bind<IUser>()
      .ToMethod(ctx => new User(configuration.Username,
                                configuration.Password,
                                ctx.Kernel.Get<Foo>()));
于 2013-09-11T11:11:08.180 回答
3

Is this code smell ?

Yes. Either create a ConfigurationRepository or create a factory/builder (two different design patterns) which creates the different services and then register that factory/builder in the container instead.

I got an issue with this code too:

kernel.Bind<IUser>().To<User>()
      .WithConstructorArgument(@"username", configuration.Username)
      .WithConstructorArgument(@"password", configuration.Password);

A IoC container is primarly not used to create domain entities but to create services/repositories/controllers etc. i.e. to create the objects which control the flow in your application.

于 2013-09-11T10:28:50.113 回答
0

这里似乎有两个问题:

  1. 注入原语是代码气味/反模式吗?
  2. 是否应该创建某种类型来组成用户名和密码字符串?

这些是完全不同的问题。首先,依赖是依赖。它可以是复杂的或原始的,但依赖关系的管理应该是一致的。如果您使用的是 IoC 容器,那么注入原语绝对不是代码异味。IoC 容器和组合根是代码的特权部分,它们了解所有服务的需求以及如何满足它们。从概念上讲,这适用于复杂和原始类型。然而,实际上,注册复杂依赖项和原始依赖项有不同的机制。您列出的参数名称方法是完全有效的。其他方法取决于参数索引。在两者之间进行选择很麻烦,但归根结底是您是否希望在不更改连线代码的情况下自由地重命名构造函数参数或重新排序构造函数参数。

另一种方法是有具体的依赖关系(或者更糟糕的是,服务定位器模式),这是一个滑坡到一个难以辨认的泥球。

其次,这两个字符串是否应该组合成一个类型取决于这些值一起使用的频率以及您想要的 DRY 程度。在某些时候,追求 DRYness 的回报会递减。此外,如果这些值总是一起注入,您可以考虑简单地重构 Ninject 配置调用。无论您选择什么,请确保基本原理在整个代码库中是一致的。

最后一点,使用 IoC 容器管理实体被认为是代码异味。实体通常负责在执行域操作时维护域不变量。(不确定示例代码片段的上下文/意图是什么,因此无法提供替代方案。)

于 2014-08-28T18:10:36.760 回答
0

我竭尽全力避免注入原始类型。

查看您发布的代码,我的第一直觉是创建一个“IConfigurationService”并根据需要注入它。该服务将包含用户名和密码的属性。

于 2013-09-11T18:30:59.720 回答
0

这是一个更通用的答案,但与其直接注入字符串,不如创建一个接口、一个提供程序并注入提供程序本身是一个更好的主意。因此,将来,如果您需要更改读取和创建字符串的方式,那会更容易。(假设您今天从 app.config 中读取它,但随后您决定将其存储在 DB 中,或者从注册表中读取它,或者从 Web 请求中传递它。)示例应用程序:

 public interface IMachineIdProvider
    {
//Assume we are returning a machine Id.
        String ResolveMachineId();
    }

并实施。假设我们正在从 webconfig 中读取它

 public class MachineIdProvider : IMachineIdProvider
    {
        private string _machineId;
        public string ResolveMachineId()
        {
            if (String.IsNullOrEmpty(_machineId))
            {
                this._machineId=  ConfigurationManager.AppSettings["MachineId"];
            }

            return this._machineId;
        }
    }

并以这种方式注入:

kernel.Bind<IMachineIdProvider>().To<MachineIdProvider>().InSingletonScope();

用接口声明类

public class MyWonderfulController  : BaseController
{
    private IMachineIdProvider _machineIdProvider;

    public MyWonderfulController(IMachineIdProvider machineIdProvider)
    {
       this._machineIdProvider = machineIdProvider;
    }

}

因此,无论您需要在哪里使用它,都可以调用

var id = _machineIdProvider.ResolveMachineId()

这样,如果你改变获取字符串的方式,你只需要改变方法本身或者创建另一个注入。

于 2020-06-04T16:16:53.513 回答
-1

考虑一个专门用于保存所有用于注入的接口的程序集。在我当前的工作解决方案(棱镜,使用 MEF)中,我有一个类,它为您指定的用途声明常量字符串。

为什么要使用常量而不是文字?因为它是一个符号;它是可重构的、可发现的,字符串的实际内容无关紧要。我总是在我的字符串上使用 GUID 作为后缀,这样我就可以“保证”唯一性。常量也可用于属性装饰。

/// <summary>
/// Exposes top level application region names for use when registering views.
/// </summary>
public static class Regions
{
    public const string WORKSPACE_REGION = "WorkspaceRegion {4CCDA460-D1A8-4BCE-863A-593021079F02}"; //names include a GUID to prevent clashes.

    public const string TOOL_DIAGRAM_REGION = "ToolDiagramRegigon {AD3CED71-C49D-4BD8-86FF-57E5F35116D3}";

    public const string STATUS_REGION = "StatusRegion {4CEF7A12-1E92-4EED-BD46-F70F07E27662}";

    public const string TOOLS_REGION = "ToolsRegion {3C6F99B2-6414-4E06-ACC5-7445944FFA29}";

    public const string HARDWARE_INTERFACE_REGION = "HardwareInterfaceRegion {4F16ECD1-D3F5-4BE2-BB00-DD148BAE8A83}";
}

注意 中的拼写错误TOOL_DIAGRAM_REGION。我搞砸了也没关系,因为没有开发人员需要再次输入它。我只注意到此错误,因为我在将字符串粘贴到此处时对其进行了扫描。

于 2013-09-11T10:20:25.327 回答