2014年8月5日 星期二

Html.DropDownList 應用

1. 年齡選單

  • 預設值為空白(代表全部年紀)
  • 範圍 0 ~199
var ageList = new List<SelectListItem>();
ageList.Add(new SelectListItem { Text = "全部年紀", Value = "" });
ageList.AddRange(Enumerable.Range(0120).Select(x => new SelectListItem { Text = x.ToString(), Value = x.ToString() }));
ViewBag.age = new SelectList(ageList, "Value""Text", age);

頁面 :
@Html.DropDownList("age")

2. 英文字母選單

  • 預設值為空白(代表全部字母)
  • 範圍 A ~Z
var selectListAzItem = new List<SelectListItem>();
selectListAzItem.Add(new SelectListItem { Text = "字母", Value = "" });
selectListAzItem.AddRange(Enumerable.Range('A'26).Select(x => new SelectListItem { Text = ((char)x).ToString(), Value = ((char)x).ToString() }));
ViewBag.az = new SelectList(selectListAzItem, "Value""Text", az);

3. 自定內容

List<SelectListItem> ratingSorting = new List<SelectListItem>
{
    new SelectListItem{Text= "全部評分",Value= ""},
    new SelectListItem{Text= "沒有評分",Value= "0"},
    new SelectListItem{Text="有評分",Value= "1"}
};
ViewBag.rating = new SelectList(ratingSorting, "Value""Text", rating);

4. 從資料庫撈取資料

    客製化
  • 插入自定的資料到頂部當成預設資料
var locationList = _db.Location.OrderBy(x => x.Id).Select(x => new SelectListItem {
    Text = x.Name,
    Value = x.Id.ToString(),
}).ToList(); // 最後沒有 ToList() 則會得到型別為 IEnumerable<SelectListItem>, 只有 List 型別才能用 Insert 方法
locationList.Insert(0new SelectListItem { Text = "全部地區" });
ViewBag.location = new SelectList(locationList, "Value""Text", location);

    直接使用

  • 資料表欄位Id當Select的Vaule
  • 資料表欄位Name當Select當Text

ViewBag.location = new SelectList(_db.Location, "Id""Name");
     

5. 使用Enum

var reportTypeList = Enum.GetValues(typeof(ReportType)).Cast<ReportType>().Select(v => new SelectListItem
{
    Text = v.ToString(),
    Value = ((int)v).ToString()
}).ToList();
reportTypeList.Insert(0new SelectListItem { Text = "全部" });
ViewBag.reportType = new SelectList(reportTypeList, "Value""Text", reportType);

如果範例中的 enum ReportType 有 DiplayName, 並想要使用 DisplayName 做 Text 名稱
public enum ReportType
{
    [Display(Name = "訊息報錯")]
    Error,
    [Display(Name = "補充電影資料")] 
    Movie,
    [Display(Name = "回報問題")] 
    Problem,
    [Display(Name = "網站Bug修復")] 
    Bug,
    [Display(Name = "其他")]
    Other,
}

先做一個 function 來取她的 DiplayName
public string GetReportTypeDisplayName(ReportType value)
{
    var type = value.GetType();
    var members = type.GetMember(value.ToString());
    var member = members[0];
    var displayAttributes = member.GetCustomAttributes(typeof(DisplayAttribute), false);
    var displayAttribute = (DisplayAttribute)displayAttributes.FirstOrDefault();
    return displayAttribute == null ? value.ToString() : displayAttribute.GetName();
}

在 Select 出 SelectListem 時, 使用自定的方法
Text = GetReportTypeDisplayName(v),


=========================

頁面上抓取 selected 數值

string ageSelected = ((SelectList)ViewBag.age).Where(x => x.Selected).Select(x => x.Value).FirstOrDefault();






2014年8月3日 星期日

常用的 System.IO.Path 與範例

string FilePath = @"D:\test\test.rar";
Console.WriteLine("路徑 : {0}", FilePath);
// 變更副檔名
Console.WriteLine("變更副檔名 : {0}", System.IO.Path.ChangeExtension(FilePath, "dat")); // "D:\test\test.rar"
// 取得檔案路徑
Console.WriteLine("取得檔案路徑 : {0}", System.IO.Path.GetDirectoryName(FilePath)); // "D:\test"
// 取得副檔名
Console.WriteLine("取得副檔名 : {0}", System.IO.Path.GetExtension(FilePath)); // ".rar"
// 取得檔案名稱(包含副檔名)
Console.WriteLine("取得檔案名稱(包含副檔名) : {0}", System.IO.Path.GetFileName(FilePath)); // "test.rar"
// 取得檔案名稱不包含副檔名
Console.WriteLine("取得檔案名稱不包含副檔名 : {0}", System.IO.Path.GetFileNameWithoutExtension(FilePath)); // "test"
// 回傳最上層實體路徑
Console.WriteLine("回傳最上層實體路徑 : {0}", System.IO.Path.GetPathRoot(FilePath)); // "D:\"
// 建立隨機檔
Console.WriteLine("建立隨機檔 : {0}", System.IO.Path.GetRandomFileName()); // 例如 : "mvho5ulp.wrn"
// 建立暫存檔並回傳整路徑
Console.WriteLine("建立暫存檔並回傳整路徑 : {0}", System.IO.Path.GetTempFileName()); // 例如 : "C:\Users\ian\AppData\Local\Temp\tmp8DBA.tmp"
// 系統暫存檔路徑
Console.WriteLine("系統暫存檔路徑 : {0}", System.IO.Path.GetTempPath()); // "C:\Users\ian\AppData\Local\Temp\
// 是否包含副檔名
Console.WriteLine("是否包含副檔名 : {0}", System.IO.Path.HasExtension(FilePath)); // True
// 絕對路徑還是相對路徑
Console.WriteLine("絕對路徑還是相對路徑 : {0}",System.IO.Path.IsPathRooted(FilePath)); // True
// 取得完整路徑檔名
Console.WriteLine("取得完整路徑檔名 : {0}", System.IO.Path.GetFullPath(FilePath)); // "D:\test\test.rar"
// 將二個路徑合併
string FilePath1 = @"D:\";
string FilePath2 = @"test\test.rar";
Console.WriteLine("將二個路徑合併 : {0}", System.IO.Path.Combine(FilePath1, FilePath2)); // "D:\test\test.rar"

使用 Request 解析網址的說明與範例 :
http://blog.miniasp.com/post/2008/02/10/How-Do-I-Get-Paths-and-URL-fragments-from-the-HttpRequest-object.aspx

2014年8月1日 星期五

管理 ASPNET MVC 中的 Entity Framework DbContext 的生命週期

在應用程式中管理 DbContext 實例是非常重要的, 一個 DbContext 使用了資料庫連線(database connections)這樣重要的資源且需要被釋放(released), 如果沒有正確的 dispose 一個 DbContext 實例, 那麼相關的資料庫連線可能不會被釋放回連線池(connection pool).
寫老式的ADO.NET程式的人都知道一定要這麼做 !

在ASP.NET MVC應用程式中, DbContext  實例基本上是在 Controller 中使用, 一些基礎知識說明 MVC 的 controllers 在當請求(request)到達時被建立, 然後在請求已經完成時被 dispose (are disposed), 在 ASP.NET MVC中如何確認 DbContext   實例被 dispose (is disposed)?



方法1 : 使用 Using 區塊

using (EmployeeContext context = new EmployeeContext())
{
    return View(context.Employees.ToList());
}

使用 using 區塊可以確保, 當執行到區塊底部時, EmployeeContext 會被 dispose (is disposed), using 區塊是 try{...}finally{...} 的簡寫方式(語法糖), context (DbContext 實例)會在 finally 裡面被 dispose (is disposed), 這區塊會確保 context 被 dispose (is disposed), 但是這樣很難在應用程式中的不同地方分享同一個 context, 會發現自己一直在建立更多要使用的 DbContext 實例, 這也使自己的 Controller 裡出現邏輯處理之外的雜訊, 即使比使用 try finally 乾淨多了, 但是依然感覺一些雜訊存在



方法2 : Dispose 區塊

另一種方法是在 controller 中實作 dispose

 public class EmployeeController : Controller

{
    private EmployeeContext _context;
 
    public EmployeeController()
    {
        _context = new EmployeeContext();
    }
        
    public ActionResult Index()
    {
        return View(_context.Employees.ToList());
    }
        
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _context.Dispose();
        }
        base.Dispose(disposing);
    }
}

當請求完成時, controller 會被dispose (is disposed), 這方式也確保了 EmployeeContext 也被dispose (is disposed); 和 using 區塊很像, 在Controller裡產生了雜訊並且很難在應用程式中不同的地方分享同一個實例, 這方法並且依賴團隊中的所有開發人員要正確的實作 dispose 

方法3 : 依賴注入 (Dependency Injection)

public class EmployeeController : Controller
{
    private EmployeeContext _context;
 
    public EmployeeController(EmployeeContext context)
    {
        _context = context;
    }
        
    public ActionResult Index()
    {
        return View(context.Employees.ToList());
    }
}

這方法解除了 controller 對 DbContext 實例的生命週期的責任. controller 要求一個 DbContext 實例, 但是不需要關心這實例從哪裡來或是當她結束的時候會去那裡?
我們知道這個 Controller 中只有一個建構子, 所以建立 EmployeeController 必須傳入一個 EmpolyeeContext 實例, 所以 Controller 不用再負責建立 DbContext, 意思說也不再需要去 dispose !
但是如果 Controller 不用再去建立 context, 那誰該去建立? 我們如何確認 context 真的被 dispose (is being disposed)?
用 IoC 容器解決這問題!
nuget上有很多 injection/IoC 容器, 例如 NInject, 大致流程是註冊使用 NInject 的 OnPerRequestHttpModule 並設置要用來產生實例的 DbContext(例如範例中的 EmpolyeeContext), 經過這些設置後, NInject 將會認出你的 Controller 所要求的實例(例如 EmpolyeeContext 的實例), 然後執行以下流程:
1. 每次 Http Request 時建立實例
2. 傳送實例到 Controller 建構子
3. Http Request 結束時 dipose 實例
OnPerRequestHttpModule 的預設行為:每次 Http Request 都建立一個新的 EmpolyeeContext 實例(context), 這表示不同的 Request 無法使用同一個 context, 也保證不會產生兩個以上的 EmpolyeeContext 被建立, 即使最後請求經過了三個都要求同一個 EmpolyeeContext 的 Controllers, 換句話說, context 的生命週期和 request 的生命週期綁在一起