【问题标题】:Ninject - how and when to injectNinject - 如何以及何时注入
【发布时间】:2010-11-20 15:43:01
【问题描述】:

我是 DI 和 ninject 的新手,我有点挣扎 关于实际注射应该发生的时间以及如何开始 绑定。

我已经在我的 Web 应用程序中使用它并且它在那里运行良好, 但现在我想在类库中使用注入。

假设我有这样的课程:

public class TestClass
{
    [Inject]
    public IRoleRepository RoleRepository { get; set; }
    [Inject]
    public ISiteRepository SiteRepository { get; set; }
    [Inject]
    public IUserRepository UserRepository { get; set; }

    private readonly string _fileName;

    public TestClass(string fileName)
    {
        _fileName = fileName;
    }

    public void ImportData()
    {
        var user = UserRepository.GetByUserName("myname");
        var role = RoleRepository.GetByRoleName("myname");
        var site = SiteRepository.GetByID(15);
        // Use file etc
    }

}

我想在这里使用属性注入,因为我需要传入一个 我的构造函数中的文件名。我是否正确地说,如果我需要 传入构造函数参数,我不能使用构造函数注入? 如果我可以使用带有附加参数的构造函数注入,该怎么做 我把那些参数传进去了?

我有一个由 Test 类使用的控制台应用程序,它看起来像 如下:

class Program
{
    static void Main(string[] args)
    {
        // NinjectRepositoryModule Binds my IRoleRepository etc to concrete
        // types and works fine as I'm using it in my web app without any
        // problems
        IKernel kernel = new StandardKernel(new NinjectRepositoryModule());

        var test = new TestClass("filename");

        test.ImportData();
    }
}

我的问题是,当我调用 test.ImportData() 时,我的存储库为空 - 没有任何东西注入其中。我尝试创建另一个模块并调用

Bind<TestClass>().ToSelf();

我认为这可能会解决 TestClass 中的所有注入属性,但我无处可去。

我确定这是一个微不足道的问题,但我似乎无法找出答案 如何解决这个问题。

【问题讨论】:

    标签: dependency-injection ninject


    【解决方案1】:

    您直接更新TestClass,Ninject 无法拦截 - 请记住,没有像代码转换这样的魔法拦截您的 news 等。

    你应该改用kernel.Get&lt;TestClass&gt;

    如果做不到这一点,你可以在 new 之后用 kernel.Inject( test); 注入它

    我认为 wiki 中有一篇文章讨论了 InjectGet 等。

    请注意,一般情况下,直接调用 GetInject 是服务位置的“做错了”气味,这是一种反模式。对于您的 Web 应用程序,NinjectHttpModulePageBase 是拦截对象创建的钩子 - 在其他风格的应用程序中有类似的拦截器/逻辑位置可以拦截。

    是您的Bind&lt;TestClass&gt;().ToSelf(),通常StandardKernel 具有ImplicitSelfBinding = true,这将使其不必要(除非您想将其范围影响为.InTransientScope() 以外的其他内容)。

    最后一个风格点:- 您正在使用属性注入。这很少有充分的理由,因此您应该改用构造函数注入。

    去购买Dependency Injection in .NET by @Mark Seemann,他在这里有很多优秀的帖子,涵盖了依赖注入领域及其周围的许多重要但微妙的考虑因素。

    【讨论】:

    • 使用 kernel.Inject(test) 似乎有效。谢谢。出于好奇,另一个问题 - 我正在更新 TestClass 因为我需要将一个字符串传递给构造函数。有没有办法使用 kernel.Get() 传递参数?
    • 是的 - Get 有重载,您也可以将它们放入 Bind 中
    • 通常一个更好的模式来代替硬连线,因为它需要构造函数 arg 而不是引用你新建的配置器对象,然后在创建模块时绑定
    • 所以,在我想从控制台应用程序中注入一个类的情况下(它最终将成为一个服务),我不应该使用 Get 或 Inject 吗?我的控制台应用程序是否应该从中派生一个类来进行布线?我在文档中没有看到类似的内容。
    • 嗯,你有关于该主题的教程/博客文章的链接吗?
    【解决方案2】:

    好的,

    我已经找到了我需要的方法,这在一定程度上要感谢您的 cmets Ruben。我创建了一个新模块,它基本上保存了我在类库中使用的配置。在这个模块中,我可以使用占位符接口进行绑定,也可以将构造函数参数添加到 CustomerLoader。 以下是来自虚拟控制台应用程序的代码,用于演示这两种方式。

    这可能有助于其他人开始使用 Ninject!

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Ninject.Core;
    using Ninject.Core.Behavior;
    
    namespace NinjectTest
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                var kernel = new StandardKernel(new RepositoryModule(), new  ProgramModule());            
                var loader = kernel.Get<CustomerLoader>();
                loader.LoadCustomer();
                Console.ReadKey();
            }
        }
    
        public class ProgramModule : StandardModule
        {
            public override void Load()
            {
                // To get ninject to add the constructor parameter uncomment the line below
                //Bind<CustomerLoader>().ToSelf().WithArgument("fileName", "string argument file name");
                Bind<LiveFileName>().To<LiveFileName>();
            }
        }
    
        public class RepositoryModule : StandardModule
        {
            public override void Load()
            {
                Bind<ICustomerRepository>().To<CustomerRepository>().Using<SingletonBehavior>();
            }
        }
    
        public interface IFileNameContainer
        {
            string FileName { get; }
        }
        public class LiveFileName : IFileNameContainer
        {
            public string FileName
            {
                get { return "live file name"; }
            }
        }
    
    
        public class CustomerLoader
        {
            [Inject]
            public ICustomerRepository CustomerRepository { get; set; }
            private string _fileName;
    
            // To get ninject to add the constructor parameter uncomment the line below
            //public CustomerLoader(string fileName)
            //{
            //    _fileName = fileName;
            //}
            public CustomerLoader(IFileNameContainer fileNameContainer)
            {
                _fileName = fileNameContainer.FileName;
            }
    
            public void LoadCustomer()
            {
                Customer c = CustomerRepository.GetCustomer();
                Console.WriteLine(string.Format("Name:{0}\nAge:{1}\nFile name is:{2}", c.Name, c.Age, _fileName));
            }
        }
    
        public interface ICustomerRepository
        {
            Customer GetCustomer();
        }
        public class CustomerRepository : ICustomerRepository
        {
            public Customer GetCustomer()
            {
                return new Customer() { Name = "Ciaran", Age = 29 };
            }
        }
        public class Customer
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }
    }
    

    【讨论】:

    • 看起来不错 - 这就是我的意思。在真正的应用程序的上下文中,我猜 IFilenameContainer 的东西可能会由像 DocumentLocation 这样的一般东西代替,并且会从 File|Open 或命令行等的顶层填充。一般来说,包装正在传递的信息的概念将 params 中的原始数据转换为可以从更高级别获取信息的东西,这使得使用参数变得不那么普遍(我在上一个项目中只使用过一次)
    猜你喜欢
    • 1970-01-01
    • 2013-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多