# 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;
}
```