Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用字符串的依赖注入(ninject),反模式?_C#_Dependency Injection_Ninject - Fatal编程技术网

C# 使用字符串的依赖注入(ninject),反模式?

C# 使用字符串的依赖注入(ninject),反模式?,c#,dependency-injection,ninject,C#,Dependency Injection,Ninject,我有一些代码使用ninject注入依赖项,这些依赖项是实际的字符串。例如,这是注入字符串而不是创建新对象的反模式 也就是说,我想注入用户名和密码,创建一个名为credentials的小类,使用Usernamd和Password两个属性,然后注入这个类,会更好吗 将字符串注入构造函数可以通过 kernel.Bind<IUser>().To<User>() .WithConstructorArgument(@"username", configuration.Us

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

也就是说,我想注入用户名和密码,创建一个名为credentials的小类,使用Usernamd和Password两个属性,然后注入这个类,会更好吗

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

kernel.Bind<IUser>().To<User>()
      .WithConstructorArgument(@"username", configuration.Username)
      .WithConstructorArgument(@"password", configuration.Password);
kernel.Bind()到()
.WithConstructorArgument(@“username”,configuration.username)
.WithConstructorArgument(@“password”,configuration.password);
这代码有气味吗


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

考虑一个用于保存所有注入接口的程序集。在我当前的工作解决方案(prism,使用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}";
}
//
///公开顶级应用程序区域名称,以便在注册视图时使用。
/// 
公共静态类区域
{
public const string WORKSPACE_REGION=“WorkspaceRegion{4CCDA460-D1A8-4BCE-863A-593021079F02}”;//名称包括GUID以防止冲突。
public const string TOOL_DIAGRAM_REGION=“tooldiagramregon{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=“硬件接口区域{4F16ECD1-D3F5-4BE2-BB00-DD148BAE8A83}”;
}
注意
TOOL\u DIAGRAM\u REGION
中的拼写错误。我搞砸了也没关系,因为任何开发人员都不需要再次键入它。我注意到这个错误是因为我在这里粘贴字符串时扫描了字符串

这代码有气味吗

对。创建一个
ConfigurationRepository
或创建一个factory/builder(两种不同的设计模式),创建不同的服务,然后在容器中注册该factory/builder

我对这个代码也有问题:

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

IoC容器主要不用于创建域实体,而是用于创建服务/存储库/控制器等,即创建控制应用程序中流的对象。

我更喜欢在此处使用
ToMethod()

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

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


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

这里似乎有两个问题:

  • 注入原语是一种代码气味/反模式吗
  • 是否应该创建某种类型来组成用户名和密码字符串
  • 这些是完全不同的问题。首先,依赖关系是一种依赖关系。它可以是复杂的,也可以是原始的,但是依赖关系的管理应该是一致的。IoC容器和组合根是代码的特权部分,它们了解所有服务的需求以及如何满足这些需求。从概念上讲,这适用于复杂和基本类型。然而,实际上,注册复杂依赖项和原始依赖项有不同的机制。您列出的参数名称方法完全有效。其他方法依赖于参数索引。在这两种方法之间进行选择是一针见血的,但归根结底是想自由地重命名构造函数参数还是在不改变连接代码的情况下重新排序构造函数参数

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

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


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

    这是一个更一般的答案,但与其直接注入字符串,不如创建接口、提供程序并注入提供程序本身。因此,在将来,如果您需要更改读取和创建字符串的方式,这将更容易。(假设您今天正在从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();
    
    所以,无论你在哪里需要使用它,你都可以打电话

    var id = _machineIdProvider.ResolveMachineId()
    

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

    毫无理由地使用逐字字符串文字是不美观的,原因之一:)谢谢Jon,完全正确!因此,理想情况下,最好创建一个具有2个属性的小型类,创建一个接口并将实现注入到
    public class MyWonderfulController  : BaseController
    {
        private IMachineIdProvider _machineIdProvider;
    
        public MyWonderfulController(IMachineIdProvider machineIdProvider)
        {
           this._machineIdProvider = machineIdProvider;
        }
    
    }
    
    var id = _machineIdProvider.ResolveMachineId()