【问题标题】:C# MVC, Dependency Injection And Child ObjectsC# MVC、依赖注入和子对象
【发布时间】:2016-07-05 12:25:34
【问题描述】:

我一直在关注 Adam Freeman 撰写的 Pro ASP.NET MVC5 书籍,虽然它对于揭示 MVC 和 DI 的基础知识非常棒,但在将所学知识应用于现实世界的示例时却有点缺乏细节。

我想返回一篇文章的视图,并显示该文章的所有 cmets。使用网络表单这很简单,但是我想使用 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();

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

Controller如下:

public class ArticleController : Controller
{
    private IArticleRepository repository;


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

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

我看不到向其中添加 CommentRepository 的方法 - 除非我添加一个属性注入,这似乎是一个障碍,因为我必须为父类的所有子对象执行此操作。一些子对象也有子对象——在这种情况下,评论有一个成员对象:

    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();
        }
    }
}

注释Service接口和类如下:

评论服务接口:

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

评论服务类: 公共类评论服务:ICommentService { 私有 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 无参数构造函数,代码编译并运行,这很好,只是感觉有点麻烦,这使得代码的可测试性较低。

【问题讨论】:

  • 您的Comments 属性应该有一个简单的{ get; set; },并且您将IArticleRepository 和ICommentRepository 都注入到控制器中。但是无论如何您都应该使用视图模型,而不是具有[NotMapped] 属性的数据模型
  • 你真的需要注入一个 ICommentRepository 吗?如果它们在同一个 DbContext 中,为什么不使用导航属性将评论映射到文章。就像斯蒂芬说的那样,将您的视图模型与您的 ef 模型分开。
  • @Fran Comments 表位于 Articles 表的单独 dbContext 中。
  • Article 类 public IEnumerable 中的这条语句在不同的上下文中如何工作
  • 嗨@Fran,我不确定我是否理解这个问题,但是:我在 web.config 文件中有 2 个连接字符串值,每个数据库/dbcontext 一个。

标签: asp.net-mvc dependency-injection


【解决方案1】:

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

您是正确的,因为您不想进行属性注入,也不想直接实例化对象(如您的示例中所示)。相反,我会为您的文章类提供一个由依赖解析器解析的 CommentService。

下面我定义一个简单的接口。接口的实现很重要,因为我也使用依赖解析器来解析 Repository。

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;

    }
}

}

【讨论】:

  • 嗨@JDBennett,我已经更新了我的文件以反映您的建议。编译并运行后,我现在从 Controller 类中得到一个问题。当我尝试返回上述部分视图结果时,代码失败。当我深入研究时,文章存储库中出现了问题: public Article GetByID(int id) { return context.Article.FirstOrDefault(a => a.ID == id);我收到一条错误消息,“Domain.Entities.Article”类没有无参数构造函数。我觉得我很接近了,但并不完全在那里......!
  • 你使用的是什么类型的依赖注入组件?看起来您的控制器没有注册?
  • 实际上听起来好像 DependencyResolver 没有正确注册。您能否发布与 DI 组件相关的 Startup 代码?
  • 嗨@JDBennett,我有一个 NinjectDependencyResolver 类,并且我已将 CommentService 添加到 AddBindings 方法:“kernel.Bind().To();">。其他类正确解析/绑定,所以我假设它已正确解析。
  • 奇怪的是,当我使用以下代码添加无参数构造函数时,它起作用了: public Article() { IKernel ninjectKernel = new StandardKernel(); ninjectKernel.Bind().To(); ninjectKernel.Bind().To(); _commentService = ninjectKernel.Get(); } 。这就像我接近一个解决方案,但不完全在那里......!
猜你喜欢
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多