C# Ninject DI/ASP.Net MVC-如何添加业务层?

C# Ninject DI/ASP.Net MVC-如何添加业务层?,c#,asp.net-mvc,entity-framework,dependency-injection,ninject,C#,Asp.net Mvc,Entity Framework,Dependency Injection,Ninject,我正在写一个愚蠢的程序,试图以一种实用的方式完全理解设计模式中涉及的各种概念。例如,我完全理解DI/IOC(我想),但我不完全理解如何在实际的ASP.NETMVC4/5环境中应用它 我正在编写一个商店程序,其中发票和产品是我仅有的两个表格。到目前为止,我已经成功地完全应用了DI/IOC,并完成了以下结构: Store.Models添加列后,您没有提到EF是否自动重新生成了您的发票实体。假设您首先使用代码,而不是通过T4模板(.TT文件)生成实体,那么您可以自己维护实体。生成是帮助您入门的一次性工

我正在写一个愚蠢的程序,试图以一种实用的方式完全理解设计模式中涉及的各种概念。例如,我完全理解DI/IOC(我想),但我不完全理解如何在实际的ASP.NETMVC4/5环境中应用它

我正在编写一个商店程序,其中发票和产品是我仅有的两个表格。到目前为止,我已经成功地完全应用了DI/IOC,并完成了以下结构:


Store.Models添加列后,您没有提到EF是否自动重新生成了您的
发票
实体。假设您首先使用代码,而不是通过T4模板(.TT文件)生成实体,那么您可以自己维护实体。生成是帮助您入门的一次性工作,因此您不必从头开始编写所有实体

在这种情况下,您可以将
Age
字段直接添加到您的
Invoice
实体,并让您的业务服务在
calage
函数中获取
Invoice
实体实例,或者只向该函数传递一个日期时间并返回一个年龄。通常情况下,您希望使用视图模型,而不是使用EF实体来实现此目的,并且您可能会将出生日期存储在DB上,并计算年龄字段,无论是在DB上还是在属性getter的实体逻辑中(它将是[NotMapping],就像您已经拥有的那样)

您不希望将业务层中的类与实际的EF实体耦合,而是对实体执行操作,可以是对新创建的实体,也可以是通过存储库层从DB检索到的实体,就像现在一样

由于您希望使用业务层,因此可以执行以下操作:

namespace Store.Models
{
    using System;
    using System.Collections.Generic;

    public partial class Invoice
    {
         public Invoice()
         {
             this.Products = new HashSet<Product>();
         }

        public int Id { get; set; }
        public string Details { get; set; }
        public Nullable<decimal> Total { get; set; }

        [NotMapped]
        public int Age {get; set;
    // ...
您还必须为更新操作等执行此操作。;将
发票
作为视图模型返回的任何操作

视图的模型将绑定到发票实体:

@model IEnumerable<Store.Models.Invoice>

@{
    ViewBag.Title = "Index";
 }
 // ... and so on
@model IEnumerable
@{
ViewBag.Title=“Index”;
}
// ... 等等
您的Ninject容器将绑定服务,并且它将是控制器的依赖项。我个人希望将存储库作为依赖项注入到服务中,并将服务注入到控制器中,而不是将服务和存储库在控制器中分开,但我将使用您所拥有的

namespace Store.Web.Ninject
{
    public class NinjectControllerFactory : DefaultControllerFactory
    {
        private IKernel ninjectKernel;

        public NinjectControllerFactory()
        {
             ninjectKernel = new StandardKernel();
            AddBinding();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            //return base.GetControllerInstance(requestContext, controllerType);
            return controllerType == null
                ? null
                : (IController)ninjectKernel.Get(controllerType);
       }

        private void AddBinding()
        {
            //TODO FR: Step 4 - Add your interface and repository to the bindings
            ninjectKernel.Bind<IProduct>().To<ProductRepository>();
            ninjectKernel.Bind<IInvoice>().To<InvoiceRepository>();
            // Add this, assuming there isn't an interface for your service
            ninjectKernel.Bind<InvoiceBL>().ToSelf();            
        }
    }
}
namespace Store.Web.Ninject
{
公共类NinjectControllerFactory:DefaultControllerFactory
{
私有IKernel-Ninject内核;
公共控制器工厂()
{
ninjectKernel=新的标准内核();
AddBinding();
}
受保护的重写IController GetControllerInstance(RequestContext RequestContext,类型controllerType)
{
//返回base.GetControllerInstance(requestContext,controllerType);
返回控制器类型==null
无效的
:(IController)ninjectKernel.Get(controllerType);
}
私有void AddBinding()
{
//TODO FR:步骤4-将接口和存储库添加到绑定中
ninjectKernel.Bind().To();
ninjectKernel.Bind().To();
//假设您的服务没有接口,请添加此选项
ninjectKernel.Bind().ToSelf();
}
}
}

我没有看到任何关于
Discriminator
列的代码,但是如果它在实体中并且被映射,那么它需要在DB表中。映射可以通过上下文中使用的类(或直接在上下文中完成),也可以使用DataAnnotation属性进行设置,如[NotMapped]属性。

我将很快在GitHub上发布完整的解决方案,供任何想参与的人使用。我会在这里贴一个链接。我只想先把它恢复到一个工作版本,大约需要一个小时。请转到断开的分支获取上面的代码,因为主分支是我拥有的最后一个工作分支。谢谢你的帮助,欢迎在这里发表评论,并提供改进建议。我不会首先使用代码。至少不适用于发票和产品表。我不知道登录和注册是如何处理的。我只是将这些表物理地复制到我自己的数据库中,并更改了con字符串,结果成功了。所以我就这样离开了。我相信有更好的办法。我知道我可以在DB表中添加一个计算字段,所有这些都可以正常工作。但我的工作前提是,这并不适用于所有问题。因此,如果我想从一个数据集中计算一些我不需要阻塞数据库的数据,我该怎么办呢?更多的是要回答为模型创建一个单独的业务层的问题。我知道我所举的例子是人为的,但足以说明我的观点。我不确定我是否完全理解。您是说viewmodels是业务层(或者至少应该是)。这在一定程度上是有道理的。所以在这种情况下,他们应该从存储库中读取。不确定这是否是你所说的。关于依赖性的东西。我还在学习这个。所以我希望你能给我一些建议,比如说你说的话有点清楚。我不必使用业务层。但是,如果这将成为我的业务层,我想将viewmodels分开,并实际将其称为viewmodels,而不是使用另一种模式(MVVM)的viewmodels,尽管事实并非如此。实际上没有一个鉴别器列。这是错误信息的一部分,我不知道这个词是从哪里来的。我的代码中没有任何内容。我想我还是先用DB吧。但是我需要一些帮助,因为我没有完全意识到我正在分离控制器中与依赖性相关的内容。这很有趣。我想去阿尔特
namespace Store.Models
{
    using System;
    using System.Collections.Generic;

    public partial class Invoice
    {
        public Invoice()
        {
            this.Products = new HashSet<Product>();
        }

        public int Id { get; set; }
        public string Details { get; set; }
        public Nullable<decimal> Total { get; set; }
        public Nullable<System.DateTime> Date { get; set; }

        public virtual ICollection<Product> Products { get; set; }
    }
}
namespace Store.Interfaces
{
    public interface IInvoice
    {
        void CreateInvoice(InvoiceBL invoice);
        DbSet<InvoiceBL> Invoices { get; }
        void UpdateInvoice(InvoiceBL invoice);
        InvoiceBL DeleteInvoice(int invoiceId);
    }
}
namespace Store.Repositories
{
    public class InvoiceRepository : BaseRepository, IInvoice
    {
        public void CreateInvoice(InvoiceBL invoice)
        {
            ctx.Invoices.Add(invoice);
            ctx.SaveChanges();
        }

        public DbSet<InvoiceBL> Invoices
        {
            get { return ctx.Invoices; }
        }

        public void UpdateInvoice(InvoiceBL invoice)
        {
            ctx.Entry(invoice).State = EntityState.Modified;
            ctx.SaveChanges();    
        }

        public InvoiceBL DeleteInvoice(int invoiceId)
        {
            InvoiceBL invoice = ctx.Invoices.Find(invoiceId);

            if (invoice != null)
            {
                ctx.Invoices.Remove(invoice);
                ctx.SaveChanges();
            }

            return invoice;
        }
    }
}
namespace Store.Web.Controllers
{
    public class InvoiceController : Controller
    {
        //---------------------Initialize---------------------------
        private IInvoice _invoiceRepository;
        private IProduct _productRepository;

        public InvoiceController(IInvoice invoiceRepository, IProduct productRepository)
        {
            _invoiceRepository = invoiceRepository;
            _productRepository = productRepository;
        }

        //-----------------------Create-----------------------------

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Store.Business.InvoiceBL invoice)
        {
            if (ModelState.IsValid)
            {
                _invoiceRepository.CreateInvoice(invoice);
                return RedirectToAction("Index");
            }

            return View(invoice);
        }

        //-------------------------Read-----------------------------

        [ActionName("Index")]
        public ActionResult List()
        {
            return View(_invoiceRepository.Invoices);
        }

        public ViewResult Details(int id)
        {
            //How is this DI - If your model changes you have to alter the fields
            //addressed here.
            return View(_invoiceRepository.Invoices.FirstOrDefault(i => i.Id == id));
        }

        //-----------------------Update-----------------------------

        [ActionName("Edit")]
        public ActionResult Update(int id)
        {
            //How is this DI - If your model changes you have to alter the fields
            //addressed here.
            var invoice = _invoiceRepository.Invoices.FirstOrDefault(i => i.Id == id);

            if (invoice == null) return HttpNotFound();

            return View(invoice);
        }

        [HttpPost, ActionName("Edit")]
        [ValidateAntiForgeryToken]
        public ActionResult Update(Store.Business.InvoiceBL invoice)
        {
            if (ModelState.IsValid)
            {
                _invoiceRepository.UpdateInvoice(invoice);
                return RedirectToAction("Index");
            }
            else
            {
                return View(invoice);
            }
        }

        //-----------------------Delete-----------------------------

        public ActionResult Delete(int id = 0)
        {
            //Do you really want to always delete only the first one found?? Not cool?
            //Even though in this case, because Id is unique, it will always get the right one.
            //But what if you wanted to delete or update based on name which may not be unique.
            //The other method (Find(invoice) would be better. See products for more.
            //How is this DI - If your model changes you have to alter the fields
            //addressed here.
            var invoice = _invoiceRepository.Invoices.FirstOrDefault(i => i.Id == id);

            if (invoice == null) return HttpNotFound();

            return View(invoice);
        }

        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            if(_invoiceRepository.DeleteInvoice(id)!=null)
            {
                //Some code
            }
            return RedirectToAction("Index");
        }

        //-----------------------Master / Detail--------------------
    }
}
@model IEnumerable<Store.Business.InvoiceBL>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Age)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Details)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Total)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Date)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Age)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Details)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Total)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Date)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
    </tr>
}

</table>
namespace Store.Web.Ninject
{
    public class NinjectControllerFactory : DefaultControllerFactory
    {
        private IKernel ninjectKernel;

        public NinjectControllerFactory()
        {
            ninjectKernel = new StandardKernel();
            AddBinding();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            //return base.GetControllerInstance(requestContext, controllerType);
            return controllerType == null
                ? null
                : (IController)ninjectKernel.Get(controllerType);
        }

        private void AddBinding()
        {
            //TODO FR: Step 4 - Add your interface and repository to the bindings
            ninjectKernel.Bind<IProduct>().To<ProductRepository>(); ;
            ninjectKernel.Bind<IInvoice>().To<InvoiceRepository>(); ;
        }
    }
}
namespace Store.Models
{
    using System;
    using System.Collections.Generic;

    public partial class Invoice
    {
         public Invoice()
         {
             this.Products = new HashSet<Product>();
         }

        public int Id { get; set; }
        public string Details { get; set; }
        public Nullable<decimal> Total { get; set; }

        [NotMapped]
        public int Age {get; set;
    // ...
using Store.Models;

namespace Store.Business
{
    public class InvoiceBL
    {
        public int CalcAge(DateTime? date)
        {
            Age = 25;
            //TODO: Come back and enter proper logic to work out age
            // Something like:
            // return date != null ? <DateTime.Now.Year - date.Year etc.> : null
            return Age;
        }
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(Store.Model invoice)
    {
        if (ModelState.IsValid)
        {
            _invoiceRepository.CreateInvoice(invoice);
            // _service is your business service, injected as a dependency via the constructor, same as the _invoiceRepository is now
            invoice.Age = __service.CalcAge(invoice.BirthDate); // or some such thing
            return RedirectToAction("Index");
        }
        return View(invoice);
    }
@model IEnumerable<Store.Models.Invoice>

@{
    ViewBag.Title = "Index";
 }
 // ... and so on
namespace Store.Web.Ninject
{
    public class NinjectControllerFactory : DefaultControllerFactory
    {
        private IKernel ninjectKernel;

        public NinjectControllerFactory()
        {
             ninjectKernel = new StandardKernel();
            AddBinding();
        }

        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            //return base.GetControllerInstance(requestContext, controllerType);
            return controllerType == null
                ? null
                : (IController)ninjectKernel.Get(controllerType);
       }

        private void AddBinding()
        {
            //TODO FR: Step 4 - Add your interface and repository to the bindings
            ninjectKernel.Bind<IProduct>().To<ProductRepository>();
            ninjectKernel.Bind<IInvoice>().To<InvoiceRepository>();
            // Add this, assuming there isn't an interface for your service
            ninjectKernel.Bind<InvoiceBL>().ToSelf();            
        }
    }
}