繼上次的ASP.NET使用筆記又過了一年多
本次記錄了ASP.NET MVC與css/js/sql所遇到的問題
希望能幫到發生同樣問題的人
若內容有誤,歡迎網友指正

 

===========[ ASP.NET MVC ]===========

※ASP.NET MVC是什麼?

ASP.NET MVC是一種建構在ASP.NET上的類別庫
可以把ASP.NET使用MVC(model-view-controller)的設計模式實作網頁
優點是可以把畫面跟商業邏輯拆開,將來維護上更容易
此外MVC走回頭路使用當初ASP那樣直接用程式控制網頁的方式
可以更容易使用javascript跟ajax
由於網頁所有內容都是自己產生的
不會像過去使用ASP.NET的控制項後就無法通過Html無障礙標準
也沒有過去ViewState的包袱,讓網頁顯示的速度更加快速
缺點是進入門檻較高,MVC架構較複雜初期理解不易
設計模式無法直接預覽畫面,因此對js/css的技術都要很熟才行

 

※所以MVC是一種趨勢嗎?

現在越來越多網站開始使用MVC開發,書店也開始有MVC 4的書籍
能支援MVC的工具程式也越來越多(例如kendo UI)
甚至Visual Studio 2012已經內建了MVC 4
如果再搭配上EntityFramework,整個開發時間會更加快速且問題較少

 

※我應該把ASP.NET專案改寫成MVC嗎?

不要(一秒)
因為ASP.NET沒辦法直接轉到MVC架構,所以等於要重寫,不建議費這些功夫
未來要作新網站的時候再考慮MVC就好了

 

※MVC要怎麼把別的頁面內嵌進來?

使用Html.RenderAction或Html.Action,語法是

@{ Html.RenderAction("Name"); }
@Html.Action("Name")

如果這個動作只會被內嵌進來的話,最好加上[ChildActionOnly]避免被直連

 

※Html.RenderAction跟Html.Action的差別是?

前者是直接把內容放到Response,後者會回傳MvcHtmlString,你可以存成變數使用
效能上哪個比較好? 其實沒差多少不用鑽牛角尖
寫哪種比較好? 當然是短的那種比較好

 

※PartialView的內容可以使用ajax方式取回html嗎?

可以,不過要寫一個RenderPartialViewToString
 

protected string RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.RouteData.GetRequiredString("action");

    ViewData.Model = model;

    using (StringWriter sw = new StringWriter())
    {
        ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
        ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
        viewResult.View.Render(viewContext, sw);

        return sw.GetStringBuilder().ToString();
    }
}

使用方式

string renderView = this.RenderPartialViewToString("ViewName", model);

在client用ajax接到之後,直接用$("#element").html(response);即可

 

※如何讓Ajax.BeginForm動態被改成Html.BeginForm?

要這樣改的原因是Ajax.BeginForm如果要回傳檔案讓使用者下載的話可能會出錯
最簡單的做法就是把form的data-ajax屬性true改成false,這樣就不會有問題了

 

※MVC的helper為什麼只有@Html.Button沒有@Html.Submit?

因為submit除了產生html code外,沒有(也不需要)validation跟model binding等功能
所以微軟就不做了吧
真的需要可以自己手寫一個helper
不過我是覺得View上面的helper少一點,可讀性比較高

 

※MVC的helper有沒有@Html.CheckBoxList或@Html.RadioButtonList?

沒有,不過你可以自己用迴圈寫,或是在NuGet用別人寫好的方法

 

※Html.Action的route可不可以用string?

可以,用json反序列化
以下範例會連到"controller/action?Name=John"

@Html.Action("action", "controller", Newtonsoft.Json.JsonConvert.DeserializeObject("{\"Name\":\"John\"}");

 

※使用PagedList沒法用DisplayNameFor的解決法

PagedList是一個可以幫IEnumerable建立分頁的一個套件 (使用說明)
不過因為需要把model改成@model PagedList.PagedList<SomeModel>
所以DisplayNameFor會失效
如果需要使用的話,需要寫上FirstOrDefault()

@Html.DisplayNameFor(model => model.UserID)
改成
@Html.DisplayNameFor(model => model.FirstOrDefault().UserID)

 

※如何讓View根據是不是Ajax載入回傳PartialView或View

以下兩種判斷方式任選一種
cshtml判斷

@if (Request.IsAjaxRequest()) {
    Layout = null;
}

controller判斷

if (HttpContext.Request.IsAjaxRequest())
    return PartialView(model);
else
    return View(model);

可以考慮把這段寫成一個function
記得回傳型態要用ActionResult
個人建議是後者的寫法比較好,因為View最好不要寫邏輯判斷式
讓View單純顯示Model給它的資料就好

 

※Controller的return type預設是ActionResult
如果我今天要回傳PartialView,Return Type要用ActionResult或PartialViewResult都可以

那PartialViewResult到底有什麼意義?

就僅是型別檢查,告訴別人這個action絕對會回傳PartialView

 

※用Toi18n()簡單建置多國語言

VS 2008開始有擴充方法(Extension Methods)的功能,簡單來說就是可以把傳統的
NewMethod("value")改寫成"value".NewMethod()的形式,讓程式可讀性提高且撰寫方便
而且任何型態都可以用,也不限定只能傳一個參數,相當不錯
Toi18n可以參考這個網頁的語法教學

 

※我用了Toi18n的語法,可是文字竟然沒有顯示在畫面上
Ex. @Html.Label("UserNameResKey".Toi18n())

因為它如果對應到的Resource有包含"."
它產生的html code是<label for="value">value</label>
但因為for不能加上.,所以整段都不會產生
比較好的作法還是用DisplayNameFor,或是直接寫成
<label>@("UserNameResKey".Toi18n())</label>

 

※使用@Html.CheckBox(),為什麼MVC會自動幫我加上一個hidden field?

因為如果沒有這個hidden field,不勾選時它不會回傳這個欄位,會讓這個參數變成null
如果它又不允許null,就會出錯 (參考)

View



Controller

public ActionResult Test(bool rememberMe){
   // 沒hidden時,不勾選會出錯
}

 

※如何對時間型態指定格式?

有兩種方法

1. 設定DisplayFormat

[DisplayFormat(DataFormatString = "{0:yyyy/MM/dd HH:mm}", ApplyFormatInEditMode = true)]
public Nullable<System.DateTime> StartTime { get; set; }

這樣會在@Html.DisplayFor(model => model.StartTime)時顯示指定的格式
如果該格式也想在文字方塊(TextBox)有相同格式,請多設定ApplyFormatInEditMode = true
然後@Html.EditorFor(model => model.StartTime)

2. 在View指定格式

不要使用EditorFor,改成使用TextBoxFor
@Html.TextBoxFor(model => model.StartTime, "{0:yyyy/MM/dd HH:mm}"

 

※有沒有辦法對EditorFor增加style或其他參數?

MVC 4沒辦法,到MVC 5.1時才開始能對EditorFor增加參數
@Html.EditorFor(model => model, new { htmlAttributes = new { @class = "form-control" } })

 

※提高某個上傳頁面的上限

ASP.NET預設封包長度限制是4MB,代表上傳功能最多只能上傳4MB
要增加一般人會用修改maxRequestLength的方式
如在<system.web>中加入以下設定
<httpRuntime maxRequestLength="10240" executionTimeout="300"/>
可是這樣會造成所有頁面都會提高上限
有沒有辦法只讓特定頁面提高呢?可以的,利用location去限制

  <location path="Data/Upload">
    <system.web>
      <!-- 單位 kb (1024 = 1MB) -->
      <httpRuntime maxRequestLength="102400" />
    </system.web>
    <system.webServer>
      <security>
        <requestFiltering>
          <!-- 單位 byte (1024000 = 1MB) -->
          <requestLimits maxAllowedContentLength="102400000" />
        </requestFiltering>
      </security>
    </system.webServer>
  </location>

修改path/maxRequestLength/maxAllowedContentLength即可
如以上範例就是修改成可上傳100MB
有些人可能會問requestLimits是啥,這是IIS 7.0開始增加的額外限制
只改maxRequestLength,上傳大檔時可能就會出現「要求篩選模組設定為拒絕超過要求內容長度的要求」的錯誤訊息
因此也要修改相對應的requestLimits

 

※短時間快速存取DateTime.Now,其值可能重複

如題,如果這個時間戳記用在DB的PK欄位或是檔名等不允許重複的項目,就會造成錯誤
建議自行創建個時間戳記物件,請參考
https://stackoverflow.com/questions/5608980/how-to-ensure-a-timestamp-is-always-unique

 

※用了Ajax.BeginForm,在Server端回傳Json卻變成取代整個網頁

必須安裝Microsoft.jQuery.Unobtrusive.Ajax套件才能運作(NuGet可下載)
這個套件過去有在範本中,新範本卻拿掉了,不知為何

 

===========[ CSS ]===========

※css的字型有除以是什麼意思?
Ex. font: 15px/24px 'Helvetica Neue',sans-serif;

其實就是line-height的意思,而且是css1就有的寫法
只是大部分的人都會獨立出來寫成
font: 15px 'Helvetica Neue',sans-serif;
line-height: 24px;

 

※chrome把div拖曳的時候會有奇怪的殘影,要如何去掉?

這是chrome的固有bug (參考),目前的解法是在css加上
-webkit-backface-visibility: hidden;

 

※使用display: table-header-group;要在列印的時候重覆標題列
IE跟Firefox都正常,Chrome卻無效,為什麼?

因為chrome到現在還不願意去實作這個功能
解決法: 沒有!自己想辦法兜出這個功能吧

 

※如何在一個div內把裡面的元素水平垂直置中?

如果你想就直接用align: center跟vertical-align: middle,直接說垂直是行不通的
(1) 用absolute的方式置中

<div class="div">
    <img class="img" alt="" src="xxxxx" />
</div>

<style>
    .div {
         width: 100px;
         height: 100px;
         position: relative;
    }
    .img {
         max-width: 100px;
         max-height: 100px;
         position: absolute;
         top: 0;
         bottom: 0;
         left: 0;
         right: 0;
         margin: auto;
    }
</style>

(2) IE 9以後的瀏覽器支援了display: table-cell; 這種css屬性
table-cell會把元素當成儲存格一樣渲染,所以可以直接加上vertical-align: middle;

 

※用css指定元素有四種方式 (1)id (2)class name (3)tag name (4)style,如果某元素同時被這四種方式指定,請由優先權最高至最低排序

4>1>2>3
原理是最能代表該元素的會最優先套用
style寫在元素上,只可能影響該元素
id有可能重複(雖然對DOM來說不允許同名ID)
class name可以套用在多個元素上
tag name連設定class都不必,只要是同樣tag就會被影響,故優先權最低
另外這是在css只有一層的情形,如果是div > div這樣多層又會有不同結果

 

===========[ jQuery ]===========

※如何在html元素外包一個div?

比方說原本網頁內容是<label>Hello</label>
我想用js改成<div><label>Hello</label></div>要怎麼做呢?

我一開始還在想創造一個div後把原來的html剪下貼上
其實正確作法相當簡單,只要使用jquery.wrap就可以了
$("label").wrap("<div></div>");

 

※jquery validation可以針對動態產生的網頁增加驗證嗎?

$.validator.unobtrusive.parse('#element');

 

※兩個網頁元素中跑出一個space,無法緊鄰,要如何解決?

會有space是因為兩個元素中間有空白,最好的做法就是去掉空白
1. <div></div><div></div> 強迫元素相接
2. <div></div><!--
--><div></div>
3. <div></div
  ><div></div>

 

※js如果要指定用Model指定的物件,使用IdFor的方式會比較好

因為到時候如果Model的屬性需要重構(refactor)改名時
雖然View的內容還是不會跟著改,不過編輯的時候就會因為找不到屬性報錯
總比完全沒報的好

$("#UserName").val();   // 當屬性更名後,除非網頁出錯不然設計師不知道要改
$("#@Html.IdFor(m => m.UserName)").val();   // 編輯網頁時就會顯示錯誤了

 

※$(document).ready(function () { });可以直接簡寫成$(function() { });

 

※js可以刪掉asp.net建立的cookie嗎? 直接刪沒用耶

可以,但是要加上path

$.removeCookie("cookieName", { path: "/" });

 

※讓Ajax避免CSRF攻擊

CSRF也就是跨站請求偽造(Cross-site request forgery)
簡單來說就是登入後使用偽造封包傳送
比較簡單的預防方法是增加校驗token,驗證封包的確是在網站上傳送
ASP.NET MVC有內建的方法
在View宣告@Html.AntiForgeryToken()
在Controller加上[ValidateAntiForgeryToken]的標籤
這樣當他沒有驗證到token時就會丟出exception

以上方法是針對一般的表單
但Ajax Post沒有內含token,所以沒法用[ValidateAntiForgeryToken]
解法就是建立個假表單,並產生token
當Ajax要Post時就將token塞入

請將以下網頁存成Partial

<form id="__AjaxAntiForgeryForm" action="#" method="post">@Html.AntiForgeryToken()</form>
<script>
    AddAntiForgeryToken = function (data) {
        data.__RequestVerificationToken = $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val();
        return data;
    };
</script>

在Ajax時加上AddAntiForgeryToken這個方法

$.ajax({
    url: "@Url.Action("GetCity")",
    data: AddAntiForgeryToken({ country: "台灣" }),
    type: "POST",
});

 

※Ajax的參數不建議直接寫在url

因為若參數是中文可能會造成IE跟Chrome編碼錯誤(Firefox反而正確...)
比較正統的方式是把參數寫在data中
或是用encodeURI()將url編碼

// 不好的寫法
$.ajax({
    url: "@Url.Action("GetCity")?country=" + "台灣",
    type: "POST",
});

// 較好的寫法
$.ajax({
    url: "@Url.Action("GetCity")",
    data: { country: "台灣" },
    type: "POST",
});

 

※js要如何使用String.Format的寫法?

可以自己加上方法,以下取自網路
另外如果有使用Kendo UI,裡面有個kendo.format也是相同用法 

if (!String.Format) {
    String.Format = function (format) {
        var args = Array.prototype.slice.call(arguments, 1);
        return format.replace(/{(\d+)}/g, function (match, number) {
            return typeof args[number] != 'undefined'
              ? args[number]
              : match
            ;
        });
    };
}

 

用法就是var value = String.Format("Hello {0}", "World");

 

===========[ EntityFramework ]===========

※如何讓EntityFramework產生的資料表物件增加DisplayName等metadata?

如果想要直接去改class那是一定不可行的,因為每次更新都會被清掉
如果要自己創建一個類似名稱的class,每次把參數手動搬過去實在太過麻煩
因為寫回db時又要做同樣的事情

db.Account.Select(x => new MyAccount {
    Name = x.Name   // 不建議這樣的作法
});

比較建議的做法是使用metadata創建同名class,就可以在參數上增加metadata了
還可以做驗證

[MetadataType(typeof(AccountMetadata))]
public partial class Account
{
    public class AccountMetadata
    {
        [Display(Name = "姓名")]
        [StringLength(30)]
        public string Name { get; set; }
    }
}

View的model可以設定成Account,這樣controller得到的也會是Account物件
就可以直接將該物件新增到DB
Ex: db.Account.Add(model);

如果想在model增加不同於資料表物件的參數(例如帳號修改會有確認密碼等等)
可以直接寫在partial class的裡面,之後直接新增也不會有問題

[MetadataType(typeof(AccountMetadata))]
public partial class Account
{
    public class AccountMetadata
    {
        [Display(Name = "密碼")]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
    [Display(Name = "確認密碼")]
    [Compare("Password")]
    [DataType(DataType.Password)]
    public string ConfirmPassword { get; set; }
}

 

※EntityFramework有沒有辦法用預設方法讓Model在Server做驗證?

雖然Model可以用Data Annotation,但沒法適用所有驗證(例如某時間必須大於某時間)
因此我們可以對Model增加新的驗證方法
使用方法就是繼承IValidatableObject

[MetadataType(typeof(EventMetadata))]
public partial class Event : IValidatableObject
{
    public class EventMetadata
    {
        public DateTime StartTime { get; set; }
    	public DateTime EndTime { get; set; }
    }

    public IEnumerable Validate(ValidationContext validationContext)
    {
        if (StartTime > EndTime)
        {
            yield return new ValidationResult("結束時間不能早於開始時間", new string[] { "EndTime" });
        }
    }
}

 

※EntityFramework想用Database-First的方式,但是卻變成Code-First,要如何改回來?

1. 刪除所有動態產生的tt檔
2. edmx的程式碼產生策略改成"預設值"
3. 執行自訂工具
沒記錯的話VS2010預設是DB-First,但2012是Code-First
至於我為什麼要捨棄Code-First使用DB-First
是因為Code-First似乎沒法指定ConnectionString,也沒有ExecuteQuery

 

※在Linq使用if else的縮寫卻發現找出來的結果不正確?

假設我今天查詢有兩個條件,A條件不重要
B條件是:如果某變數為空值表示不篩選欄位,反之篩選欄位
就會寫成
where a && b ? true : c
實際上這句話EF會解讀成
where (a && b) ? true : c
而不是我所想的
where a && (b ? true : c)
所以...請適時的條件加上括號

其實這種寫法也不好,更好的寫法是利用IQueryable來增加篩選條件

var queryable = db.Accounts.AsQueryable();
if (!stinrg.IsNullOrEmpty(filterAccount)) {
    queryable = queryable.Where(x => x.Account == filterAccount)
}

也可以用網路上有人寫好的WhereIf擴展方法,可讀性更高

 

※EntityFramework要怎麼在新增一筆資料後,取得由資料庫自動編號的欄位?

其實你呼叫SaveChanges()後,該欄位就會出來了
以下假設有個table叫作User,包含ID跟Name,ID是自動產生的欄位

var entity = new User { Name = "John" };
dbcontext.User.Add(entity);
dbcontext.SaveChanges();
int id = entity.ID;

但這代表一定要先儲存後才能取回id,所以就沒辦法作交易(transaction)
如果你要用這個id建立其他資料而且整段都要交易的話
就要改用TransactionScope了

 

※EntityFramework怎麼做批次刪除?

EF 5沒辦法
EF 6可以使用db.Account.RemoveRange(removeEntities);

 

※EntityFramework取一筆資料有沒有更快的做法?

EF 6新增了Find方法,如果該筆資料有cache的情況會優先使用cache
而且多個PK也支援

return db.Account.SingleOrDefault(x => x.ID == id);   // 普通方法,無cache
return db.Account.Find(id);   // 新方法,有cache

 

※只改某筆資料欄位儲存的時候,出現了DbEntityValidationException?

因為DbContext會在儲存的時候先進行一次Server端Data Annotation驗證
如果出錯就不會跑到DB端去,減少讀取DB的次數
但如果我想要取消驗證該怎麼辦呢?
可以先把ValidateOnSaveEnabled設為false,存完後再打開

            try
            {
                base.Configuration.ValidateOnSaveEnabled = false;
                base.SaveChanges();
            }
            catch (DbEntityValidationException dbEx)
            {
                throw dbEx;
            }
            finally
            {
                base.Configuration.ValidateOnSaveEnabled = true;
            }

 

※出現DbEntityValidationException,卻沒有寫驗證失敗的欄位,要怎麼看?

必須使用try catch (DbEntityValidationException)才能看到其錯誤的資訊
如果你不想要每次SaveChanges()的時候都要寫一遍try catch
可以包成擴充方法(以下部分內容取自網路)

        public override int SaveChanges()
        {
            try
            {
                return base.SaveChanges();
            }
            catch (DbEntityValidationException ex)
            {
                // Retrieve the error messages as a list of strings.
                var errorMessages = ex.EntityValidationErrors
                        .SelectMany(x => x.ValidationErrors)
                        .Select(x => x.ErrorMessage);

                // Join the list to a single string.
                var fullErrorMessage = string.Join("; ", errorMessages);

                // Combine the original exception message with the new one.
                var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);

                // Throw a new DbEntityValidationException with the improved exception message.
                throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors);
            }
        }

 

※我想用字串來指定Linq的欄位,有沒有辦法?

可以用Dynamic Linq (用NuGet下載)
缺點是編譯時會失去強型別檢查

 

※Dynamic Linq要怎麼作Group?

使用前請先修改DynamicLibrary.cs

static readonly Type[] predefinedTypes = {
    // 前略
    typeof(SqlFunctions)   // 加上這行讓它可以解析SqlFunctions
}

以下假設有個Account資料表,針對其Birthday屬性作Group

dbContext.Account
.GroupBy("new (String.Concat(SqlFunctions.DateName(\"yyyy\", Birthday) , \"/\", SqlFunctions.StringConvert(SqlFunctions.DatePart(\"mm\", Birthday)).Trim()) as Birthday)", "it")
.Select("new (Key.Birthday)").OrderBy("new (Birthday)");

 

※Dynamic Linq對於SqlFunctions.StringConvert不支援int?,要如何解決?

修改DynamicLibrary.cs

static int CompareConversions(Type s, Type t1, Type t2)
    // 前略
    if (s == typeof(Int32?) && t1 == typeof(Decimal?)) return 1;    // 加上這行
    return 0;
}

 

※Dynamic Linq只要比對Guid就會出錯,要如何解決?
(訊息為 "Operator '=' incompatible with operand types 'Guid' and 'Guid'")

一樣修改DynamicLibrary.cs

interface IEqualitySignatures : IRelationalSignatures
{
    void F(bool x, bool y);
    void F(bool? x, bool? y);
    void F(Guid x, Guid y);    // 加上這行
    void F(Guid? x, Guid? y);  // 加上這行
}

 

※EntityFramework使用自定義function的轉換方法

EntityFramework雖然有SqlFunctions的函式可以提供基本轉換
但對於把string轉成decimal這種基本功能卻難以達成
(明明在SQL其實只要加一個CAST(field as decimal)就解決了...)
如果在SqlFunctions沒有看到自己需要的方法,可以自己撰寫

將edmx使用xml開啟,找到<edmx:ConceptualModels>底下的<Schema Namespace="xxx"這段
在內部直接加上
        <Function Name="CastDecimal" ReturnType="Edm.Decimal">
          <Parameter Name="stringvalue" Type="Edm.String" />
          <DefiningExpression>
            CAST(stringvalue as decimal(13,3))
          </DefiningExpression>
        </Function>

隨意一處加上這個靜態類別跟方法

    public static class MySqlFunctions
    {
        [EdmFunction("DBModel", "CastDecimal")]
        public static decimal CastDecimal(string stringvalue)
        {
            return Convert.ToDecimal(stringvalue);
        }
    }

最後在作轉換的時候就能直接用MySqlFunctions.CastDecimal(fieldName)

dbContext.Account.Select(x => new { ID = MySqlFunctions.CastDecimal(x.ID) });

如果要用在Dynamic Linq,請先修改DynamicLibrary.cs

static readonly Type[] predefinedTypes = {
    // 前略
    typeof(MyProject.MySqlFunctions)   // 加上這行
}

 

※有沒有辦法取得IQueryable轉成SQL的語法?

IQueryable queryable = GetQueryable();
string sql = ((ObjectQuery)queryable).ToTraceString();

 

※沒有定義型態的IQueryable有沒有辦法取出欄位?

可以使用IQueryable to DataTable的方法,之後以DataTable的方式處理
以下語法取自網路

public static DataTable ToDataTable(this IQueryable varlist)
{
	DataTable dtReturn = new DataTable();

	// column names 
	PropertyInfo[] oProps = null;

	if (varlist == null) return dtReturn;

	foreach (var rec in varlist)
	{
		// Use reflection to get property names, to create table, Only first time, others will follow 
		if (oProps == null)
		{
			oProps = ((Type)rec.GetType()).GetProperties();
			foreach (PropertyInfo pi in oProps)
			{
				Type colType = pi.PropertyType;

				if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
				{
					colType = colType.GetGenericArguments()[0];
				}

				dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
			}
		}

		DataRow dr = dtReturn.NewRow();

		foreach (PropertyInfo pi in oProps)
		{
			dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
			(rec, null);
		}

		dtReturn.Rows.Add(dr);
	}
	dtReturn.AcceptChanges();
	return dtReturn;
}

 

Entity Framework要怎麼對連線寫Log?

EF6開始增加了Log屬性,可以顯示所有Linq to SQL的字串跟執行時間

var db = new MyEntities();
db.Database.Log = (log) => System.Diagnostics.Debug.WriteLine(log);

 

※查詢時間範圍,千萬不可使用"23:59:59"這種查詢方式

常看到有人查詢會把時間後面加上23:59:59來查詢
例如查詢當日,有人會用
where date >= DateTime.Parse(today + " 0:0:0") && date <= DateTime.Parse(today + " 23:59:59")
實際上這是非常不好的寫法,因為時間並不是只到秒
如果資料時間為23:59:59.1就查不到了
當然這種情形很少見,但不是不會發生,最正確的寫法是
where date >= today && date < today.AddDays(1)
讓時間小於邊界即可
 

===========[ MsSQL ]===========

※如何動態建立資料庫的時候限制資料庫大小?

利用Alter database語法修改max_size

EXEC('CREATE DATABASE [' + @db_name + '];
ALTER DATABASE [' + @db_name + '] MODIFY FILE
( NAME = N''' + @db_name + ''', MAXSIZE = ' + @max_size + ');')    

 

※DB某欄位換行是\n,要怎麼都換成\r\n?

先將欄位\r\n都取代成\n,再取代回\r\n
不然若只把\n取代成\r\n,本來就是\r\n的資料就會出錯

UPDATE [MyDB].[dbo].[MyTable]
SET [MyTable].[MyColumn] = REPLACE(REPLACE([MyTable].[MyColumn], nchar(13) + nchar(10), nchar(10)), nchar(10), nchar(13) + nchar(10))

 

※如何直接砍掉某個資料庫

如果你的答案是直接執行
DROP DATABASE [DBName]
這段SQL的話,那很有可能會回傳這段話
Cannot drop database "DBName" because it is currently in use.
因此建議作法是先把資料庫轉成單人模式,由於轉換後自己會先佔用住
這時db砍掉就不會有人阻止了
USE [master]
ALTER DATABASE [DBName] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [DBName];

 

===========[ 疑難雜症 ]===========

※已經產生的SelectList有沒有辦法去除某個項目?

雖然SelectList其實是IEnumerable<SelectListItem>
但它產生後就不可變了...
只能用重新產生的方式

SelectList origin = GetOriginSelectList();
SelectList newList = new SelectList(origin.Where(x => x.Value != "1"));

 

※如何把一個Enumerable的物件轉成代表的數字?

enum Sex {
    Male = 1,
    Female = 2
}
// 答案是直接轉型就好
Sex sex = Sex.Male;
int value = (int)sex;

// 那要怎麼把數字直接轉成Enum?
// 也是直接轉型就好...
int value = 1;
Sex sex = (Sex)value;

 

※Web.config會存在記憶體還是會每次使用變數時才去讀取一次?

第一次以後都是使用cached,所以不用費心幫它優化了 (參考)
The web.config file is usually just read once and the info cached so I dont think it will have any performance issue.

 

※全空白的字串trim()完之後會變成empty還是null?

empty

 

※假設有個form,有個textbox,不填任何值就submit
1. 在Model對應的型態是string時,最後回傳時結果會是?
2. 在Model對應的型態是DateTime?時,最後回傳時結果會是?
3. 在Model對應的型態是DateTime時,最後回傳時結果會是?

1. empty
2. null
3. exception,null無法轉型成DateTime

推測的原因是瀏覽器在沒有輸入的情況下回傳都是empty
但是對於非string且nullable的type他就會轉null

 

※別用ASP.NET內建的Json序列化程式JavaScriptSerializer

因為那實在是太慢又有問題
改用Json.Net吧(如果是MVC4的範例專案一開始就會附了)

 

※value.HasValue跟value != null的差別?

bool? test = null;
if (test.HasValue && test.Value) { }   // type 1
if (test != null && test.Value) { }    // type 2

答案是沒有差別,因為compile幫我們做掉了 (參考)
哪個比較好?用HasValue可讀性高一點點

The compiler replaces null comparisons with a call to HasValue, so there is no real difference. Just do whichever is more readable/makes more sense to you and your colleagues.

 

※DataTable的DataColumn,除了Name、Caption外,有其他欄位可以記錄自定義資料嗎?

ExtendedProperty

 

※DataReader讀取資料的三種寫法,哪種比較快?
1. reader["field"].ToString()
2. (string)reader["field"]
3. reader.GetString(0)

速度上第二種較第一種快,但僅是幾微秒的差距
第一種當型態轉成int或別得不用特地改程式,第二種需要(不改會出錯)
第三種的缺點是它沒辦法用欄位名稱取回資料
所以可能會因為SQL或DB欄位順序改變導致取回的結果錯誤

 

※使用Visual Studio開啟專案一陣子後,編譯會出現VS2010建置時,發生"無法將檔案 "obj\Debug\ProjectName.dll" 複製到 "bin\Debug\ProjectName.dll"。由於另一個處理序正在使用檔案 'bin\Debug\ProjectName.dll',所以無法存取該檔案。",該如何解決?

Visual Studio 2010的固有bug,開前面版本專案就會發現這個錯誤
雖然網路上是說VS2010 SP1已經修好了這個bug,但我裝過還是會發生...
最後是改用VS 2012來編輯

You use the Visual Studio 2010 IDE to open a project that is in an earlier version. For example, you open a Visual Studio 2008 project. After a debug session, you cannot rebuild or debug the project again, and you receive an error message that resembles the following:
Unable to copy file "obj\Debug\assembly_name" to "bin\Debug\assembly_name". The process cannot access the file ‘bin\Debug\assembly_name" because it is being used by another process.

 

※超連結<a>使用onclick執行動作,其他瀏覽器正常但是IE 9會發生未預期的結果?

因為IE 9的動作跟其他瀏覽器都不同,試著在onclick後面加上return false;來解決

Link   
Link   

 

※可以讓客戶修改的彈性設定,要放在web.config好還是db好?

比較有可能變動的放DB
比較不可能變動的放web.config

 

※判斷字串不能為null或empty

不用寫麻煩的if (stringvalue != null && stringvalue != "")
.NET 2.0後可以直接用String.IsNullOrEmpty(stringvalue)
但如果還要限制比方說trim完後不可為empty
.NET 4.0新增了String.IsNullOrWhiteSpace(stringvalue)
建議用內建的作法,可讀性比較高

 

※底下code會出現"輸入字串格式不正確"的錯誤訊息,想想看為什麼?

String.Format(@"<script>
$(""#{0}"").click(function(){
   $(this).toggle();
});
</script>", "SomeOne");

因為大括號{}會被.NET視為特殊符號,如果要印出{或},請使用{{或}}

 

※為什麼有些人有問題都不願意求助?

定型心態
「當我們在考慮做或不做某件事情時,定型心態者是以「表現導向」做為背後的動機,而成長心態者則是以「學習導向」為出發點思考。如果去做這件事情,很有可能遭受失敗或是讓他看起來很蠢,定型心態者經常會避而遠之;而對於成長心態者來說,他們不會因為害怕失敗就不去做,因為他們重視的不是勝敗結果,而是知道自己能夠由每次的嘗試中更加進步並且更加熟練。」

 

※使用@Url.Action("action", "controller")時,卻產生action?Length=4這種網址,要怎麼解決?

這是因為同名異式造成呼叫function出錯

看一下MSDN就知道,雖然Url.Action的確有第一格回傳ActionName,第二格回傳ControllerName的組合(第四行)
Action(String, String)又同時符合Action(String, Object)的條件(因String也是一個Object)
所以實際上編譯器會使用Url.Action(actionName, route)的方法
原本當作是Controller的參數會以Route方法讀取
但因為無法解析,所以變成了Length=X的輸出結果(X為ControllerName的長度)

這個問題也同時發生在Controller的return View(string);
如果今天Model的型態是string,那我丟入一個string時
他應該當成model還是viewName?

所以這個問題有沒有解決方法呢? 答案是有的
1. 給予所有的參數
例如Url.Action,若無Route,可以傳入null給他,變成這樣
@Url.Action("action", "controller", null)
他就不會認錯了
但這種方法實在太複雜,且不能保證一定有傳入所有參數的方法

2. 指定參數名稱
參數前面是可以指定名稱的,只要改寫成
@Url.Action("action", controllerName: "controller")
他就一定會使用Url.Action(actionName, coltrollerName)的方法了
我是建議用第二種啦

 

※我下載了Log4net,但好像沒有作用?

必須先讀取設定檔,有兩種方法

1. 在Global.asax.cs中Application_Start()最後加上
log4net.Config.XmlConfigurator.Configure();
2. 在AssemblyInfo.cs最後加上
[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "config", Watch = true)]

建議是用第一種,第二種很多人不會注意

 

※SMTP寄信的設定值,不用寫在appSettings

大部份網站應該都會有寄信給使用者的功能
.NET本身就有SmtpClient這個類別可以寄信
比較差的工程師可能會直接把SMTP的位置跟Port寫死在程式中
好一點的會自己在appSettings加上相關設定
但其實只要直接宣告new SmtpClient(),他就會去抓組態檔的設定
不需要自己寫額外的code喔

在<configuration>內加上以下設定

   <system.net>
    <mailSettings>
      <smtp deliveryMethod="Network" from="&quot;顯示名稱&quot; &lt;電子信箱&gt;">
        <network host="郵件伺服器" port="25" defaultCredentials="false" userName="" password="" />
      </smtp>
    </mailSettings>
  </system.net>

將from/host/port改成自己的設定即可
其中因為我的郵件伺服器不需要認證,所以defaultCredentials="false"
如果要帳密認證,請自行填上username/password
接著直接用var smtp = new SmtpClient();就可以使用了

 

※C#如何拿到(1)當前日期,不包含時分秒 (2)當前時間,不包含日期

(1) DateTime.Today / DateTime.Now.Date
(2) DateTime.Now.TimeOfDay

 

arrow
arrow

    蕭雲 發表在 痞客邦 留言(1) 人氣()