2011年11月29日 星期二

[asp.net] 永久登入,延長(自訂)表單驗證(Forms Authentication)保存時間

有些網站會提供記錄使用者登入密碼二週或永久
如pchome的 '記住我的密碼' 、 yahoo的 '保持我的登入狀態'

若使用Login控制項時,可以在Login控制項的LoggedIn事件裡面加入下面的程式碼:

// 使用Login控制項,在成功登入之後會自動幫我們建立驗證的cookie
// 所以我們必須拿出來重新修改,改成我們希望延長的時間
HttpCookie cookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];

// 從cookie解碼還原票證
FormsAuthenticationTicket oldTicket = FormsAuthentication.Decrypt(cookie.Value);

// 建立新的票證 註:DateTime.Now.AddYears(1)為票證到期日,也就是希望延長的期限
FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(oldTicket.Version,
oldTicket.Name, DateTime.Now, DateTime.Now.AddYears(1), oldTicket.IsPersistent, oldTicket.UserData, FormsAuthentication.FormsCookiePath);

// 使用新的票證來建立新的驗證cookie
string cookiestr = FormsAuthentication.Encrypt(newTicket);
HttpCookie newCookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookiestr);
newCookie.Expires = newTicket.Expiration;
newCookie.Path = FormsAuthentication.FormsCookiePath;

// 最後將新的驗證cookie重新寫回給user
Response.Cookies.Add(newCookie);

-----完成-----

2011年11月24日 星期四

submit特定元素的資料

有時候因為某些不可抗拒的因素,而只能將某些html元素寫在某個form當中

例子如下:

<form id="form1" method="post" action="post_target.html">
<input id="txtName" type="text" name="Name">
<input id="txtTel" type="text" name="Tel">
<input id="txtAddr" type="text" name="Address">
<input id="btnSend" type="submit" value="送出">
</form>
此時一按下送出,就會將整個form1的資料送出去

如果希望只送出Name欄位的資料出去該怎麼做呢?
以下為了程式碼的精簡,使用了jQuery來處理dom物件,改寫後的code如下
<form id="form1"> // 將 method="post" action="post_target.html"拿掉
<input id="txtName" type="text" name="Name">
<input id="txtTel" type="text" name="Tel">
<input id="txtAddr" type="text" name="Address">
<input id="btnSend" type="button" value="送出" onclick="doPost();" />
</form>

<script type="text/javascript">
function pay() {
var name= $('#txtName').val();
var form = $('<form method="post" action="post_target.html"></form>');
form.append('<input type="hidden" name="Name" value="' + name + '" />"');
$('body').append(form);
form.submit();
}
</script>

2011年11月23日 星期三

[C#] long(Int64) 與 base64 互轉

// long(Int64) 轉 base64
var buffer1 = BitConverter.GetBytes(long.MaxValue);
string str = Convert.ToBase64String(buffer1);

// base6轉long(Int64)
var buffer2 = Convert.FromBase64String(str);
long val = BitConverter.ToInt64(buffer2, 0);

2011年11月17日 星期四

LINQ動態查詢 使用(PredicateBuilder)

官網抵佳:PredicateBuilder
需使用套件: LINQKit
套件及範例下載位址:在LINQKit頁面的最下方

官網範例如下
IQueryable<SearchProducts>(params string[] keywords)
{
 var predicate = PredicateBuilder.False();
 foreach (string keyword in keywords)
 {
  string temp = keyword; // 很重要!! 一定要加
  predicate = predicate.Or (p => p.Description.Contains (temp));
 }
 return dataContext.Products.Where (predicate);
}

--------------------------------
下面是運作說明

步驟1:建立PredicateBuilder物件(我們需要使用此物建來作查詢條件的串接)
var predicate = PredicateBuilder.False();
步驟2:迴圈加入查詢條件
foreach(string keyword in keywords)
{
  string temp = keyword; // 避免參考到相同變數位址,所以需加上此行
  predicate = predicate.Or (p =< p.Description.Contains (temp));
}
步驟3:執行查詢
dataContext.Products.Where (predicate);
or 使用 dataContext.Products.Where(predicate.Compile());

-----------------完成!!------------------

接下來是運作原理的說明

當只執行
var predicate = PredicateBuilder.False();
dataContext.Products.Where (predicate.Compile());
這段描述看起來會像是 dataContext.Products.Where (src => false);

若是執行
var predicate = PredicateBuilder.False();
predicate = predicate.Or (src => src.keyword == "123");
dataContext.Products.Where (predicate.Compile());
這段描述看起來會像是 dataContext.Products.Where (src => false || src.keyword == "123");

若是執行
var predicate = PredicateBuilder.False();
predicate = predicate.Or (src => src.keyword == "123");
predicate = predicate.And(src => src.id == 5);
dataContext.Products.Where (predicate.Compile());
這段描述看起來會像是 dataContext.Products.Where (src => false || src.keyword == "123" && src.id == 5);

由此可知條件是串接起來的
因此預設選擇PredicateBuilder.False或PredicateBuilder.True就很重要了

舉個例子,如果希望找出keyword1或keyword2,兩個關鍵字只要一個成立即可的結果

範例會像這樣
var predicate = PredicateBuilder.False();
predicate = predicate.Or (src => src.keyword == "123");
predicate = predicate.Or (src => src.keyword == "456");
dataContext.Products.Where (predicate.Compile());
這段描述看起來會像是 dataContext.Products.Where (src => false || src.keyword == "123" ||src.keyword == "456"); //這是正確的結果
若預設選擇PredicateBuilder.True();
這段描述看起來會像是dataContext.Products.Where (src => true || src.keyword == "123" ||src.keyword == "456");
這樣一來查詢的結果永遠會是true,而導致錯誤了

在一個例子,如果希望找出keyword1且keyword2,兩個關鍵字都要成立的結果
範例應該像這樣
var predicate = PredicateBuilder.True();
predicate = predicate.And (src => src.keyword == "123");
predicate = predicate.And (src => src.keyword == "456");
dataContext.Products.Where (predicate.Compile());
這段描述看起來會像是 dataContext.Products.Where (src => true && src.keyword == "123" && src.keyword == "456"); //這是正確的結果
若預設選擇PredicateBuilder.False();
這段描述看起來會像是 dataContext.Products.Where (src => false && src.keyword == "123" && src.keyword == "456");
這樣一來查詢的結果就永遠會是false,而導致錯誤了







2011年10月6日 星期四

WebControl Text屬性 讓Html Tag失效

當使用Asp.net WebControl的Text屬性時

遇到如 Label.Text = "<span style=\"color:red\">HelloWorld</span>";
若我們希望他能夠完整顯示 <span style=\"color:red\">HelloWorld</span>字串
而不是顯示 HelloWorld

則需使用 Server.HtmlEncode 方法

修正後如下
Label.Text = Server.HtmlEncode( "<span style=\"color:red\">HelloWorld</span>");

2011年9月28日 星期三

WebSite 與 WebApplication 偵錯時的路徑問題

當使用WebSite專案時,按下偵錯(F5)後,產生的網址會像是這樣
http://localhost:7788/WebSite1/index.aspx

當使用WebApplication專案時,按下偵錯(F5)後,產生的網址會像是這樣
http://localhost:7788/index.aspx


假設在根目錄下有一的名為MyJS.js的javascript文件需要引用
我們在index.aspx裡面下入以下程式碼
<script type="text/javascript" src="/MyJS.js"></script>

當使用WebApplication專案時,一切都沒有問題
若使用WebSite專案時,就會發生錯誤

原因應該很明顯了 ' / ' 代表網站的根目錄
因為WebSite專案自動幫我們加了一個WebSite1的虛擬路徑
而導致錯誤的發生



解決方法:
1. 打開專案的屬性視窗
2. 將'虛擬路徑'屬性 由原先的 ' /WebSite1 '改成' / '
3. 完成

2011年9月22日 星期四

當網站部屬到IIS後 使用jQuery ajax呼叫webService(.asmx)失敗

在本機開發的時候可以正常呼叫,一但部署到IIS上面就發成錯誤
檢查error status為404錯誤

解決方式:
在web.config加入


<configuration>
  <system.web>
    <webServices>
       <protocols>
          <add name="HttpPost" />
       </protocols>
    </webServices>
  </system.web>
</configuration>

2011年9月6日 星期二

呼叫FacebookWebContext.Current 時跳出 !string.IsNullOrEmpty(settings.AppId) 例外

到web.config的<configuration>區段裡面加入以下程式碼:
<configSections>
<section type="Facebook.FacebookConfigurationSection, Facebook" name="facebookSettings"/>
<section name="canvasSettings" type="Facebook.Web.CanvasConfigurationSettings, Facebook.Web" />
</configSections>
<facebookSettings appId="你的appId" appSecret="你的appSecret" />
<canvasSettings
canvasPageUrl="http://apps.facebook.com/smartbuttonthree/"
canvasUrl="https://preview.smartbutton.com/dev/"
authorizeCancelUrl="http://www.facebook.com"/>

注意:<configSections>區段必須放在<configuration>的下面,也就是只能為<configuration>的第一個子區段

2011年8月26日 星期五

使用 Entity Framework 查詢出來的是舊資料?

使用entity framework也有幾個月的時間了
因為一開始是在web上開發,後來到win form上面才發現到這個問題
一個簡單的程式如下:


pubilc class Demo
{
private DemoEntities _db = new DemoEntities();

public void Query()
{
var enumProducts = from product in _db.Products
select product;
}
}


當第一次執行Query()時,查詢出來的資料都是正確的
之後手動到資料庫去變更Products資料表中的資料後
當執行第二次Query()後,發現查詢出來的還是之前的資料

原來每個ObjectSet都會自己維護查詢後的資料,也就是會將查詢後的資料放在物件中
在ObjectSet中有一個稱為MergeOption的屬性,是用來處理新查詢結果和物件中資料的合併方式

有四種狀態如下:
AppendOnly(預設):有查到新的資料列就新增到物件中,若資料表有變更過的資料列,並不會更新到物件中
OverwriteChanges:不管資料表中的資料列是新增或是修改過,都覆蓋原先物件中的資料。
PreserveChanges不管資料表中的資料列是新增或是修改過,都覆蓋原先物件中的資料,但原先物件中的某筆資料的狀態如果為modified則不會被覆蓋。
NoTracking:不維護物件中的資料。

以上為不精確的解釋,詳情請參考

最後補充一下為什麼用web開發時沒遇到這個情形
因為每要求一次頁面就會重新建立DemoEntities所以維護的資料都重新來過,就不會發生這種問題

所以除了AppendOnly狀態以外自己看情況選擇其他的狀態吧

對了對了 ObjectSet還有一個Method叫Refresh() 可以動態決定什麼時候要更新,有興趣在研究吧~

2011年7月28日 星期四

圖片被AdBlock隱藏了 =.= 換個方式顯示吧

在設計網頁的時候 因為瀏覽器裝了 adblock 套件
當路徑含有ad or ads 等廣告字樣 圖片就顯示不出來了

後來發現當html tag 為 <img>的時候會有這個問題
引此將圖片改為用 <input type="image" style="cursor:default" onclick="return false;"> 顯示就可以了

style="cursor:default" //因為用input type=image表是為圖示按鈕,所以由標會以手指顯示,加入這行讓游標變成箭頭

onclick="return false;" //因為用input type=image表是為圖示按鈕,為了防止點了圖片就做出submit動作,加入這行讓submit事件無效

2011年7月21日 星期四

一些在Windows Installer時,撰寫Registry會用到的保留字

最常用的應該就是
[TARGETDIR] //此保留字,表示程式安裝的路徑

其他... 嗯... 太多了自己看

http://msdn.microsoft.com/zh-tw/library/aa370905.aspx

2011年4月14日 星期四

使用chrome或firefox,無法在ashx中取得session

問題1:
當context.Session = null,是因為少繼承了IRequiresSessionState介面
於是需改成

using System.Web.SessionState;

public class Handler : IHttpHandler, IRequiresSessionState
{ ... }

問題2:
雖然context.Session != null了,但是Session.Count = 0,無法取得由aspx所設定的session值
於是需在 web.config 中加入

<configuration>
<system.web>
<sessionState cookieless="UseUri" />
</system.web>
</configuration>

OK! 可順利取到值了

2011年3月2日 星期三

找不到Sys命名空間

只要在aspx檔案中加入

<asp:ScriptManager runat="server" id="ScriptManager1" />

就會出現了... orz

2011年2月22日 星期二

GridView CommandArgument is Empty

當CommandArgument == ""的時候,可以使用下面的屬性來設定asp伺服器元件
(LinkButton , CommandButton等...)

CommandArgument='<%# Container.DataItemIndex%>'
(取出來的值是代表目前的row在整個gridview裡的index)

CommandArgument='<%# Container.DisplayIndex %>'
(取出來的值是代表目前的row顯示在gridview裡的index)

ex.
<asp:LinkButton ID="lbtnSend" runat="server" CausesValidation="False" CssClass="btnlink"
CommandName="Send" Text="發信" CommandArgument='<%# Container.DisplayIndex %>'></asp:LinkButton>

2011年2月8日 星期二

取得控制項在螢幕上的絕對位置

Point p = control.PointToScreen(new Point(0, 0));