C# Ninject DI/ASP.Net MVC-如何添加业务层?
我正在写一个愚蠢的程序,试图以一种实用的方式完全理解设计模式中涉及的各种概念。例如,我完全理解DI/IOC(我想),但我不完全理解如何在实际的ASP.NETMVC4/5环境中应用它 我正在编写一个商店程序,其中发票和产品是我仅有的两个表格。到目前为止,我已经成功地完全应用了DI/IOC,并完成了以下结构: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文件)生成实体,那么您可以自己维护实体。生成是帮助您入门的一次性工
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();
}
}
}