MVC 5 Scaffolder + EntityFramework+UnitOfWork Pattern 代碼生成工具
經過一個多星期的努力總算完成了單表,多表關聯(一對多,多對一)的增刪改查的代碼生成,基本上可以實現不用添加任何代碼完成對數據表的操作。
下個階段打算集成.net 4.5的認證和授權已經用戶注冊和角色管理
一旦有更好的方式我會把它集成到模板中實現自動生成。所以很希望得到大家的幫助。在這里我先拋磚引玉了。
asp.net MVC 5 Scaffolding多層架構代碼生成向導開源項目(邀請你的參與)
Demo和所有源代碼代碼在 https://github.com/neozhu/MVC5-Scaffolder
先看一下我的Demo實例
實體類的結構
Order 與 OrderDetail 一對多,OrderDetail 與 Product 多對一,Order與Product則是多對多的關系。
實現的操作界面如下圖
查詢界面
功能:
1/新增操作 : 頁面跳轉到新增頁面
2/刪除操作 : 選擇記錄或單擊行上的刪除 彈出確認框 是否刪除改記錄
3/編輯操作 : 單擊行上的編輯圖標頁面跳轉到編輯頁面
4/查詢操作 : 在Search 文本框中輸入關鍵字會根據后台每個字段的查詢,選擇需要顯示的字段
5/Table功能 :字段顯示勾選,分頁顯示,所有功能都是服務端實現
新增頁面
功能:
布局:上部表頭,下部的Tab為表體明細
一對多 :表體用table顯示
多對一 :用Dropdownlist進行選擇
表體明細的操作 :使用popup modal的方式操作
單擊保存后,系統會自動合並表頭表體一起提交到后台進行保存
編輯頁面
功能:
加載數據:會根據主從關系把表頭/表體的數據一次性加載
編輯數據:對表體明細的的操作類似新增頁面,但對表體進行刪除操作時會進行提示是否真的要刪除后台數據。
保存數據:一次性提交所有表頭/表體數據到后台進行操作。
上述這些功能完全可以代碼生成 不需要做任何修改
項目結構
View層的代碼
Index :查詢Table List
Create :新增頁面
Edit : 編輯頁面
EditForm :Partial View內嵌在Create 和Edit頁面中
_OrderDetailForm : pupup 子表維護表單頁面
Create,和Edit頁面通過Ajax Post 把數據提交到后台的Controller進行操作
代碼如下
<script type="text/javascript"> var orderid = 0; var ObjectState = "Added"; var $orderdetailstable = {}; $(document).ready(function () { $('form').submit(function () { var token = $('[name=__RequestVerificationToken]').val(); var serializedForm = $(this).serialize(); var actionurl = $(this).attr('action'); var orderdetails = $orderdetailstable.bootstrapTable('getData'); var newitem = { OrderDetails: orderdetails, Id: orderid, Customer: $('#Customer', 'form').val(), ShippingAddress: $('#ShippingAddress', 'form').val(), OrderDate: $('#OrderDate', 'form').val(), ObjectState: ObjectState }; $.ajax({ url: actionurl, type: "POST", dataType: "json", contentType: "application/json; charset=utf-8", data: JSON.stringify(newitem), success: function (result) { if (result.success) { self.location = "/Orders/Index"; } else { alert(result.err); } }, error: function (result) { console.log(result.statusText + result.responseText); alert(result.statusText + result.responseText); } }); return false; }); }); </script>
通過Jquery 獲取表頭和表體數據 序列化成Json對象然后Post到后台
Controller層代碼
這里就只貼Create方法的代碼
public class OrdersController : Controller { //private StoreContext db = new StoreContext(); private readonly IOrderService _orderService; private readonly IUnitOfWorkAsync _unitOfWork; public OrdersController(IOrderService orderService, IUnitOfWorkAsync unitOfWork) { _orderService = orderService; _unitOfWork = unitOfWork; } // GET: Orders/Index public ActionResult Index() { var orders = _orderService.Queryable().AsQueryable(); return View(orders); } // Get :Orders/PageList // For Index View Boostrap-Table load data [HttpGet] public ActionResult PageList(int offset = 0, int limit = 10, string search = "", string sort = "", string order = "") { int totalCount = 0; int pagenum = offset / limit + 1; var orders = _orderService.Query(new OrderQuery().WithAnySearch(search)).OrderBy(n => n.OrderBy(sort, order)).SelectPage(pagenum, limit, out totalCount); var rows = orders.Select(n => new { Id = n.Id, Customer = n.Customer, ShippingAddress = n.ShippingAddress, OrderDate = n.OrderDate }).ToList(); var pagelist = new { total = totalCount, rows = rows }; return Json(pagelist, JsonRequestBehavior.AllowGet); } // GET: Orders/Details/5 public ActionResult Details(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Order order = _orderService.Find(id); if (order == null) { return HttpNotFound(); } return View(order); } // GET: Orders/Create public ActionResult Create() { return View(); } // POST: Orders/Create // To protect from overposting attacks, please enable the specific properties you want to bind to, for more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] //[ValidateAntiForgeryToken] public ActionResult Create([Bind(Include = "OrderDetails,Id,Customer,ShippingAddress,OrderDate")] Order order) { if (ModelState.IsValid) { order.ObjectState = ObjectState.Added; foreach (var item in order.OrderDetails) { item.OrderId = order.Id; item.ObjectState = ObjectState.Added; } _orderService.InsertOrUpdateGraph(order); _unitOfWork.SaveChanges(); if (Request.IsAjaxRequest()) { return Json(new { success = true }, JsonRequestBehavior.AllowGet); } DisplaySuccessMessage("Has append a Order record"); return RedirectToAction("Index"); } if (Request.IsAjaxRequest()) { var modelStateErrors = String.Join("", this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors.Select(n => n.ErrorMessage))); return Json(new { success = false, err = modelStateErrors }, JsonRequestBehavior.AllowGet); } DisplayErrorMessage(); return View(order); } // GET: Orders/Edit/5 public ActionResult Edit(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Order order = _orderService.Find(id); if (order == null) { return HttpNotFound(); } return View(order); } // POST: Orders/Edit/5 // To protect from overposting attacks, please enable the specific properties you want to bind to, for more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] //[ValidateAntiForgeryToken] public ActionResult Edit([Bind(Include = "OrderDetails,Id,Customer,ShippingAddress,OrderDate")] Order order) { if (ModelState.IsValid) { order.ObjectState = ObjectState.Modified; foreach (var item in order.OrderDetails) { item.OrderId = order.Id; //set ObjectState with conditions if (item.Id <= 0) item.ObjectState = ObjectState.Added; else item.ObjectState = ObjectState.Modified; } _orderService.InsertOrUpdateGraph(order); _unitOfWork.SaveChanges(); if (Request.IsAjaxRequest()) { return Json(new { success = true }, JsonRequestBehavior.AllowGet); } DisplaySuccessMessage("Has update a Order record"); return RedirectToAction("Index"); } if (Request.IsAjaxRequest()) { var modelStateErrors = String.Join("", this.ModelState.Keys.SelectMany(key => this.ModelState[key].Errors.Select(n => n.ErrorMessage))); return Json(new { success = false, err = modelStateErrors }, JsonRequestBehavior.AllowGet); } DisplayErrorMessage(); return View(order); } // GET: Orders/Delete/5 public ActionResult Delete(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } Order order = _orderService.Find(id); if (order == null) { return HttpNotFound(); } return View(order); } // POST: Orders/Delete/5 [HttpPost, ActionName("Delete")] //[ValidateAntiForgeryToken] public ActionResult DeleteConfirmed(int id) { Order order = _orderService.Find(id); _orderService.Delete(order); _unitOfWork.SaveChanges(); if (Request.IsAjaxRequest()) { return Json(new { success = true }, JsonRequestBehavior.AllowGet); } DisplaySuccessMessage("Has delete a Order record"); return RedirectToAction("Index"); } // Get Detail Row By Id For Edit // Get : Orders/EditOrderDetail/:id [HttpGet] public ActionResult EditOrderDetail(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } var orderdetailRepository = _unitOfWork.Repository<OrderDetail>(); var orderdetail = orderdetailRepository.Find(id); var orderRepository = _unitOfWork.Repository<Order>(); var productRepository = _unitOfWork.Repository<Product>(); if (orderdetail == null) { ViewBag.OrderId = new SelectList(orderRepository.Queryable(), "Id", "Customer"); ViewBag.ProductId = new SelectList(productRepository.Queryable(), "Id", "Name"); //return HttpNotFound(); return PartialView("_OrderDetailEditForm", new OrderDetail()); } else { ViewBag.OrderId = new SelectList(orderRepository.Queryable(), "Id", "Customer", orderdetail.OrderId); ViewBag.ProductId = new SelectList(productRepository.Queryable(), "Id", "Name", orderdetail.ProductId); } return PartialView("_OrderDetailEditForm", orderdetail); } // Get Create Row By Id For Edit // Get : Orders/CreateOrderDetail [HttpGet] public ActionResult CreateOrderDetail() { var orderRepository = _unitOfWork.Repository<Order>(); ViewBag.OrderId = new SelectList(orderRepository.Queryable(), "Id", "Customer"); var productRepository = _unitOfWork.Repository<Product>(); ViewBag.ProductId = new SelectList(productRepository.Queryable(), "Id", "Name"); return PartialView("_OrderDetailEditForm"); } // Post Delete Detail Row By Id // Get : Orders/DeleteOrderDetail/:id [HttpPost, ActionName("DeleteOrderDetail")] public ActionResult DeleteOrderDetailConfirmed(int id) { var orderdetailRepository = _unitOfWork.Repository<OrderDetail>(); orderdetailRepository.Delete(id); _unitOfWork.SaveChanges(); if (Request.IsAjaxRequest()) { return Json(new { success = true }, JsonRequestBehavior.AllowGet); } DisplaySuccessMessage("Has delete a Order record"); return RedirectToAction("Index"); } // Get : Orders/GetOrderDetailsByOrderId/:id [HttpGet] public ActionResult GetOrderDetailsByOrderId(int id) { var orderdetails = _orderService.GetOrderDetailsByOrderId(id); if (Request.IsAjaxRequest()) { return Json(orderdetails.Select(n => new { OrderCustomer = n.Order.Customer, ProductName = n.Product.Name, Id = n.Id, ProductId = n.ProductId, Qty = n.Qty, Price = n.Price, Amount = n.Amount, OrderId = n.OrderId }), JsonRequestBehavior.AllowGet); } return View(orderdetails); } private void DisplaySuccessMessage(string msgText) { TempData["SuccessMessage"] = msgText; } private void DisplayErrorMessage() { TempData["ErrorMessage"] = "Save changes was unsuccessful."; } protected override void Dispose(bool disposing) { if (disposing) { //_unitOfWork.Dispose(); } base.Dispose(disposing); } }
Popup Modal編輯子表數據代碼
<script type="text/javascript"> var currentrowindex = -1; function deleteOrderDetail(id) { var url = '@Url.Action("DeleteOrderDetail")'; bootbox.dialog({ message: "Are you sure delete " + id, title: "Delete OrderDetail ", buttons: { Cancel: { label: "Cancel", className: "btn-default", callback: function () { } }, OK: { label: "OK", className: "btn-success", callback: function () { $.post(url, { id: id }, function (data) { $orderdetailstable.bootstrapTable('remove', { field: 'Id', values: [id] }); //console.log('remove' + row.Id); }); } } } }); } function orderdetailsrowindexFormatter(value, row, index) { return index + 1; } function orderdetailsactionFormatter(value, row, index) { return [ '<a class="edit" id="orderdetailsedit" href="javascript:void(0)" title="Add">', '<i class="glyphicon glyphicon-edit"></i>', '</a> ', ' <a class="remove" id="orderdetailsremove" href="javascript:void(0)" title="Remove">', '<i class="glyphicon glyphicon-remove"></i>', '</a>' ].join(''); } window.actionEvents = { 'click #orderdetailsedit': function (e, value, row, index) { currentrowindex = index; var url = "/Orders/EditOrderDetail" $.get(url + '/' + row.Id, function (data) { $('#orderdetailformModal-body').html(data); if (row.Id > 0) { //var id = $('#Id','#orderdetailformModal-body'); //id.val(row.Id); //var productid = $('#ProductId','#orderdetailformModal-body'); //productid.val(row.ProductId); //var qty = $('#Qty','#orderdetailformModal-body'); //qty.val(row.Qty); //var price = $('#Price','#orderdetailformModal-body'); //price.val(row.Price); //var amount = $('#Amount','#orderdetailformModal-body'); //amount.val(row.Amount); //var orderid = $('#OrderId','#orderdetailformModal-body'); //orderid.val(row.OrderId); } else { var id = $('#Id', '#orderdetailformModal-body'); id.val(row.Id); var productid = $('#ProductId', '#orderdetailformModal-body'); productid.val(row.ProductId); var qty = $('#Qty', '#orderdetailformModal-body'); qty.val(row.Qty); var price = $('#Price', '#orderdetailformModal-body'); price.val(row.Price); var amount = $('#Amount', '#orderdetailformModal-body'); amount.val(row.Amount); var orderid = $('#OrderId', '#orderdetailformModal-body'); orderid.val(row.OrderId); } $('#orderdetailformModal').modal('toggle'); }); }, 'click #orderdetailsremove': function (e, value, row, index) { if (row.Id > 0) { deleteOrderDetail(row.Id); } else { $orderdetailstable.bootstrapTable('remove', { field: '$index', values: [index] }); } } }; $(function () { $orderdetailstable = $('#orderdetails-table').bootstrapTable({ data: [] }); if (ObjectState == "Modified") { orderid = $('#Id').val(); var url = '/Orders/GetOrderDetailsByOrderId/' + orderid; $.get(url, function (data) { //console.log(data); $orderdetailstable.bootstrapTable('load', data) }) } $('#addorderdetailsbutton').on('click', function (e) { if ($("form").valid()) { currentrowindex = -1; var url = "/Orders/CreateOrderDetail" $.get(url, function (data) { //console.log(data); var index = -1; $('#orderdetailformModal-body').html(data); $('#Id', '#orderdetailformModal-body').val(0); $('#OrderId', '#orderdetailformModal-body').val(orderid); $('#orderdetailformModal').modal('toggle'); }); } e.preventDefault(); //Return false regardless of validation to stop form submitting //prior to ajax doing its thing return false; }) $('#orderdetailconfirmbutton').on('click', function (e) { $("form").removeData("validator"); $("form").removeData("unobtrusiveValidation"); $.validator.unobtrusive.parse("form"); if (!$('form').valid()) { e.preventDefault(); return false; } var ordercustomer = $('#OrderId :selected', '#orderdetailformModal-body').text(); var productname = $('#ProductId :selected', '#orderdetailformModal-body').text(); var id = $('#Id', '#orderdetailformModal-body').val(); var productid = $('#ProductId :selected', '#orderdetailformModal-body').val(); var qty = $('#Qty', '#orderdetailformModal-body').val(); var price = $('#Price', '#orderdetailformModal-body').val(); var amount = $('#Amount', '#orderdetailformModal-body').val(); var orderid = $('#OrderId :selected', '#orderdetailformModal-body').val(); var orderdetail = { OrderCustomer: ordercustomer, ProductName: productname, Id: id, ProductId: productid, Qty: qty, Price: price, Amount: amount, OrderId: orderid, ObjectState: 'Added' } if (currentrowindex == '-1') { $orderdetailstable.bootstrapTable('append', orderdetail); } else { $orderdetailstable.bootstrapTable('updateRow', { index: currentrowindex, row: orderdetail }); } $('#orderdetailformModal').modal('toggle'); }); }); </script>
所有代碼都是根據Entity 類型 和字段名進行生成,理論上針對業務系統都可以模板化,只要模式定下來什么代碼都可以生成。