C#MVC,依赖注入和子对象

时间:2016-07-05 12:25:34

标签: asp.net-mvc dependency-injection

我一直在关注Adam Freeman撰写的Pro ASP.NET MVC5书籍,虽然它对于揭示MVC和DI的基础知识非常棒,但是它在应用这些内容方面缺乏细节。学习现实世界的例子。

我想返回文章的视图,并显示该文章的所有评论。使用网络表单这很简单,但我想使用MVC和DI,我确定如何继续。

我有文章类:

public class Article
{
    //[DataMember]
    [HiddenInput(DisplayValue = false)]
    public int ID { get; set; }
    //[DataMember]
    [Required(ErrorMessage = "Please enter a headline")]
    [StringLength(100, ErrorMessage = "Headline cannot be longer than 100 characters.")]
    public virtual string Headline { get; set; }

    [...]

    //TO DO: Find a way to use DI for this
    //[NotMapped]
    private IEnumerable<Comment> comments;
    [NotMapped]
    public IEnumerable<Comment> Comments
    {
        get
        {
            if (null == comments)
            {
                ICommentRepository cRepository = new CommentRepository();
                comments = cRepository.Comments.Where(c => c.ArticleID == this.ID).ToList();
            }
            return comments;
        }
    }
}

我遇到的问题是:

        ICommentRepository cRepository = new CommentRepository();
        comments = cRepository.Comments.Where(c => c.ArticleID == this.ID).ToList();

以上几行意味着我现在依赖于一个接口和一个具体的存储库。

控制器如下:

public class ArticleController : Controller
{
    private IArticleRepository repository;


    public ArticleController(IArticleRepository articleRepository)
    {
        repository = articleRepository;
    }

    public PartialViewResult SnippetView(int id)
    {
        return PartialView(repository.GetByID(id));
    }
}

我没有看到添加CommentRepository的方法 - 除非我添加一个Property Injection,它看起来像一个bodge,因为我必须为父类的所有子对象执行此操作。一些子对象也有子对象 - 在这种情况下,Comments有一个Member对象:

    public PartialViewResult SnippetView(int id)
    {
        Article a = repository.GetByID(id);
        a.CommentReposiory = ICommentRepository;

        return PartialView(repository.GetByID(id));
    }

我根据@JDBennett提供的回复更新了课程

文章类为评论服务添加了参数化构造函数:

public class Article
{
    private ICommentService _commentService;

    //public Article()
    //{

    //    IKernel ninjectKernel = new StandardKernel();
    //    ninjectKernel.Bind<ICommentService>().To<CommentService>();
    //    ninjectKernel.Bind<ICommentRepository>().To<CommentRepository>();
    //    _commentService = ninjectKernel.Get<ICommentService>();

    //}

    public Article(ICommentService commentService)
    {
        _commentService = commentService;
    }

    //[DataMember]
    [HiddenInput(DisplayValue = false)]
    public int ID { get; set; }

    [...]

    [NotMapped]
    public IEnumerable<Comment> Comments
    {
        get
        {
            //return _commentService.Comments.ToList();

            return _commentService.Comments.Where(c => c.ID == this.ID).ToList();
        }
    }
}

评论服务界面和类如下:

评论服务界面:

public interface ICommentService
{
    IEnumerable<Comment> Comments { get; }
}

评论服务类:     公共类CommentService:ICommentService     {         private ICommentRepository cRepository;

    public CommentService(ICommentRepository repo)
    {
        cRepository = repo;
    }

    public IEnumerable<Comment> Comments
    {
        get
        {
            return cRepository.Comments.ToList();
        }
        /*set not yet implemented*/   
    }
}

ninject解析器代码是:

public class NinjectDependencyResolver : IDependencyResolver
{
    private IKernel kernel;

    public NinjectDependencyResolver(IKernel kernelParam)
    {
        kernel = kernelParam;
        AddBindings();
    }

    public object GetService(Type serviceType)
    {
        return kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return kernel.GetAll(serviceType);
    }

    private void AddBindings()
    {
        //put bindings here

        //comment service
        kernel.Bind<ICommentService>().To<CommentService>();

        //Community db
        kernel.Bind<IBlogRepository>().To<BlogRepository>();
        kernel.Bind<ICommentRepository>().To<CommentRepository>();
        //news db
        kernel.Bind<IArticleRepository>().To<ArticleRepository>();
        kernel.Bind<IMemberRepository>().To<MemberRepository>();
        //registration db
        kernel.Bind<IEventRepository>().To<EventRepository>();
        kernel.Bind<IRegistrationRepository>().To<RegistrationRepository>();


    }
}

ninjectwebcommon文件,位于app_start文件夹中:

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Nordics.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(Nordics.App_Start.NinjectWebCommon), "Stop")]

namespace Nordics.App_Start
{
    using System;
    using System.Web;

    using Microsoft.Web.Infrastructure.DynamicModuleHelper;

    using Ninject;
    using Ninject.Web.Common;

    public static class NinjectWebCommon 
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();

        /// <summary>
        /// Starts the application
        /// </summary>
        public static void Start() 
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }

        /// <summary>
        /// Stops the application.
        /// </summary>
        public static void Stop()
        {
            bootstrapper.ShutDown();
        }

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

                RegisterServices(kernel);
                return kernel;
            }
            catch
            {
                kernel.Dispose();
                throw;
            }
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            System.Web.Mvc.DependencyResolver.SetResolver(new Nordics.Infrastructure.NinjectDependencyResolver(kernel));
        }        
    }
}

代码编译,但是当我尝试运行代码时出现错误:

System.Reflection.TargetInvocationException was unhandled by user code.
Message=Exception has been thrown by the target of an invocation.
[...]
InnerException: System.InvalidOperationException
Message=The class 'Domain.Entities.Article' has no parameterless constructor.

如果我要更新Article类以删除参数化构造函数,那么添加remm&#d; d out无参数,代码编译并运行,这很好,除了它感觉有点像一个bodge,这使得代码不易测试。

1 个答案:

答案 0 :(得分:1)

您选择的IoC容器将解析组件。

你是正确的,因为你不想要属性注入,也不想直接实例化对象(如你的例子)。相反,我会提供一个CommentService,由您的文章类的Dependency Resolver解决。

下面我定义一个简单的界面。接口的实现很重要,因为我还使用依赖项解析器来解析存储库。

public interface ICommentService
{

      IEnumerable<Comment> Comments {get;set;}

 }


public class CommentService : ICommentService
{
    //Using the repository pattern - I assume the comments come from a Database somewhere
    private IRepository _repository;

    public CommentService(IRepository repository)
    {

         _repository = repository;

    }

    IEnumerable<Comments> 
    {
        get
        {
             return _repository.Comments().ToList();    

        }
        set
        {
               _repository.Comments.AddRange(val);
               _repository.SaveChanges();
        }        

}


public class Article
{
     private ICommentService _commentService;

     public Article (ICommentService commentService)
     {
         _commentService = commentService;

      }


    //[DataMember]
   [HiddenInput(DisplayValue = false)]
   public int ID { get; set; }

   //[DataMember]
   [Required(ErrorMessage = "Please enter a headline")]
   [StringLength(100, ErrorMessage = "Headline cannot be longer than 100 characters.")]
   public virtual string Headline { get; set; }



[NotMapped]
public IEnumerable<Comment> Comments
{
    get
    {
        //This is all you need 
        return _commentService.Comments;

    }
}

}