Friday, October 03, 2008
#
一開始還以為是我的幻覺,還不斷的回想,是不是己前就這麼慢,是我變的比較沒耐心,專案何時變的那麼龐大?啟動竟然要10幾秒?
中間做了些測試,譬sql 連線,一些啟動優化,檢測httpModules花費時間,甚至磁碟檢查重組,暫停google search,移除最近安裝的軟體,不過都沒有解決問題,當我認命開始說服自己,網站就是那麼慢時,但放到 iis 上時卻又正常了。
好啦,最後終於找到原因,我之前把 visual studio 2008查閱asp.net 的原始碼功能打開了,我在 Options->Dbugging->Symbols 的 Symbols Symbol file (.pdb) locations: 設定了 http://referencesource.microsoft.com/symbols,我猜或許是這網站掛了吧,先把這個功能暫時關掉就正常了。
Thursday, September 04, 2008
#
Json.Net是一套將物件序列成Json的字串、Json字串反序列回物件的程式庫
其實1.31版我還蠻習慣,在稍微修改後也一直用的好好的,激發我想跟上去的點,是今天試了3.0才發現,作者早就把不能序列Null、Value Type的封印解開了,再加上他有給SilverLight的版本(小加分),又看到 Rick Strahl 為 Json.Net 寫的 DataSet、DataTable、DataRow 的 Converter 要新版才能用(這可能會用到吧?)。
開始更新其實還蠻順利的,要克服的點都在預期內
- 序列日期: 原本1.31 是序列成 new Date(ticks),前端直接的js直接eval即可拿到Date object
但到了3.0是序列成 asp.net的json日期格式 "\/Date(ticks)\/”,如果想迎向新的格式,
相關參考 Truly's 微軟ASP.NET AJAX中日期類型的JSON處理
如果想將日期序列成原本格式,將JavaScriptDateTimeConverter序列時加入即可
JsonSerializer json = new JsonSerializer();
json.Converters.Add(new JavaScriptDateTimeConverter()); - 因為我原本的專案仍在 .net 2.0 下運作,但Json.Net 3.0版己經是用 VS2008 Net3.5下開發,讓.Net 2.0可以運行 .Net 3.5 的技巧可參考 CMHuang's 在 .NET 2.0 SP1 使用 LINQ (PLINQ) 及一些 Benchmark,主要是抽出 System.Core.Dll 給 .net 2.0 使用,另一個方式是引用 LINQBridge (把它想成 System.Core 的精簡版),我試過也是可行,重新編譯Json.Net時會有一個非必要的method仍有錯,把該method移除即可。
Tuesday, September 02, 2008
#
今天碰到一個小麻煩,XML序列化出來的資料,竟不能用 XmlDocument 讀取,出現錯誤訊息
Data at the root level is invalid. Line 1, position 1.
查了一下,序列化後資料最前面多了3 個byte [239,187,191],如果以字元來看是 65279,
也就是所謂的BOM(宣告文件是用utf8編碼)
這是原本的程式:
public string Serialize(object obj) {
//更改產生後的 xml namespace
XmlSerializerNamespaces xmlNameSpace = new XmlSerializerNamespaces();
xmlNameSpace.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
settings.CheckCharacters =false;
XmlSerializer serializer = new XmlSerializer(obj.GetType());
MemoryStream stream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(stream, settings)) {
serializer.Serialize(writer, obj, xmlNameSpace);
}
return Encoding.UTF8.GetString(stream.ToArray());
}
雖然知道原因,但卻不知道是怎麼產生該如何控制,於是打開了vs2008的 debug .net framework
的功能,一步一步追,查到是 settings.Encoding 的設定會影響,
然後再看到UTF8Encoding建構時有encoderShouldEmitUTF8Identifier參數可以設定
settings.Encoding = Encoding.UTF8;
改成
settings.Encoding = new UTF8Encoding(false);
搞定囉.
Wednesday, August 06, 2008
#
標題就快把狀況說完了,再稍微解釋一下
Url Rewrite 是讓你的網址,譬如原本的 http://localhost/post.aspx?id=303&page=7 重寫成 http://localhost/post/303/7.aspx ,據說這能讓搜尋引擎更方便的搜尋你的網站,或是拿來在特定的網頁間傳值(這是感受的問題? 我就是覺得一直帶出 ?id=333&page=7 有些不專業)。
那通常我是自己寫 HttpModule 來處理,用 context.RewritePath(newPath, false); 來重寫網址,當然這些不是本文重點,因為網路上詳細的文章多的是,假設網址改寫成功,第一個會碰到的難題是,雖然可以正確的 rewrite ,但在 postback 後,網址又會回到 ?id=303&page=7,這個的解法有2種,Rickel's blog 有詳細的程式碼,我們是選了繼承改寫 HtmlForm 的方式解,一開始也好好的,雖然會因此無法切換到 design mode, 但因為我也少用該模式就算了,只是呢 ~好景不長,幾天後才然發現,原本運作正常的驗證控制項都沒作用了:( ,花了一段時間的debug 才找到原因,如果page使用驗證控制項,他的 form 會產生如下的語法 <form name="aspnetForm" method="post" onsubmit="javascript:return WebForm_OnSubmit();" id="aspnetForm">,但是改寫的 HtmlForm 不會產生 onsubmit 的部份。
那我的解法如下
1 public class RewriteForm : System.Web.UI.HtmlControls.HtmlForm {
2 protected override void RenderAttributes(HtmlTextWriter writer) {
3 writer.WriteAttribute("name", this.Name);
4 base.Attributes.Remove("name");
5
6 writer.WriteAttribute("method", this.Method);
7 base.Attributes.Remove("method");
8
9 this.Attributes.Render(writer);
10
11 base.Attributes.Remove("action");
12
13 //重新把 javascript:return WebForm_OnSubmit(); 加回 onsubmit
14 //以修正使用 RewriteForm 驗證控制項的Client驗證功能失效
15 //因為 ClientOnSubmitEvent 是Page的internal 的屬性,所以才用反射來取值
16 string clientOnSubmitEvent =
17 (string)typeof(Page).GetMethod("get_ClientOnSubmitEvent",
18 BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic)
19 .Invoke(this.Page, null);
20 if (String.IsNullOrEmpty(clientOnSubmitEvent) == false) {
21 writer.WriteAttribute("onsubmit",
22 clientOnSubmitEvent);
23 }
24
25 if (base.ID != null)
26 writer.WriteAttribute("id", base.ClientID);
27 }
28 }
Thursday, July 17, 2008
#
工作中偶爾要寫控制項,因為偶爾,所以常常會忘了該怎麼開始,而他的那些Attributes讓我混亂,通常我的對應之道,是打開Reflector.Net,找一個類似的控制項看他的原始碼,通常會順利,但偶爾還是做了一些蠢事。
這是今天要做的控制項,上面DIV包DIV放標題與圖示,然後是內容,然後是DIV等的結束標籤,我希望控制項輸入的內容,呈現時直接套用該風格,我第一個直覺是想到LoginView,就是那個設定依登入狀況顯示不同畫面的,打開他的原始碼,一看原來他是用Template來做,這個我有點概念,於是開始拼拼湊湊出底下的程式碼,運作起來也是可行的,然後我就回家了。
使用方式
<blog:PageBlockControl ID="block" runat="server">
<Template>
<ul>
<li><a href="javascript:void(0)">balaba 1</a></li>
<li><a href="javascript:void(0)">balaba 2</a></li>
<li><a href="javascript:void(0)">balaba 3</a></li>
</ul>
</Template>
</blog:PageBlockControl>
PageBlockControl.cs
[ParseChildren(true), DefaultProperty("Template")]
public classPageBlockControl : Control, INamingContainer {
private ITemplate _template;
[Browsable(false), DefaultValue((string)null), PersistenceMode(PersistenceMode.InnerProperty), TemplateContainer(typeof(MyPageBlockControl))]
public ITemplate Template {
get {return _template;}
set { _template = value; }
}
protected override void CreateChildControls() {
LiteralControl header = new LiteralControl(String.Format(@"
<div class='module'>
<div class='module_top'>
<div class='module_head'>
{0}</div>
</div>
<div class='module_body'>
<div class='module_content'>", "My Title"));
LiteralControl footer = new LiteralControl(String.Format(@"
</div>
<div class='module_content_bottom'>
...more<img src='{0}' width='16' height='13' /></div>
</div>
<div class='module_bottom'>
</div>
</div>", this.ResolveClientUrl("~/image/more.gif")));
Control container = new Control();
Template.InstantiateIn(container);
this.Controls.Add(header);
this.Controls.Add(container);
this.Controls.Add(footer);
}
}
回到家後腦袋清醒些,再想想我的需求,應該只要模仿 Panel 就可以了吧? 沒事弄個Template出來做什麼呢,底下是我迷途知返後修改的第2個版本。
使用方式
<blog:PageBlockControl2 ID="block" runat="server">
<ul>
<li><a href="javascript:void(0)">balaba 1</a></li>
<li><a href="javascript:void(0)">balaba 2</a></li>
<li><a href="javascript:void(0)">balaba 3</a></li>
</ul>
</blog:PageBlockControl2>
PageBlockControl2.cs
[ParseChildren(false), PersistChildren(true)]
public class SpacePageBlockControl2 : Control {
protected override void Render(HtmlTextWriter writer) {
writer.Write(String.Format(@"
<div class='module'>
<div class='module_top'>
<div class='module_head'>
{0}</div>
</div>
<div class='module_body'>
<div class='module_content'>", "My Title"));
base.Render(writer);
writer.Write(String.Format(@"
</div>
<div class='module_content_bottom'>
...more<img src='{0}' width='16' height='13' /></div>
</div>
<div class='module_bottom'>
</div>
</div>", this.ResolveClientUrl("~/image/more.gif")));
}
}
延伸參考資料(或許是本篇最有價值的部份):
The ParseChildren and PersistChildren attributes - Justin Lovell's Blog
其實微軟自己的文件也很詳細
Tuesday, July 15, 2008
#
- Asp.Net :
HttpContext.Current.IsDebuggingEnabled
- DLL
public static bool IsDebugDLL(string AssemblyName) {
Assembly assembly = Assembly.LoadFrom(AssemblyName);
object[] attributes = assembly.GetCustomAttributes(typeof(DebuggableAttribute), false);
if (attributes.Length == 0) {
return false;
}
foreach (Attribute attr in attributes) {
if (attr is DebuggableAttribute) {
DebuggableAttribute d = attr as DebuggableAttribute;
return d.IsJITOptimizerDisabled;
}
}
return false;
}
Friday, May 30, 2008
#
我寫的簡單plugin。
- 目的: 是要延幾秒(譬如3秒)再執行function,如果在這3秒內重複觸發的話,那原本動作取消,會再等3秒後才執行。
- 使用時機:譬如要做 autocomplete,一般人可能連續輸入多個字元,如果每次keydown都到後端查符合的資料,那沒效能且沒必要,還不如等到使用者停下來再查,這時這個plugin 應該可以派上用場。
/*
* @name doLater
* @author unicorn liu
* @param delay_ms 1000 = 1 second
* @example $.doLater("job", 3000, function() {
* //do it
* });
*/
jQuery.doLater = function(name, delay_ms, func_job) {
if (window.doLaterTimerID == null) {
window.doLaterTimerID = {};
}
if (window.doLaterTimerID[name] != null) {
window.clearTimeout(window.doLaterTimerID[name]);
}
window.doLaterTimerID[name] = window.setTimeout(
function() {
window.doLaterTimerID[name] = null;
func_job.call();
}, delay_ms);
}
Tuesday, April 22, 2008
#
底下是2種Asp.Net底下,使用多國語的標準方式
- <asp:Literal ID="litMessage" runat="server" Text="<%$Resources: Welcome%>" />
這裏會顯示 local resource 裏的 Welcome 裏的字串
- <asp:Literal ID="litMessage2" runat="server" meta:resourcekey="litMessage2" />
這裏會顯示 local resource 裏的 litMessage2.Text 裏的字串
這種設計有什麼麻煩之處呢?
-
如果有一個地方要顯示的訊息 "歡迎到台灣",你又很堅持想分成3個字串"歡迎" "到" "台灣"來存放多國語資料 (假設你很堅持啦),那頁面就得這樣寫了
<asp:Literal ID="litMessage" runat="server" Text="<%$Resources: Welcome%>" /><asp:Literal ID="litMessage2" runat="server" Text="<%$Resources: To%>" /><asp:Literal ID="litMessage3" runat="server" Text="<%$Resources: Taiwan%>" />
是不是很麻煩呢? 如果可以這樣
<asp:Literal ID="litMessage" runat="server" Text="{$Welcome} {$To} {$Taiwan}" />
甚至是 <span>{$Welcome} {$To} {$Taiwan}</span>
- 如果是多國語在javascrpit 裏,一般我習慣把js寫在 head 裏面,大概是會是這樣
<head runat="server">
<script language="javascript">
window.onload = function() {
var msg = '<%=this.GetLocalResourceObject("Welcome") %>';
alert(msg);
}
</script>
</head>
這時的多國語得改用<%=this.GetLocalResourceObject("Welcome") %>,這樣不一致的用法,是不是容易讓人困惑? 如果可以這樣
<head runat="server">
<script language="javascript">
window.onload = function() {
var msg = '${Welcome}';
alert(msg);
}
</script>
</head>
底下提供一個可能方案,其實如果知道 ControlAdapter 是作什麼,大概也猜的出來接下來會做什麼,ControlAdapter 是在不改變原本程式碼的情況下,插入或是改寫控制項在Render行為。那針對Literal 就寫一個LiteralAdapter
namespace Adapter {
public class LiteralAdapter : ControlAdapter {
protected override void Render(HtmlTextWriter writer) {
Literal literal = (Literal)this.Control;
literal.Text = AdapterUtil.TranslateString(literal.Text);
base.Render(writer);
}
}
}
AdapterUtil.TranslateString() 作用是將 ${Welcome} 從資源檔撈出比對替換,那 <span>...span> 或 Header 裏的 script,這些html/js,在Asp.Net裏是放置於 LitertalControl 控制項,於是乎就再寫LiteralControlAdapter
namespace Adapter {
public class LiteralControlAdapter : ControlAdapter {
protected override void Render(HtmlTextWriter writer) {
LiteralControl literal = (LiteralControl)this.Control;
literal.Text = AdapterUtil.TranslateString(literal.Text);
base.Render(writer);
}
}
}
我揣測這樣的替換機制,應該會比原來作法效能差些,那要得到方便失去效能就看個人囉。另外我也沒在工作中實證,有興趣的話就下載回去玩玩。
Friday, April 04, 2008
#
Koogra 一套讀取Excel 的類別庫,雖然作者很久沒更新了,但它簡單好用讀取快速,是我讀取excel時最佳選擇,但今天碰到了一個問題,當 sheet's name 用中文命名時,用程式讀取sheet's name會變亂碼,還好他是開放原始碼,花了點時間看看,做了底下修改,至於為什麼我知道這樣改? 我是參考 MyXls 的原始碼,是另一套還在持續發展中有點bug但同時可寫/讀excel檔的類別庫。
將 \Excel\Records\BoundSheetRecord.cs 34~38行
ushort nameLen = reader.ReadUInt16();
StringBuilder nb = new StringBuilder(nameLen);
nb.Append(new string(reader.ReadChars(nameLen)));
_name = nb.ToString();
改成
ushort nameLen = (ushort)reader.ReadByte();
bool compressed = (reader.ReadByte() * 0x01) == 0;
if (!compressed) {
nameLen *= 2;
}
byte[] charBytes = reader.ReadBytes(nameLen);
if (compressed) {
//decompress
byte[] wideBytes = new byte[charBytes.Length * 2];
for (int i = 0; i < charBytes.Length; i++)
wideBytes[2 * i] = charBytes[i];
charBytes = wideBytes;
}
_name = new string(Encoding.Unicode.GetChars(charBytes));
Thursday, April 03, 2008
#
SilverLight:
HtmlPage.Window.Invoke("unicorn", 33, new DateTime(1976, 3, 11));
會觸發底下 window 的 sayUserInfo 事件,並且傳入3個參數 name, age, birthday
JavaScript:
<script type="text/javascript">
window.sayUserInfo = function(name,age,birthday) {
alert(name);
alert(age);
alert("民國" + new String(birthday.getFullYear()-1911) + "年");
};
</script>
如果 Invoke 事件時,要傳入物件當參數,那物件的宣告要貼上 ScriptableType 標籤
SilverLight:
HtmlPage.Window.Invoke("sayUserInfo2",
new UserInfo("unicorn", 33, new DateTime(1976, 3, 11)));
[ScriptableType]
public class UserInfo {
private string name;
private int age;
private DateTime? birthday;
public UserInfo(string name, int age, DateTime? birthday) {
this.name = name;
this.age = age;
this.birthday = birthday;
}
public string Name {
get { return name; }
set { name = value; }
}
public int Age {
get { return age; }
set { age = value; }
}
public DateTime? Birthday {
get { return birthday; }
set { birthday = value; }
}
}
JavaScript:
<script type="text/javascript">
window.sayUserInfo2 = function(user) {
alert(user.Name);
alert(user.Age);
alert("民國" + new String(user.Birthday.getFullYear()-1911) + "年");
};
</script>
Wednesday, April 02, 2008
#
用initParams param,如果參數只有一個,譬如value="file_types=rar"
如果多個則用逗號隔開,譬如 value="file_types=rar,file_size_limit=2048000"
Client端如下
<object id="Xaml1" data="data:application/x-silverlight," type="application/x-silverlight-2-b1" width="400" height="300">
<param name="source" value="ClientBin/SilverlightApplication1.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="onLoad" value="pluginLoaded" />
<param name="background" value="white" />
<param name="initParams" value="file_types=rar,file_size_limit=2048000" />
<a href="http://go.microsoft.com/fwlink/?LinkID=108182" style="text-decoration: none;">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none"/>
</a>
</object>
SilverLight - App.xaml.cs
private void Application_Startup(object sender, StartupEventArgs e) {
// Load the main control
this.RootVisual = new Page();
Option.FileTypes = ((Dictionary<string, string>)(e.InitParams))["file_types"];
}
Tuesday, April 01, 2008
#
SWF 長寬
http://www.codeproject.com/KB/graphics/ReaderSWFHeader.aspx
FVL 長寬
http://www.moitah.net/
後來看到更強的 MediaInfo,swf,flv,rmvb,wmv 通通都可以,真是太神了,不過因為是c++寫的dll ,所以會asp.net 要用會稍微麻煩一點,主要是asp.net web site project 會將程式編譯到 %SystemRoot%\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\ ,所以就算把MediaInfo.dll放到bin底下, [DllImport("MediaInfo.dll")] 時仍會找不到,那解決方式可能是
- 改成絕對路徑,譬如 [DllImport(@"c:\web\demosite\bin\MediaInfo.dll")]
- 在Global的Application_Start 時,複制MediaInfo.Dll到 AppDomain.CurrentDomain.DynamicDirectory
另外, 也有64位元版本dll 也有source喔
Friday, March 28, 2008
#
名字:FusionCharts Free v2
語言:Flash
網址:http://www.fusioncharts.com/free/
版權:商業可用
名字:Visifire
語言:SilverLight
網址:http://www.visifire.com/index.php
版權:GPL
名字:Open Flash Chart
語言:Flash
網址:http://teethgrinder.co.uk/open-flash-chart/
網址:免費(有源碼),作者還寫了為什麼要免費的心路歷程,因為他花錢買了別公司的Chart元件,發現了裏面有bug 跟軟體公司反應,但卻被拖了很久,問題一直沒有被妥善處,也沒有獲得應有的尊重,這讓他興起這自己動手做的動機,他的結論是"千萬不要惹毛客戶啊"
相關連結: Open Flash Chart Lib
Sunday, February 17, 2008
#
1.如果你序列出的文件,XML表頭是 utf-16 而不是 utf-8 ,請參考這篇
2.如果你不想要 prefix ns 的宣告,想讓
<?xml version="1.0" encoding="utf-8"?>
<DemoUser xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
</DemoUser>
變成
<?xml version="1.0" encoding="utf-8"?>
<DemoUser>
</DemoUser>
那底下的code會有幫助
XmlSerializerNamespaces xmlNameSpace = new XmlSerializerNamespaces();
xmlNameSpace.Add("","");
序列時把 xmlNameSpace 加入, serializer.Serialize(writer, serObj, xmlNameSpace)
3.避免循環參考,可以用 XmlIgnore 標籤,譬如分類裏有很產品,而產品裏又有分類資料,序列時就會沒完沒了,那如果要針對分類做序列化,因為分類會自動帶出產品資料,那產品裏的分類參考就該 XmlIgnore
4.如果序列的物件裏有用到 Set 或其它集合,丟出沒有預設存取子(default accessor)的Exception
所謂default accessor是指
public T this[int idx] {
get {
}
set {
}
}
那解法可以是繞過去,用可以序列化的porpery 來取代不能序列的,譬如底下,隱藏起不能序列的 Roles, 改用 RoleArray 來做取代。
[XmlIgnore]
public SortedSet<Role> Roles {
get { return roles; }
set { roles = value; }
}
[XmlArray(ElementName="Roles")]
public Role[] RoleArray {
get { return new List<Role>(roles).ToArray(); }
set { }
}
另一個解法,就是繼承再實作 default accessor,這得視情況而定
譬如先前我用 SortedSet 無法做XML序列,我就寫了SortedSetProxy 繼承 SortedSet 並實作 default accessor 。
public class SortedSetProxy<T> : SortedSet<T> {
private bool needUpdate = false;
private List<T> list = new List<T>();
public T this[int idx] {
get {
if (needUpdate) {
list = new List<T>(this.InternalDictionary.Keys);
needUpdate = false;
}
return list[idx];
}
}
public override bool Add(T o) {
needUpdate = true;
return base.Add(o);
}
public override void Clear() {
needUpdate = true;
base.Clear();
}
public override bool Remove(T o) {
needUpdate = true;
return base.Remove(o);
}
}
Thursday, February 14, 2008
#
功能: 主要是取得2個Collection 裏,相同、相異、聯集的部份
必需加入的dll: Iesi.Collections.dl
範例:
補充:
1. 如果順序是重要的,那 HashedSet 可以改成 SortedSet
2. 如果用SortedSet, 集合內的物件必需繼承IComparable介面
4.Iesi.Collection.dll 可在 NHibernate 專案裏找到, 裏面的版本加上對泛型的支援