【问题标题】:Ninject : Constructor parameterNinject : 构造函数参数
【发布时间】:2023-12-21 19:44:01
【问题描述】:

我将 Ninject 与 ASP.NET MVC 4 一起使用。我正在使用存储库并希望进行构造函数注入以将存储库传递给其中一个控制器。

这是我的存储库界面:

public interface IRepository<T> where T : TableServiceEntity
{
    void Add(T item);
    void Delete(T item);
    void Update(T item);
    IEnumerable<T> Find(params Specification<T>[] specifications);
    IEnumerable<T> RetrieveAll();
    void SaveChanges();
}

下面的AzureTableStorageRepositoryIRepository&lt;T&gt; 的实现:

public class AzureTableRepository<T> : IRepository<T> where T : TableServiceEntity
{
    private readonly string _tableName;
    private readonly TableServiceContext _dataContext;

    private CloudStorageAccount _storageAccount;
    private CloudTableClient _tableClient;

    public AzureTableRepository(string tableName)
    {
        // Create an instance of a Windows Azure Storage account
        _storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString);

        _tableClient = _storageAccount.CreateCloudTableClient();
        _tableClient.CreateTableIfNotExist(tableName);
        _dataContext = _tableClient.GetDataServiceContext();
        _tableName = tableName;
    }

请注意所需的 tableName 参数,因为我使用通用表存储库将数据持久保存到 Azure。

最后我有了以下控制器。

public class CategoriesController : ApiController
{
    static IRepository<Category> _repository;

    public CategoriesController(IRepository<Category> repository)
    {
        if (repository == null)
        {
            throw new ArgumentNullException("repository");
        }

        _repository = repository;
    }

现在我想将一个存储库注入到控制器中。所以我创建了一个包含绑定的模块:

/// <summary>
/// Ninject module to handle dependency injection of repositories
/// </summary>
public class RepositoryNinjectModule : NinjectModule
{
    public override void Load()
    {
        Bind<IRepository<Category>>().To<AzureTableRepository<Category>>();
    }
}

模块的加载在NinjectWebCommon.cs中完成

/// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
        // Load the module that contains the binding
        kernel.Load(new RepositoryNinjectModule());

        // Set resolver needed to use Ninject with MVC4 Web API
        GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);
    } 

DependencyResolver 的创建是因为 Ninject 的 DependencyResolver 实现了 System.Web.Mvc.IDependencyResolver,而这不能分配给 WebApi 应用程序的 GlobalConfiguration.Configuration

所有这些都到位后,Ninject 部分实际上是在 Controller 中注入正确的类型,但 Ninject 无法在 AzureTableRepository 的构造函数中注入 tableName 参数。

在这种情况下,我将如何做到这一点?我查阅了很多文章和 ninject 文档以了解如何使用参数,但我似乎无法使其正常工作。

任何帮助将不胜感激。

【问题讨论】:

    标签: dependency-injection asp.net-mvc-4 ninject


    【解决方案1】:

    我会使用WithConstructorArgument() 方法,例如...

    Bind<IRepository<Category>>().To<AzureTableRepository<Category>>()
        .WithConstructorArgument("tableName", "categories");
    

    存储库设计的其余部分可能是另一个问题。恕我直言,创建一张桌子或在演员身上做任何繁重的工作似乎是一个很大的禁忌。

    【讨论】:

    • 我想我会从构造函数中删除创建,...,因为在构造函数中进行大量初始化确实不是一个好习惯。谢谢!
    【解决方案2】:

    与此同时,我一直在与 Providers 一起玩,试图做到这一点,它似乎奏效了。

    我不知道这是个好主意还是有点矫枉过正,但这是我所做的: 我创建了一个通用的提供者类:

    public abstract class NinjectProvider<T> : IProvider
    {
        public virtual Type Type { get; set; }
        protected abstract T CreateInstance(IContext context);
    
        public object Create(IContext context)
        {
            throw new NotImplementedException();
        }
    
        object IProvider.Create(IContext context)
        {
            throw new NotImplementedException();
        }
    
        Type IProvider.Type
        {
            get { throw new NotImplementedException(); }
        }
    }
    

    然后我在 AzureTableRepositoryProvider 中实现了那个。 (T 支持为多个实体类型使用相同的存储库。)

    public class AzureTableRepositoryProvider<T> : Provider<AzureTableRepository<T>> where T : TableServiceEntity
    {
        protected override AzureTableRepository<T> CreateInstance(IContext context)
        {
            string tableName = "";
    
            if (typeof(T).Name == typeof(Category).Name)
            {
                // TODO Get the table names from a resource
                tableName = "categories";
            }
            // Here other types will be addedd as needed
    
            AzureTableRepository<T> azureTableRepository = new AzureTableRepository<T>(tableName);
    
            return azureTableRepository;
        }
    }
    

    通过使用此提供程序,我可以传入正确的表名以供存储库使用。但对我来说,还有两个问题:

    1. 这是一种好的做法还是我们可以做的更简单?
    2. 在 NinjectProvider 类中,我有两个 notImplementedException 案例。我该如何解决这些?我使用了以下链接中的示例代码,但这不起作用,因为 Provider 是抽象的,并且代码没有 create 方法的主体...enter link description here

    【讨论】: