# MVC程式開發筆記 <span class="fa fa-clock-o"/> 2017-10-3 [TOC] ## 基礎筆記 ### SQLExpress 家庭用 :::success (localdb)\MSSQLLocalDB ::: ### Database First, How To Connect MVC to Database 1. Models (rightClick) > 新增 ADO.NET 實體資料模型 2. 從資料庫產生 3. 新增連結 (hqssql****t) 4. 選取資料表 5. 完成 ### Unobtrusive jqueryval 1. 加入section scripts ```javascript= @section scripts{ @Scripts.Render("~/bundles/jqueryval") } ``` 2. 加入Html Helper, input 必須使用helper 否則必須自打data-val ```htmlmixed= @Html.ValidationMessageFor(model => model.FacetItem.FacetOrder) ``` 3. 一併顯示使用 ```htmlmixed= @Html.ValidationSummary() ``` ## 多對多關係,加入Checkbox處理呈現與修改 ### Controller 端 ```clike= // GET: /Employee/Edit/5 public ActionResult Edit(int? id) { if (id == null) { return new HttpStatusCodeResult(HttpStatusCode.BadRequest); } auth_employee emp = db.auth_employee .Include(i => i.auth_EmployeeGroup) .Where(i => i.Id == id) .Single(); GetAllRole(emp); if (emp == null) { return HttpNotFound(); } ViewBag.allCheckbox = db.auth_group.ToList(); return View(emp); } ``` 輔助程式,邏輯重點取得所有的角色類型構成ViewModel存入ViewBag ```clike= private void GetAllRole(auth_employee emp) { var auth_groupTable = db.auth_group; var empOwnRole = new HashSet<int>( emp.auth_EmployeeGroup.Select(c => c.groupId)); var viewModel = new List<groupViewModel>(); foreach (var row in auth_groupTable) { viewModel.Add(new groupViewModel { groupId = row.groupId, roleName = row.groupName, isOwn = empOwnRole.Contains(row.groupId)}); ViewBag.roles = viewModel; } } ``` 利用TryUpdateModel來更新模型,但尚未處理被塞入資料的問題。 ```clike= // POST: /Employee/Edit/5 [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(int? Id, string[] selectedRole) { var emp = db.auth_employee .Include(j => j.auth_EmployeeGroup) .Where(j => j.Id == Id) .Single(); if (TryUpdateModel(emp)) { UpdateEmployeeRoles(selectedRole, emp); db.Entry(emp).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } else { // error; } return View(emp); } ``` 輔助程式,讀入checkBox構成的字串陣列,迭代所有的角色如果符合字串陣列的加入emp的屬性。 ```clike= private void UpdateEmployeeRoles(string[] selectedRole, auth_employee emp) { if (selectedRole == null) { emp.auth_EmployeeGroup = new List<auth_EmployeeGroup>(); return; } // Edit 勾選的角色 var selectedRoleHS = new HashSet<string>(selectedRole); // 員工擁有的角色 var empRoles = new HashSet<int> (emp.auth_EmployeeGroup.Select(c => c.groupId)); emp.auth_EmployeeGroup = new List<auth_EmployeeGroup>(); // 迭代所有的角色 foreach (var role in db.auth_group) { if (selectedRoleHS.Contains(role.groupId.ToString())) { emp.auth_EmployeeGroup.Add( new auth_EmployeeGroup { groupId = role.groupId, employeeId = emp.Id }); } } } ``` ### View端 ```htmlmixed= <div class="form-group"> <label class="control-label col-md-2">具備角色</label> <div class="col-md-10"> @using sideMenu.Models; @{ List<sideMenu.ViewModel.groupViewModel> roles = ViewBag.roles; foreach (var role in roles) { <input type="checkbox" name="selectedRole" value="@role.groupId" @(Html.Raw(role.isOwn ? "checked=\"checked\"" : ""))> @role.groupId @: @role.roleName </input> <br/> } } </div> </div> ``` ## MVC 調用 Process / python script ```clike= using System.Diagnostics; public ActionResult Eng() { String Command = "python C:\\users\\631469\\create.py"; ProcessStartInfo ProcessInfo; // /C 關閉結束後關閉console /K 結束後保留console ProcessInfo = new ProcessStartInfo("cmd.exe", "/C " + Command); ProcessInfo.CreateNoWindow = true; ProcessInfo.UseShellExecute = true; var p = Process.Start(ProcessInfo); p.Close(); return View(); } ``` ## Model Partial Class Models Directory / classNamePartial.cs ```csharp= [MetadataType(typeof(anyClassName))] public partial class originalClassName { } public class anyClassName { [Required] [Display(Name = "partialName")] public string property { get; set; } } ``` edmx / classNamePartial.cs ```csharp= pulibc originalClassName { public string property { get; set; } } ``` ## 排序ParentId 與其 Child附加其下 ```clike= public ActionResult Index(int? projectId) { ViewBag.projectId = projectId; var cases = db.Case .Include(i => i.Department) .Include(i => i.Project); // 判別是否有輸入專案Id // TODO: 判斷是否擁有權限存取 if (projectId == null) { return RedirectToAction("Index", "Project"); } else { var parentCases = from c in db.Case where c.projectId == projectId && c.caseParentId == null select c; var sortCase = parentCases.ToList(); var Childcases = from c in db.Case where c.projectId == projectId && c.caseParentId != null select c; foreach (var item in Childcases) { var parentIndex = sortCase.FindIndex( ite => ite.caseId == item.caseParentId); sortCase.Insert(parentIndex + 1, item); } @ViewBag.projectName = db.Project.Find(projectId).projectName; return View(sortCase); } } ``` ## Razor ### MVC View Intellisense 錯誤修復 在View > Web.Config 新增此段 ```htmlmixed= <system.web> <compilation> <assemblies> <add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> </assemblies> </compilation> </system.web> <!-- Before end of configuration </configuration> --> ``` factorType 版本必須維持5.0 ```xml <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> ``` ### 如何擴充 Html Helper :::success 1. 新增資料夾 Extentions (非Convention) 2. 加入HtmlHelperExtetion.cs (非Convention) 3. 示範程式碼, Html.Submit 可以製造一顆input tag submit ::: ```clike= namespace System.Web.Mvc.Html { public static MvcHtmlString Submit(this HtmlHelper htmlHelper) { var tag = new TagBuilder("input"); tag.Attributes.Add("type", "submit"); tag.Attributes.Add("value", "送出"); var html = tag.ToString(); return MvcHtmlString.Create(html); } } ``` ### DropdownList with Class (form-control) ```htmlmixed= @Html.DropDownListFor(x => x.caseId, (IEnumerable<SelectListItem>)ViewBag.caseId, new { @class = "form-control"}) ``` ### EditorFor Complex Situation (Value, placeholder) ```htmlmixed= @Html.EditorFor(model => model.FacetItem.FacetOrder, new { htmlAttributes = new { @class = "form-control", @Value = value, placeholder = "構面順序" } } ) ``` ### EditorFor With Class (form-control) ```htmlmixed= @Html.EditorFor(model => model.Accumulated, new { htmlAttributes = new { @class = "form-control" } }) ``` ### 日期格式 Decorator, Format Attribute ```clike= [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] public System.DateTime projectDateEnd { get; set; } ``` ### Session ```clike= System.Web.HttpContext.Current.Session[""] ``` ### 唯讀 Texbox ```clike= @Html.TextBoxFor(model => model.CheckPointName, new { @class = "form-control", @readonly = true}) ``` ```clike= @Html.DropDownListFor(model => model.CheckPointName, new { @class = "form-control", @disabled = true}) ``` ### 檔案 Size Convert ```clike= @functions { static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; public string SizeSuffix(Int64 value, int decimalPlaces = 1) { if (value < 0) { return "-" + SizeSuffix(-value); } if (value == 0) { return "0.0 bytes"; } // mag is 0 for bytes, 1 for KB, 2, for MB, etc. int mag = (int)Math.Log(value, 1024); // 1L << (mag * 10) == 2 ^ (10 * mag) // [i.e. the number of bytes in the unit corresponding to mag] decimal adjustedSize = (decimal)value / (1L << (mag * 10)); // make adjustment when the value is large enough that // it would round up to 1000 or more if (Math.Round(adjustedSize, decimalPlaces) >= 1000) { mag += 1; adjustedSize /= 1024; } return string.Format("{0:n" + decimalPlaces + "} {1}", adjustedSize, SizeSuffixes[mag]); } } ``` ### 鑲嵌其他 Action Partial Result ```clike= # Controller public ActionResult Simple() { return PartialView(); } # View @Html.Action("Simple", "ControllerName") ``` ## Nuget 系列 ```shell= Install-Package jQuery -Version 3.1.1 Install-Package Microsoft.AspNet.Mvc -Version 5.2.3 Install-Package EntityFramework -Version 6.0.0 ``` ### PageList 示例 > Web.config ```xml= <system.web.webPages.razor> <pages pageBaseType="System.Web.Mvc.WebViewPage"> <namespaces> <add namespace="PagedList" /> <add namespace="PagedList.Mvc" /> </namespaces> </pages> </system.web.webPages.razor> ``` > Controller ```clike= public ActionResult Index(int? page) { var project = db.project; project = project.OrderBy(i => i.ProjectId); int pageSize = 2; // 每頁顯示筆數 int pageNumber = (page ?? 1); pageNumber = (pageNumber > (project.ToList().Count / pageSize + 1)) ? 1 : pageNumber; return View(project.ToPagedList(pageNumber, pageSize)); ``` > View 引入model方式 與 表頭顯示技巧 ```htmlmixed= @model PagedList.IPagedList<Models.project> <th> @Html.DisplayNameFor(model => model.FirstOrDefault().ProjectId) </th> ``` > 分頁顯示 ```htmlmixed= 第 @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) 頁 共 @Model.PageCount 頁 @Html.PagedListPager(Model, page => Url.Action("Index", new { page })) ``` ## Controller ### Model Binding 多重物件拋接 Object Class ```clike Class person{ string name {get; set;} int age {get; set;} } ``` Controller ```clike public ActionResult method(IList<person> per){ ... } ``` View ```htmlmixed= <input type="text" name="per[0].name" value=""/> <input type="text" name="per[0].age" value=""/> ``` :::info View name 迭代一定要從0開始 ::: ### TryUpdate ```clike= [HttpPost] [ValidateAntiForgeryToken] public ActionResult Edit(int Id, FormCollection FromValue) { Product product = db.Product .Where(p => p.Id.Equals(Id)) .FirstOrDefault(); if (TryUpdateModel( product, //要更新的對象 "", //ViewModel的前置詞 FromValue.AllKeys, //代入的更新資料會自動綁定 new string[] { "ModifyUid" })) //要排除綁定的對象 { db.SaveChanges(); return RedirectToAction("Index"); } return View(product); } ``` ### TryUpdate ViewModel https://dotblogs.com.tw/jasonyah/2013/05/11/103443 ### SelectList <專門> ViewBag 塞入 SelectList ```clike= ViewBag.UnitSelect = new SelectList(db.Wtp_Unit, "UnitId", "UnitName"); ``` View 顯示 SelectList ```htmlmixed= //DropDownList @Html.DropDownList ( "inputName", (IEnumerable<SelectListItem>)ViewBag.UnitSelect, "請選擇", new { @class = "form-control" } ) //DropDownListFor @Html.DropDownListFor ( x => x.InputName, (IEnumerable<SelectListItem>)ViewBag.EmpselectList, "請選擇", new { @class = "form-control" } ) ``` 自造 SelectList ```clike= var selectList = new List<SelectListItem> { }; foreach (var item in itemList) { selectList.Add ( new SelectListItem() { Text = item, Value = (int)item } ); } return selectList; ``` ### Enum 轉 selectList ```clike= var RoleList = Enum.GetValues(typeof(ROLE)) .Cast<ROLE>() .Select(v => new SelectListItem{ Text = v.ToString(), Value = ((int)v).ToString()}) .ToList(); ``` ### RouteDate 取得Controller 與 Action *View* ```clike= @ (ViewContext.RouteData.Values["action"]); @ (ViewContext.RouteData.Values["controller"]); ``` *Controller* ```clike= var currentAction = RouteData.Values["action"]; var currentController = RouteData.Values["controlller"]; ``` ## Ajax ### AjaxHelper ActionLink ```clike= @Ajax.ActionLink("顯示文字", "ActionName", new {routeValue = "value"}, new AjaxOptions { HttpMethod = "GET", UpdateTargetId = "divMessage", InsertionMode = InsertionMode.Repalce }) ``` ### AjaxHelper BeginForm ```clike= @using (Ajax.BegingForm("ActionName", new AjaxOptions{ HttpMethod = "GET", UpdateTargetId = "divMessage", InsertionMode = InsertionMode.Replace })) { <input type="text" name="routeValue" value="value" /> <input type="submit" /> } ``` ### jQuery Ajax #### 後端回傳Json 匿名類型串列示例 ```clike= // JSON Practice public ActionResult GetJson() { var empList = new[] { new { empName = "", empId = "" } }.ToList(); foreach (var emp in db.Wtp_Employee) { if (emp.Title == "副總經理") { empList.Add( new { empName = emp.EmployeeName, empId = emp.EmployeeId }); } } return Json(empList.Skip(1), JsonRequestBehavior.AllowGet); } ``` #### ajax Function ```clike= public ActionResult JsonData(string id, string name){ var data = new {empId = id, EmpName = name, Age = 25}; return Json(data, JsonRequestBehavior.AllowGet); } ~/home/jsondata/100?name=marry ``` ```clike= public ActionResult JsonData(string id, string name) { if (Request.IsAjaxRequest()){ var data = new {empId = id, EmpName = name, Age =25}; return Json(data, JsonRequestBehavior.AllowGet); } return View(); } ``` ```javascript= $(function(){ $("#button1").click(function(){ $.ajax({ type: "GET", url: "JsonData", data: "id=1&name=mary", success: function(data) { //data用於接住回傳值 console.log(data); $("#result").text(data.EmpName); } }); }); }); ``` ### Confirm Delete Alert 以下範例結合 SweetAlert2 與 Ajax ```javascript= $('#closeCase').click(function () { var caseId = $(this).attr("data-closeId"); swal({ title: '您確定要結案嗎?', text: " 確定結案後便無法再作意見編輯與檔案上傳", type: 'question', showCancelButton: true, confirmButtonColor: '#DD6B55', cancelButtonColor: '#aaa', confirmButtonText: '確認結案', cancelButtonText: '取消', allowEnterKey: false }).then(function () { $.ajax({ url: "@Url.Action("Close", "Do", null)", type: "POST", data: { id: caseId }, dataType: "Json", success: function (data) { swal("結案", "結案完成!", "success"); toDetach.remove(); }, error: function (xhr, ajaxOptions, thrownError) { swal("結案過程發生錯誤!", "請稍後再次執行", "error"); } }); // end of ajax }); // end of then }); // end of jQuery ``` [SweetAlert2 ==Refrence==](https://limonte.github.io/sweetalert2) ## JSON ### 存取 local json file ```csharp= using System.IO; using Newtonsoft.Json; public static List<AuthMember> readJson() { var r = new List<AuthMember>{}; using (var sr = new StreamReader( System.Web.HttpContext.Current.Server.MapPath("~/App_Data/member.json"))) { r = JsonConvert.DeserializeObject<List<AuthMember>>(sr.ReadToEnd()); } return r; } ```