Ninject无法注入并抛出空引用异常。

5
我是新手,对Ninject不太熟悉,肯定是我做错了什么,只是不确定是哪里出了问题。我在我的MVC3 Web应用程序中使用Ninject和Ninject.MVC3。以下是我尝试做的示例。
我正在使用存储库模式:
public interface IRepository<T>
{
    T Get(object id);
    IList<T> GetAll();
    void Add(T value);
    void Update(T value);
    void Delete(T value);
}

对于具体类型:

public Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }

    public Customer()
    {
    }
}

现在我有两个独立的存储库,一个是需要注入到数据库存储库的缓存版本:
public CachedCustomerRepository : IRepository<Customer>
{
    private IRepository<Customer> _repository;

    public Customer Get(object id)
    {
        Customer cust = new Customer();
        IList<Customer> custs = GetAll();
        if (custs != null && custs.Count > 0)
            cust = custs.FirstOrDefault(c => c.Id == int.Parse(id.ToString()));

        return cust;
    }

    public IList<Customer> GetAll()
    {
        IList<Customer> custs = HttpRuntime.Cache["Customers"] as IList<Customer>;
        if (custs == null)
        {
            custs = _repository.GetAll();
            if (custs != null && custs.Count() > 0)
            {
                double timeout = 600000d;
                HttpRuntime.Cache.Insert("Customers", custs, null, DateTime.UtcNow.AddMilliseconds(timeout), System.Web.Caching.Cache.NoSlidingExpiration);
            }
            else
            {
                throw new NullReferenceException();
            }
        }

        return custs;
    }

    public void Add(Customer value)
    {
        throw new NotImplementedException();
    }

    public void Update(Customer value)
    {
        throw new NotImplementedException();
    }

    public void Delete(Customer value)
    {
        throw new NotImplementedException();
    }

    public CachedCustomerRepository()
    {
    }

    [Inject]
    public CachedCustomerRepository(IRepository<Customer> repository)
    {
        _repository = repository;
    }
}

public class CustomerRepository : IRepository<Customer>
{   
    public Customer Get(object id)
    {
        Customer cust = new Customer();
        IList<Customer> custs = GetAll();
        if (custs != null && custs.Count > 0)
            cust = custs.FirstOrDefault(c => c.Id == int.Parse(id.ToString()));

        return cust;
    }

    public IList<Customer> GetAll()
    {
        //Customer retrieval code
    }

    public void Add(Customer value)
    {
        throw new NotImplementedException();
    }

    public void Update(Customer value)
    {
        throw new NotImplementedException();
    }

    public void Delete(Customer value)
    {
        throw new NotImplementedException();
    }

    public CachedCustomerRepository()
    {
    }
}

I set up a NinjectModule like this:

public class ServiceModule : NinjectModule
{
    public override void Load()
    {
        Bind<IRepository<Customer>>().To<CustomerRepository>();
    }
}

我修改了位于AppStart文件夹中的NinjectMVC3.cs文件,以便在创建内核时获取该模块:

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel(new ServiceModule());
    RegisterServices(kernel);
    return kernel;
}

在我的控制器中,我正在使用以下代码:

public ViewResult Index()
{
    IRepository<Customer> custRepo = new CachedCustomerRepository();
    return View(custRepo.GetAll());
}

在我的中,它在_repository.GetAll()这一行崩溃。

我设置了断点以确保CreateKernel()正在执行并获取绑定,它确实在执行。 我只是不确定为什么注入没有发生。 另外一个副笔记,我不知道是否重要,IRepository、Repositories和具体类型都在一个单独的类库中,并且被引用在mvc3 web应用程序中。 Web应用程序和类库都引用了Ninject,而Web应用程序还引用了Ninject.MVC3。 绑定和内核创建都在Web应用程序中进行。


2
+1 对于初学依赖注入的人来说,感到沮丧是很正常的。 - TheCloudlessSky
5个回答

3

首先,您在控制器中调用了错误的构造函数。这个无参构造函数没有调用任何其他内容,这就是为什么您会收到空指针异常的原因。其次,您希望重构控制器,以便没有直接依赖。

您需要执行以下操作:

public class SomeController
{
    private readonly IRepository<Customer> repo;

    public SomeController(IRepository<Customer> repo)
    {
        this.repo = repo;
    }

    public ViewResult Index()
    {
        return View(this.repo.GetAll());
    }
}

这样,Ninject会为您解决依赖关系! MVC将要求Ninject的内核创建一个带有IRepository<Customer>的控制器。由于Ninject具有此“绑定”,它将尝试为您实例化CustomerRepository
另外,为什么要创建“CachedRepository”?我真的认为这是过早优化。老实说,您只需要一个CustomerRepository,并将其连接到Ninject模块中即可。

我最终做的是为repo创建一个属性,并注入它,因为将来控制器中会有多种repo类型,我还对其应用了自定义属性。这使我能够使用上下文属性绑定。 - Alexander Kahoun
1
@Alexander - 虽然属性注入确实可行,但通常并不被看好。要添加更多的存储库依赖项,只需添加另一个构造函数参数即可。在控制器中需要什么样的上下文绑定?无论如何,我很高兴您能解决问题。干杯! - TheCloudlessSky
这也是好知道的,我没有意识到属性注入被看作是不好的。 - Alexander Kahoun

0
问题在于,当您在操作方法中创建CachedCustomerRepository时,您只是自己实例化它,而没有让Ninject为您实例化它并随后注入其依赖项。

您应该为控制器使用构造函数注入。

例如:

public class MyController : Controller
{
    public IRepository<Customer> CustomerRepo { get; protected set; }

    public MyController(IRepository<Customer> customerRepo)
    {
        CustomerRepo = customerRepo;
    }

    public ViewResult Index()
    {
        return View(CustomerRepo.GetAll());
    }
}

您的情况有点令人困惑,因为您正在将 IRepository<Customer> 注入到需要注入到 MyController 中的 IRepository<Customer> 中。


0

你在Global.asax中让类继承了NinjectHttpApplication吗?


我认为使用Ninject.MVC3 2.2.1.0时,如果您正在使用AppStart\NinjectMVC3.cs,则不必这样做。 - Alexander Kahoun
@Alexander Kahoun:很有可能;看起来我已经太久没有使用Ninject了... - Aasmund Eldhuset

0
请确保调用DependencyResolver.SetResolver(CreateKernel());以告诉MVC您将使用哪个解析器。您可能已经调用了它,但我在您的帖子中没有看到它-否则您的代码看起来很好。

是的,我在发布答案后也意识到了这一点。我还没有尝试过新的AppStart Nuget功能。不过还是谢谢你的明确回答,现在我将来会知道了 :) - Buildstarted
不用担心 :) 新的东西使得与MVC的集成变得非常顺畅。 - TheCloudlessSky

0
为了充分利用IOC的优势,您应该从控制器类中重构CachedCustomerRepository依赖项。您需要向Ninject模块添加新的绑定。此绑定将需要使用上下文来确定是否将“IRepository”绑定到CachedCustomerRepository实例或MVC控制器实例。一旦您将其分解出来,Ninject将创建和管理两个对象的生命周期。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接