2013年8月18日 星期日

效能大戰首部曲 - 快速開發篇 ASP.NET MVC 使用 PartialView 實作 Treeview

雖然主題是效能,但是本篇沒有任何跟效能有關的內容,只是介紹專案如何快速開發出原形(之後效能實驗用的專案)





建立方案 Massive

建立專案 Console 主控台應用程式 - 塞資料/測試 DB 用

建立專案 Repository 類別庫 - ORM DB Layer

建立專案 MvcApplication ASP.NET MVC 4 Web 應用程式(網際網路應用程式) - MVC 站台

建立專案 WebApplication ASP.NET Web Form Web 應用程式(網際網路應用程式) - WebForm 站台

然後去內臟...不,是把沒用的一些檔案砍了 因為這兩個 web 專案都很好心雞婆的附上會員機制





建立資料庫 TABLE 大致如下

於 Repository 建立 ADO.NET 實體資料模型





利用 MVC 的 scaffold template 快速產出 CRUD 原形 (Controllers & Views)

(如果跳出什麼連線字串的警告就手動把 Repository App.Config 裡面的連線字串複製到 web 專案的 Web.config)

然後將 Controllers 的 DB Layer 抽離至 Repository 供全部專案使用

這邊 Web Form 就比較麻煩些 沒有方便的 scaffold 產出頁面 (visual studio 2013 就有了,等不及的去抓 preview 吧 ~.~)

但是這難不倒 PG<F2E> 泛型設計師 就把 MVC 的 CHTML 模仿一下到 Web Form

顯示資料的 TABLE 改成 GridView 即可(頁面呈現的方式在後面幾部曲會詳細討論)

這裡先挑軟柿子 Department 整理

MvcApplication/Content/DepartmentController.cs

using Repository;
using System;
using System.Web.Mvc;

namespace MvcApplication.Controllers
{
    public class DepartmentController : Controller
    {
        private DepartmentRepository departmentRepository = new DepartmentRepository();

        #region GET

        //LIST
        public ActionResult Index()
        {
            return View(departmentRepository.GetAllDepartment());
        }

        //Detail
        public ActionResult Details(int id = 0)
        {
            return GetDepartment(id);
        }

        //Edit
        public ActionResult Edit(int id = 0)
        {
            return GetDepartment(id);
        }

        //Delete confirm
        public ActionResult Delete(int id = 0)
        {
            return GetDepartment(id);
        }

        private ActionResult GetDepartment(int id)
        {
            Department department = departmentRepository.GetDepartmentByID(id);
            if (department == null)
            {
                return HttpNotFound();
            }
            return View(department);
        }

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

        #endregion GET

        #region POST

        //Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Department department)
        {
            return Save(department, () =>
            {
                departmentRepository.AddDepartment(department);
            });
        }

        //Edit
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Department department)
        {
            return Save(department, () =>
            {
                departmentRepository.ModifiedDepartment(department);
            });
        }

        private ActionResult Save(Department department, Action action)
        {
            if (ModelState.IsValid)
            {
                action();
                return RedirectToAction("Index");
            }
            return View(department);
        }

        //Delete
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            departmentRepository.RemoveDepartment(id);
            return RedirectToAction("Index");
        }

        #endregion POST

        protected override void Dispose(bool disposing)
        {
            departmentRepository.Dispose();
            base.Dispose(disposing);
        }
    }

}

Repository/DepartmentRepository.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

namespace Repository
{
    public class DepartmentRepository : IDisposable
    {
        private MassiveEntities db = new MassiveEntities();

        public IEnumerable<Department> GetAllDepartment()
        {
            return db.Department.ToList();
        }

        public Department GetDepartmentByID(int id = 0)
        {
            return db.Department.Find(id);
        }

        public Department AddDepartment(Department department)
        {
            var result = db.Department.Add(department);
            db.SaveChanges();
            return result;
        }

        public Department ModifiedDepartment(Department department)
        {
            db.Entry(department).State = EntityState.Modified;
            db.SaveChanges();
            return department;
        }

        public Department RemoveDepartment(int id)
        {
            Department department = db.Department.Find(id);
            var result = db.Department.Remove(department);
            db.SaveChanges();
            return result;
        }

        public void Dispose()
        {
            db.Dispose();
        }
    }

}




但是即使整理完了 DepartmentController 雖然的確可以 CRUD 但是有點不敷使用啊

(WTF is it...)

這種階層式的資料 很自然會想弄成 treeview 的形式

做法當然千奇百怪都有 這邊我們以 PartialView 來實作





TreeViewNode 類用來實作 TreeView 結構的資料

DepartmentController 主要修改 Index 的部分 使用遞迴 填充 TreeViewNode

DepartmentRepository 主要修改 RemoveDepartment 刪除會連同子結點一併刪除

Node.cshtml PartialView(部分檢視) 用來實現 TreeViewNode 的 HTML

Index.cshtml 原本的 List 頁面 改用 Node.cshtml 來呈現

原本的 Create 頁 改為 Add 沒什麼特別原因 只是個人喜好

Edit 原本 ParentID 用 TextBox 輸入 改為 DropDownList 用挑的

Details 沒啥變 ParentID 改為顯示 ParentDepartmentName

Delete 同 Details

最後玩弄的效果

2013年7月28日 星期日

那些年 我們誤會很深的 Javascript 變數作用域

自從上次學會第一次惹惱隊友就上手 以 JSHint 為例之後

其中有話這麼說:請注意這貨十分殘暴 在惹惱隊友 會先惹惱自己

毫無疑外馬上就中槍





故事是這麼發生的

身為一個 C# 嫻熟的 PG 這段 Foo 是用來表示奇偶數的 很合理吧

正如我剛才所說 身為一個 C# 嫻熟的 PG 把剛剛的 Foo 用 JS 翻寫成這樣 也很合邏輯

說時遲那時快杰哥 JSHint 來了





花了發科 為啥這樣寫是錯的 從古至今一路走來 都這樣寫 難到小弟我錯了嗎?

stackoverflow教教你:http://stackoverflow.com/questions/810313/what-is-the-reason-behind-jslint-saying-there-are-too-many-var-statements





原來 Javascript 是沒有 block scope 的

也就是說 只要在同一個 function 裡面的變數

不管是在 if for while 還是蝦米小 的 block 之中

都能在外部存取變數

也就是說 以這個案例來看 即便離開 for 迴圈 下面仍然抓的到 result 甚至 loop 用的 i 變數

而 Javascript 變數的作用域 唯一的分水嶺 在於這個 var

沒有宣告 var 的變數 會是全域變數 全世界都抓的到他

有宣告 var 的變數 只有在該 function 內可以使用

但是 只要是在 function 內 是無視任何 block 的

所以 JSHint 認為 明明這些變數 並非具有 block scope 約束 但是卻以相似的方式撰寫 是一種誤導行為 所以他不允許





最後 依照 JSHint 拉比法典 的規則 必須寫成這樣 只有 一個 var 且最好在 function 最上面 避免他說 我們在宣告前就使用變數

但是老實說 即使是之前的寫法 程式真的會因此出錯嗎 或者在維護上 會讓人搞不清楚嗎 那也不見得

老話一句 工具幫助我們檢查細節 但是 是否要 100% 服從 我想就看誰是杰哥了

2013年7月24日 星期三

第一次惹惱隊友就上手 以 JSHint 為例

身為一個被全世界誤解最深的程式語言(但是最流行)

自由奔放 脫韁野馬 難以馴服 也是常有的

這樣也可以那樣也可以 有時候很方便 但是有時候是噩夢

尤其是 team work 的時候

即便有訂定 coding style

但是人類豈是能相信的動物

在連自己都無法相信的前提之下 我們需要靠工具來惹惱隊友





燈燈燈~JSHint!!!

只要把 CODE 貼上來他就會「嚴格」的幫你把關

讓你的 CODE 有足夠的資格蓋上 CAS 電宰豬





但是手動一一貼過來檢查未免搞剛

更別說人類不可相信呀!!

燈燈燈~JSLint.VS2012!!!

請注意這貨十分殘暴 在惹惱隊友 會先惹惱自己





工具/擴充功能和更新 可以取得這個整人玩具

琳瑯滿目的調教(?)設定

第二頁 記得選到 JSHint

開始調教(?)前記得把一些 Library 檔案都勾選 Skip on build 否則...很可怕不要問

調教(?)前

華麗的鞭打

調教(?)後

CAS 優良認證

2013年7月21日 星期日

AngularJS 玩弄手札 對 REST 用武器 - $resource

本故事純屬虛構,與一切真實人物、團體、故事無關。





摳頂人都知道 摳頂有許多原則

有時候人在摳頂身不由己 沒原則沒節操 又豈是少有之事

但是君子有所為有所不為 即便再沒原則沒節操 也不能忘記下面這個原則

KISS原則

師父常常跟我這麼說:

要把程式寫的複雜 很簡單 隨便TM怎麼寫 都很複雜 保證你的絕世武功絕不會被偷 包括未來的自己

但是要把程式寫的簡單 很困難 要怎樣寫 才能旁邊這些山頂洞人能一目了然 甚至讓未來的自己讚賞一下前人的偉大

Keep It Simple, Stupid

不怕神一般的需求 只怕豬一般的同事

這便是師父傳授我野球拳的最後一重





從此之後 我便把師父的諄諄教誨謹記在心 只要是 不夠 KISS 的 CODE 都是我的仇敵

而今天要被開刀的 就是這位在虎山被大蟲咬 然後結識又結識 Mr.Q 的朋友

Web API in Web Form 之 SPA 明知山有虎 偏向虎山行 上篇

AngularJS in Web Form 之 SPA 明知山有虎 偏向虎山行 下篇

AngularJS 玩弄手札 Mr.Q 非關 Jolin

這位朋友有什麼毛病呢?

他沒什麼毛病 程式能跑也能跳

但是 各位 不夠 KISS 呀

$q 先生這貨很神 果斷牛逼 山頂洞人們十分佩服 信奉其為神 天天三牲蔬果膜拜

那糟糕了 這群山頂洞人 完全不知道在衝三小 那豈不是我要 handle 所有 CRUD





那可不行 對付戰車有穿甲彈 對付這幫山頂洞人 我得找找 對 REST 用武器 好讓我奴役他們 handle 所有 CRUD

今日主角 燈燈燈 ~ 對 REST 用武器 - $resource

老歪文:Mapping an Angular Resource Service to a Web API





首先大斬 roleService.js 56 => 25 行

再來斬 rolesController 115 => 98 行

Web API 跟著改一些





飯粒檔