tag:blogger.com,1999:blog-51886426180960965912024-03-06T08:34:09.137+08:00程式設計筆記學習程式設計過程中總會碰到疑難雜症,而這些疑難雜症又不是一時能解決,不論是以往或是從未遇過的,都可將這些在程式設計上所碰到問題的解決方法筆記下來,或許解決了就算了,但等到下次有相同問題時,可將這程式設計筆記再拿來參考,就不枉做筆記了。David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.comBlogger332125tag:blogger.com,1999:blog-5188642618096096591.post-21832447300131858282015-05-07T10:36:00.000+08:002015-05-07T17:16:13.755+08:00Async/Await、Task產生執行緒之前一直認為,Async/Await跟Task一樣都會建出新的執行緒,這是錯誤的,Await是用主執行緒,遇到真正的Async方法(ex:HttpClient.GetAsync)或手動執行的Task.Run或Task.Factory.StartNew,這樣才會建立出新的執行緒
<br />
<br />
<pre class="code prettyprint">//主執行緒ID
Console.WriteLine(string.Format("Main:{0}", System.Threading.Thread.CurrentThread.ManagedThreadId));
//Task執行緒ID
Task t = Task.Run(() => { Console.WriteLine(string.Format("Task:{0}", System.Threading.Thread.CurrentThread.ManagedThreadId)); });
//使用Await呼叫方法
await GetAwaitThreadId();
//等候Task完成
t.Wait();
async Task GetAwaitThreadId()
{
//Await方法執行緒ID
Console.WriteLine(string.Format("Await:{0}", System.Threading.Thread.CurrentThread.ManagedThreadId));
}
</pre>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEeKApWJ0oe6ENjPtcRCVCB4bkINU9qiMUSiL6OsjH31tzQbTevQoIH1-Su0oF6cbp3VMU1iTLAuZy_es4StuvBrwirKn3mMquclesPFFxi-cwHKGRaSKC8pU_BKBVKjdOR7U9f0pQb20/s1600/TreadId.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEeKApWJ0oe6ENjPtcRCVCB4bkINU9qiMUSiL6OsjH31tzQbTevQoIH1-Su0oF6cbp3VMU1iTLAuZy_es4StuvBrwirKn3mMquclesPFFxi-cwHKGRaSKC8pU_BKBVKjdOR7U9f0pQb20/s320/TreadId.JPG" /></a></div>
<br />
<br />
另外,當呼叫非同步方法時,在前半部的程式碼(Await之前),依然是原本的執行緒在執行,遇到Await時,原本的執行緒會回到呼叫端
<br />
後半部的程式碼(Await之後),會暫時被保留,等到要等待的工作完成以後,會另外找一條執行緒出來執行後半部程式碼
<br />
<pre class="code prettyprint">//呼叫方法
Task t = PrintAwaitThreadId();
for (int i = 0; i < 10; i++)
{
//列出目前執行緒
Console.WriteLine(string.Format("Main[{1}]:{0}", System.Threading.Thread.CurrentThread.ManagedThreadId, i));
//產生新的執行緒做時間延遲
await Task.Delay(rnd.Next(1, 10));
}
await t;
async Task PrintAwaitThreadId()
{
for (int i = 0; i < 10; i++)
{
//列出目前執行緒
Console.WriteLine(string.Format("Await[{1}]:{0}", System.Threading.Thread.CurrentThread.ManagedThreadId, i));
//產生新的執行緒做時間延遲
await Task.Delay(rnd.Next(1, 10));
}
}
</pre>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiK7TMwJxKw9VqxYDVg_lfyXvlq6eZwYpKgodcejkCh8TMStuq3i8bhVDpK5MJ2rJDfa0l2vlhhW6H8WvnxmogXy0h6Jpyy28Mrzru1CjbdjGops7IO960PiOs86HcWKKsCKcrVr8EroSU/s1600/MultTreadId.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiK7TMwJxKw9VqxYDVg_lfyXvlq6eZwYpKgodcejkCh8TMStuq3i8bhVDpK5MJ2rJDfa0l2vlhhW6H8WvnxmogXy0h6Jpyy28Mrzru1CjbdjGops7IO960PiOs86HcWKKsCKcrVr8EroSU/s320/MultTreadId.JPG" /></a></div>
<br /><br />KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com4tag:blogger.com,1999:blog-5188642618096096591.post-76320630992445932792015-04-14T10:28:00.000+08:002015-04-14T10:30:14.704+08:00使用 Windows Azure Redis 快取有些資料需要大量運算,但是變動機會極低的時候,常會把運算結果存起來,下次直接抓出結果即可。<br />
<br />
如果有使用Windows Azure,這邊提供一個快取方式。<br />
<br />
參考:<a href="http://azure.microsoft.com/zh-tw/documentation/articles/cache-dotnet-how-to-use-azure-redis-cache/">如何使用 Azure Redis 快取</a><br />
<br />
首先建立<a href="http://azure.microsoft.com/zh-tw/documentation/articles/cache-dotnet-how-to-use-azure-redis-cache/#create-cache">Redis Cache Service</a><br />
<br />
安裝<a href="https://www.nuget.org/packages/StackExchange.Redis/">StackExchange.Redis</a>並加入參考<br />
<br />
先來看一下Code:<br />
<pre class="code prettyprint"> /// <summary>
/// Cache Redis 連線字串,來自Web.config > AppSettings > CacheRedisConnectionString
/// </summary>
private static string _CacheRedisConnectionString = "{名稱}.redis.cache.windows.net,ssl=true,password={密碼}";
/// <summary>
/// Cache Redis 預設保留時間
/// </summary>
private static int _CacheRedisTimeOut = 60;
/// <summary>
/// Cache Redis 連線(類似SQLConnection)
/// </summary>
private static ConnectionMultiplexer CacheRedisConnection;
/// <summary>
/// Cache Redis 存取(類似entity framework的DbContext)
/// </summary>
public static IDatabase CacheRedis;
/// <summary>
/// 設定快取初始連線
/// </summary>
public static void Initialize()
{
if(CacheRedisConnection == null)
{
CacheRedisConnection = ConnectionMultiplexer.Connect(_CacheRedisConnectionString);
if(CacheRedis == null)
{
CacheRedis = CacheRedisConnection.GetDatabase();
}
}
}
/// <summary>
/// 序列化物件
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static string Serialize(object value)
{
if (value == null)
return null;
return JsonConvert.SerializeObject(value);
}
/// <summary>
/// 字串反序列化為物件
/// </summary>
/// <typeparam name="T">回傳物件型別</typeparam>
/// <param name="value">字串資料</param>
/// <returns>物件</returns>
private static T Deserialize<T>(string value)
{
if (string.IsNullOrWhiteSpace(value)) return default(T);
return JsonConvert.DeserializeObject<T>(value);
}
/// <summary>
/// 取得指定型別快取物件
/// </summary>
/// <typeparam name="T">回傳物件型別</typeparam>
/// <param name="key">快取名稱</param>
/// <returns>回傳快取資料</returns>
public static T GetCache<T>(string key)
{
string value = GetCache(string key);
if(string.IsNullOrWhiteSpace(value) == true) return null;
return Deserialize<T>(GetCache(string key));
}
/// <summary>
/// 取得快取物件
/// </summary>
/// <param name="key">快取名稱</param>
/// <returns>回傳快取資料</returns>
public static string GetCache(string key)
{
//cache為null 或 key為空 或 Cache無資料,回傳null
if (CacheRedis == null || string.IsNullOrWhiteSpace(key) == true || CacheRedis.KeyExists(key) == false) return null;
return CacheRedis.StringGet(key);
}
/// <summary>
/// 設定快取物件,(選用)設定系統預設快取保留時間
/// </summary>
/// <param name="key">快取名稱</param>
/// <param name="value">要快取的資料</param>
/// <param name="defaultexpiry">是否設定預設快取保留時間</param>
public static void SetCache(string key, object value, bool defaultexpiry = true)
{
SetCache(key, value, 0, defaultexpiry);
}
/// <summary>
/// 設定快取物件,並設定快取保留時間
/// </summary>
/// <param name="key">快取名稱</param>
/// <param name="value">要快取的資料</param>
/// <param name="expiry">快取保留時間(分鐘)</param>
public static void SetCache(string key, object value, int expiry)
{
if (expiry <= 0) throw new Exception("CacheRedisHelpers.SetCache(string key, object value, int expiry) error : No Set Expiry");
SetCache(key, value, expiry, false);
}
/// <summary>
/// (private)設定快取物件
/// </summary>
/// <param name="key">快取名稱</param>
/// <param name="value">要快取的資料</param>
/// <param name="expiry">快取保留時間(分鐘)</param>
/// <param name="defaultexpiry">是否設定預設快取保留時間</param>
private static void SetCache(string key, object value, int expiry, bool defaultexpiry)
{
//CacheRedis為null 或 key為空,則不處理
if (CacheRedis != null && string.IsNullOrWhiteSpace(key) == false)
{
//value不為null,則設定快取,反之刪除
if (value != null)
{
//物件轉字串
string strvalue = value as string ?? Serialize(value);
//存入Cache
if (defaultexpiry) //使用預設快取保留時間
CacheRedis.StringSet(key, strvalue , TimeSpan.FromMinutes(_CacheRedisTimeOut));
else if (expiry > 0) //使用傳入快取保留時間
CacheRedis.StringSet(key, strvalue , TimeSpan.FromMinutes(expiry));
else //不設定快取保留時間
CacheRedis.StringSet(key, strvalue );
}
else CacheRedis.KeyDelete(key);
}
}
</pre>
<br />
在程式中,寫好建立連線的Method(Initialize),<br />
建立共用序列化與反序列化Method,<br />
取得的部分,如果取不到資料,Redis Cache也是回傳null,Method只是添加一些判斷減少跟Service連線,<br />
儲存的部分,建立2種較彈性的設定保留時間Method,一個是使用預先設定的預設快取保留時間,另一個是自行設定快取保留時間,<br />
完成了Redis Cache存取功能,接下來使用方式如下:
<br />
<pre class="code prettyprint"> //建立連線
Initialize();
//儲存資料(預設保留時間)
SetCache("key", objvalue, true);
//儲存資料(自訂保留時間)
SetCache("key", objvalue, iexpiry);
//取得資料(String)
GetCache("key");
//取得資料(指定Type)
GetCache<type>("key");
</type></pre>
<br />
另外,在CacheRedisConnection.GetDatabase(),可以指定不同的DB(int)儲存資料(預設為0),可以將資料區分讓開發者依不同情境區分資料存放位置。KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-54564459368048069912015-01-20T09:44:00.004+08:002015-01-20T09:54:06.140+08:00Entity Framework Code First依屬性(Attribute)設定SQL欄位 (續)花了點時間把<a href="http://readily-notes.blogspot.tw/2015/01/asp-net-mvc4-webapi-entity-framework_19.html">Entity Framework Code First依屬性(Attribute)設定SQL欄位</a>的程式碼Review過一次,總覺得設定的地方怪怪的,<span style="color: red;">GetMethods()</span>以後,指定第幾個到底是為了什麼?
<br />
<br />
<pre class="code prettyprint">if (propAttr.prop.PropertyType.IsGenericType && propAttr.prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[7];
}
else
{
methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[6];
}
decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration;
decimalConfig.HasPrecision((byte)propAttr.attr.Precision, (byte)propAttr.attr.Scale);
</pre>
<br />
從Debug模式下去看,原來Property有很多,需要指定正確的Type,不然就會發生轉換失敗的錯誤,而部分資料類別又有區分允不允許NULL,所以造就了上面<strike>詭異</strike>的CODE<br />
判斷有沒有允許NULL後,強制指定對應的資料型別,這個方法在網路上廣為流傳,但這實在是太暴力了,如果哪一天順序被改掉,會有一堆人死在那邊<br />
為了避免這種情況發生,決定嘗試去判斷對應型別有沒有允許NULL,無奈小弟功力不夠深厚,一直找不出解決方法<br />
<br />
運氣還不錯的找到了一組原始碼<a href="https://gist.github.com/HerrKater/6501214">HerrKater/EntityHelper.cs</a><br />
參考了裡面的<span style="color: red;">ProcessDecimalProperties</span>,在GetMethod取得MethodInfo時,後面傳入的Type[]使用<span style="color: red;">LambdaExpression.GetType()</span>去指定對應型別,這時候就可以找到我們正確的<span style="color: red;">DecimalPropertyConfiguration</span><br />
<pre class="code prettyprint">MethodInfo methodInfo = entityConfig.GetType().GetMethod("Property", new[] { lambdaExpression.GetType() });
DecimalPropertyConfiguration decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration;
decimalConfig.HasPrecision((byte)propAttr.attr.Precision, (byte)propAttr.attr.Scale);
</pre>
<br />
<br />
另外參考了他取得在DbContext內宣告DbSet<>的方法<br />
<pre class="code prettyprint">var contextProperties = typeof (T).GetProperties().Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof (DbSet<>));
</pre>
<br />
決定把原本用命名空間做判斷的方式改掉,但是他是用<span style="color: red;">PropertyInfo</span>,然後在迴圈裡面進行轉換,這樣會變更到原本我們的Code,所以做一些調整<br />
<pre class="code prettyprint">var tmplist = typeof(T).GetProperties().Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)).SelectMany(p => p.PropertyType.GetGenericArguments()).Where(t => t.GetProperties(BindingFlags.Public | BindingFlags.Instance).Any(p => p.GetCustomAttribute<DecimalAttribute>() != null));
</pre>
<br />
<span style="color: red;">備註:</span><br />
<span style="color: red;">取得PropertyInfo以後,要先取PropertyType找出宣告的Type,但是因為有外層的DbSet<>,所以還需要再使用GetGenericArguments()去找出定義的Model</span><br />
<br />
參考:<br />
<a href="http://readily-notes.blogspot.tw/2015/01/asp-net-mvc4-webapi-entity-framework.html">Entity Framework Code First建立Decimal,並設定大小</a><br />
<a href="http://readily-notes.blogspot.tw/2015/01/asp-net-mvc4-webapi-entity-framework_19.html">Entity Framework Code First依屬性(Attribute)設定SQL欄位</a><br />
<a href="https://gist.github.com/HerrKater/6501214">HerrKater/EntityHelper.cs</a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-53448769507606140812015-01-19T13:04:00.001+08:002015-01-20T09:49:26.028+08:00Entity Framework Code First依屬性(Attribute)設定SQL欄位繼上遍<a href="http://readily-notes.blogspot.tw/2015/01/asp-net-mvc4-webapi-entity-framework.html">Entity Framework Code First建立Decimal,並設定大小</a>,覺得要針對每個Model設定SQL欄位大小,又要設定驗證大小,日後維護要改2個地方,有點給他麻煩,甚至容易忘記<br />
<br />
嘗試對已有設定<span style="color: red;">DecimalAttribute</span>的Model在<span style="color: red;">OnModelCreating</span>的時候,把所有的都一次設定完<br />
<br />
首先建立一個靜態類別<span style="color: red;">DecimalModelCreating</span>,並建立設定方法<span style="color: red;">OnModelCreating</span>(如下)<br />
<br />
<br />
<pre class="code prettyprint">public static class DecimalModelCreating
{
public static void OnModelCreating(DbModelBuilder modelBuilder, string Namespace)
{
//取得所有在指定命名空間中,有欄位包含DecimalAttribute的類別
foreach (Type classType in from t in Assembly.GetAssembly(typeof(DecimalAttribute)).GetTypes() where t.IsClass == true && string.IsNullOrEmpty(t.Namespace) == false && t.Namespace.Equals(Namespace, StringComparison.CurrentCultureIgnoreCase) == true && t.GetProperties(BindingFlags.Public | BindingFlags.Instance).Any(p => p.GetCustomAttribute<DecimalAttribute>() != null) select t)
{
//取得類別中,包含DecimalAttribute的欄位
foreach (var propAttr in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute<DecimalAttribute>() != null).Select(p => new { prop = p, attr = p.GetCustomAttribute<DecimalAttribute>(true) }))
{
var entityConfig = modelBuilder.GetType().GetMethod("Entity").MakeGenericMethod(classType).Invoke(modelBuilder, null);
ParameterExpression param = ParameterExpression.Parameter(classType, "c");
Expression property = Expression.Property(param, propAttr.prop.Name);
LambdaExpression lambdaExpression = Expression.Lambda(property, true, new ParameterExpression[] { param });
DecimalPropertyConfiguration decimalConfig;
MethodInfo methodInfo;
//依欄位是否允許null,設定對應MethodInfo
if (propAttr.prop.PropertyType.IsGenericType && propAttr.prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[7];
}
else
{
methodInfo = entityConfig.GetType().GetMethods().Where(p => p.Name == "Property").ToList()[6];
}
decimalConfig = methodInfo.Invoke(entityConfig, new[] { lambdaExpression }) as DecimalPropertyConfiguration;
decimalConfig.HasPrecision((byte)propAttr.attr.Precision, (byte)propAttr.attr.Scale);
}
}
}
}
</pre>
<br />
接著將<span style="color: red;">DbContext</span>的<span style="color: red;">OnModelCreating</span>調整為使用<span style="color: red;">DecimalModelCreating</span>進行設定
<br />
<pre class="code prettyprint">protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
DecimalModelCreating.OnModelCreating(modelBuilder, "YourNamespance");
base.OnModelCreating(modelBuilder);
}
</pre>
<br />
參考:<br />
<a href="http://readily-notes.blogspot.tw/2015/01/asp-net-mvc4-webapi-entity-framework.html">Entity Framework Code First建立Decimal,並設定大小</a><br />
<br />
<span style="color: blue; font-size: large;">2015/01/19調整:</span><br />
近期將部分資料表移到<span style="color: red;">Azure Storage Table</span>的時候,發生了資料庫移轉錯誤
<br />
<pre class="code prettyprint">在模型產生期間偵測到一個或多個驗證錯誤:
System.Data.Entity.Edm.EdmEntityType: : EntityType 'myclass' 未定義索引鍵。請定義此 EntityType 的索引鍵。
System.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'myclass' 是以未定義索引鍵的類型 'myclass' 為基礎。
</pre>
<br />
檢查<span style="color: red;">OnModelCreating</span>後發現,在條件中,只要是在指定<span style="color: red;">命名空間(Namespace)</span>的Model都會被設定:<br />
<pre class="code prettyprint">t.Namespace.Equals(Namespace, StringComparison.CurrentCultureIgnoreCase) == true
</pre>
<br />
增加判斷語法,除了命名空間以外,還要有宣告在DbContext裡面:
<br />
<pre class="code prettyprint">typeof(MyContext).GetProperties().Where(x => x.PropertyType.IsGenericType && x.PropertyType.Name.StartsWith("DbSet") && x.PropertyType.GenericTypeArguments != null && x.PropertyType.GenericTypeArguments.Count() > 0).SelectMany(x => x.PropertyType.GenericTypeArguments).Select(x => x.Name).Contains(t.Name)
</pre>
<br />
參考:<br />
<a href="http://readily-notes.blogspot.tw/2015/01/asp-net-mvc4-webapi-entity-framework.html">Entity Framework Code First建立Decimal,並設定大小</a>
<br />
延伸閱讀:<br />
<a href="http://readily-notes.blogspot.tw/2015/01/entity-framework-code-firstattributesql.html">Entity Framework Code First依屬性(Attribute)設定SQL欄位 (續)</a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-73979736805528308642015-01-19T09:57:00.000+08:002015-01-20T09:47:55.887+08:00Entity Framework Code First建立Decimal,並設定大小在Sql建立Decimal的時候,習慣性會設定大小,但是在使用Entity Framework Code First的時候卻發現2個問題:<br />
1.沒辦法改變大小(預設是18,2),可能欄位太大或是小數位數不夠<br />
2.沒辦法檢查傳入的資料有沒有在限制的範圍內,可能會發生輸入不符合規則<br />
<br />
有人會使用float或double,然後配合<span style="color: red;">[RangeAttribute]</span>去限制大小,以及<span style="color: red;">[RegularExpressionAttribute]</span>來檢驗格式;這或許解決了資料驗證的問題,但是總不可能每個欄位都去下這些屬性,對資料庫欄位大小問題也一樣沒有解決。<br />
首先先來解決資料庫decimal大小問題。<br />
網路上找的最快的方式<a href="http://stackoverflow.com/questions/9032919/set-decimal16-3-for-a-column-in-code-first-approach-in-ef4-3">(Set decimal(16, 3) for a column in Code First Approach in EF4.3)</a>:
<br />
<pre class="code prettyprint">public class MyContext : DbContext
{
public DbSet<myclass> MyClass;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<myclass>().Property(x => x.Sum).HasPrecision(5, 1);
modelBuilder.Entity<myclass>().Property(x => x.Price).HasPrecision(3, 1);
base.OnModelCreating(modelBuilder);
}
}
</pre>
解決了資料庫大小問題<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB0PijjWRGpH3F5miUXUHyzAa29FgwVRT9GAZ-aFUVYm8nXICpF0Ad20Q3o-ncQHmNE9_v24QpZTbXGnnw6XM_vzAI42iGsoHn9HddeVyYuxCA8AN1pS3PV7CTa6UGIRggDz-Cx7SUpdk/s1600/1.PNG" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB0PijjWRGpH3F5miUXUHyzAa29FgwVRT9GAZ-aFUVYm8nXICpF0Ad20Q3o-ncQHmNE9_v24QpZTbXGnnw6XM_vzAI42iGsoHn9HddeVyYuxCA8AN1pS3PV7CTa6UGIRggDz-Cx7SUpdk/s320/1.PNG" /></a><br />
<br />
測試下去就會發現,輸入多少都可以過,只是到資料庫會被截掉,要再加個驗證讓<span style="color: red;">ModelState.IsValid</span>可以為我們擋掉。<br />
先建立一個<span style="color: red;">DecimalAttribute</span>繼承<a href="http://dotnetinside.com/in/type/System.ComponentModel.DataAnnotations/ValidationAttribute/2.0.5.0">ValidationAttribute</a>:<br />
<pre class="code prettyprint">[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public class DecimalAttribute : ValidationAttribute
{
public int Precision = 18;
public int Scale = 2;
public DecimalAttribute(int Precision = 18, int Scale = 2)
{
this.Precision = Precision;
this.Scale = Scale;
}
public override bool IsValid(object value)
{
if (value == null)
{
return true;
}
try
{
decimal valueAsDecimal = Convert.ToDecimal(value);
SqlDecimal sqlDecimal = SqlDecimal.ConvertToPrecScale(new SqlDecimal(valueAsDecimal), Precision, Scale);
}
catch (Exception ex)
{
if (string.IsNullOrEmpty(base.ErrorMessage) == true)
{
base.ErrorMessage = ex.Message;
}
return false;
}
return true;
}
}
</pre>
傳入資料測試驗證機制正常運行<br />
<br />
延伸閱讀:<br />
<a href="http://readily-notes.blogspot.tw/2015/01/asp-net-mvc4-webapi-entity-framework_19.html">Entity Framework Code First依屬性(Attribute)設定SQL欄位</a>
<br />
<a href="http://readily-notes.blogspot.tw/2015/01/entity-framework-code-firstattributesql.html">Entity Framework Code First依屬性(Attribute)設定SQL欄位 (續)</a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-47408896204498893262014-12-19T11:09:00.001+08:002014-12-19T11:09:34.663+08:00升級 Microsoft Azure Storage 4.3.0 以節省輸出流量、加快速度使用 Microsoft Azure Storage 來擷取 Azure Table 的資料,通常要考慮速度與流量的最佳化,但原本的版本是 Microsoft Azure Storage 2.0.6,是沒有支援 OData 的,且 SDK 又是以 URL 去和 Azure Table 要資料,且一次都會要所有欄位資料,且速度也很慢,更別說還要做一些資料處理了。<br />
<br />
我舉我的 Log 為例:<br />
<br />
Log 資料欄位約略有 10 欄。<br />
<pre class="code prettyprint">TableQuery<Log> query = (new TableQuery<Log>());
var data = table.ExecuteQuery<Log>(query);
var _query = (
from entity in data
// where entity.ErrorMessage != ""
where entity.Timestamp > DateTime.Now.AddDays(-1)
// where entity.Location.EndsWith("PUT")
orderby entity.Timestamp descending
select entity
);
DateTime std = DateTime.Now;
var r = _query.ToList();
DateTime etd = DateTime.Now;
TimeSpan diffDT = etd.Subtract(std);
Console.WriteLine("DateTime Method : {0} ms", diffDT.TotalMilliseconds);
</pre><br />
資料有 370 筆,執行時間 25275.3736 ms,約 25 秒。<br />
<br />
使用 Fiddler 來看:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwVJU1c5PlTuj5kl0ntQOMhgFuWg-6a2ZgQSlW01x-uB6BmLaZvLDbkuof_Bw6i3Bsff6wx4g47jXEVDuchH1fOPMzkU_fgSPoe_Vy_x7IrGUMUNhsVrWD8-pIW9BpPaE_7A1qMF3P6OE/s1600/fiddler-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwVJU1c5PlTuj5kl0ntQOMhgFuWg-6a2ZgQSlW01x-uB6BmLaZvLDbkuof_Bw6i3Bsff6wx4g47jXEVDuchH1fOPMzkU_fgSPoe_Vy_x7IrGUMUNhsVrWD8-pIW9BpPaE_7A1qMF3P6OE/s400/fiddler-1.png" /></a></div><br />
由於 Azure Table 資料輸出有限制筆數,所以才會要那麼多次,每一份資料都 760000 bytes 左右,0.76 MB 左右。<br />
<br />
升級 Microsoft Azure Storage 4.3.0 後,將程式碼稍微改一下,抓出需要使用的欄位就好:<br />
<pre class="code prettyprint">TableQuery<Log> query = (new TableQuery<Log>()).Select(new string[] { "partitionKey", "Timestamp" });
</pre><br />
資料有 330 筆,執行時間 9779.4132 ms,約 9.7 秒。<br />
<br />
使用 Fiddler 來看:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQiOmubhtA2Xo6o7D-gK6I2crc7vxUw_K1QHhykvcSK4SfULH-CvPt4TvhuT6OKZV5LoCrsZwWYcliYqo8teKj4AMNyNm0KHFFoR_5C11bkQjpfrbTqpsDMxUL3cSIarHceQJLmfxkaUM/s1600/fiddler-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQiOmubhtA2Xo6o7D-gK6I2crc7vxUw_K1QHhykvcSK4SfULH-CvPt4TvhuT6OKZV5LoCrsZwWYcliYqo8teKj4AMNyNm0KHFFoR_5C11bkQjpfrbTqpsDMxUL3cSIarHceQJLmfxkaUM/s400/fiddler-2.png" /></a></div><br />
這樣一來,既省輸出流量,也縮短時間,所以還沒有升級不訪升級試看看。<br />
<br />
David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com2tag:blogger.com,1999:blog-5188642618096096591.post-11960951788065676242014-11-25T13:57:00.001+08:002014-11-25T13:57:14.464+08:00ASP.NET MVC 解決加入「控制器」( Controller ) 消失的問題前陣子將專案升級,在 NuGet 安裝 ASP.NET Web API 2.2 for OData v4.0 套件,產生許多 dll 版本錯誤之問題。<br />
<br />
修正後,又發現在要加入「控制項」( Controller ) 時,找不到該選項,如下圖:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy9PR_2RPOfcchRYjW1yaL0bGSmqjizuMFLUF_aX7pmK75Lj7ETwROuJ3pTtCepUriUjri0bekMSlGCiQ-qmChyphenhyphenfanrZFRj9cbvYcfAqS_3ivFGCen0HfGNAd4ikw2llXzpuJ01K5hu2E/s1600/__1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgy9PR_2RPOfcchRYjW1yaL0bGSmqjizuMFLUF_aX7pmK75Lj7ETwROuJ3pTtCepUriUjri0bekMSlGCiQ-qmChyphenhyphenfanrZFRj9cbvYcfAqS_3ivFGCen0HfGNAd4ikw2llXzpuJ01K5hu2E/s400/__1.png" /></a></div><br />
甚至連加入「區域」( Areas ) 也找不到。<br />
<br />
後來找到 <a href="http://stackoverflow.com/questions/19849812/visual-studio-2013-add-controller-missing">visual studio 2013 'add controller' missing</a> 文章解說,照以下方法即可解決:<br />
<br />
<h2>1.</h2>卸載專案<br />
<br />
<h2>2.</h2>編輯專案檔<br />
<br />
<h2>3.</h2>尋找 ProjectTypeGuids 標籤 ( 請先備分 ),將內容改為 <pre class="code prettyprint"><ProjectTypeGuids>{E3E379DF-F4C6-4180-9B81-6769533ABE47};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids></pre><br />
儲存<br />
<br />
<h2>4.</h2>重新載入專案,完成。<br />
<br />
<br />
<br />
David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-74189077346574478052014-08-08T13:07:00.000+08:002014-08-08T13:10:28.460+08:00ASP .NET MVC4 WebApi - 不允許路由(Route)跨區域(Area)找Controller目前專案設定權限是採用Area進行區分,基本規劃為角色 1..n Area<br />
<br />
最近在強化權限控管,發現在Area找不到Controller的時候,竟然會去其他的Area找有沒有符合名稱的Controller<a href="http://readily-notes.blogspot.tw/2013/10/area-web-api-route.html">(如何切換 Area 的 Web Api 路由)</a><br />
<br />
對規劃的權限區分來說,這一個非常嚴重的問題<br />
<br />
<br />
舉個例子:<br />
(角色=>Area)<br />
<span style="color: blue;">人員管理者</span>=><span style="color: #6aa84f;">People</span><br />
<span style="color: blue;">薪資管理者</span>=><span style="color: #6aa84f;">Salary</span><br />
<br />
王小明有<span style="color: blue;">人員管理者</span>這個角色,所以他可以使用<span style="color: #6aa84f;">People</span>裡的所有Controller,
碰巧王小明知道了<span style="color: #6aa84f;">Salary</span>的某隻Api路徑可以取得全公司員工的薪資(api/<span style="color: red;">Salary</span>/AllPeople),
就權限管理機制,王小明沒有<span style="color: #6aa84f;">Salary</span>的Area權限,所以被判定為無權限(401)。<br />
<br />
到這邊都很沒問題,一切都很美好...<strike>如果這樣就結束,還要這篇幹嘛</strike><br />
<br />
偏偏王小明很有求知的精神,想說只要是<span style="color: #6aa84f;">People</span>就會被放行,那如果把<span style="color: #6aa84f;">Salary</span>改成<span style="color: #6aa84f;">People</span>不知道可不可以?<br />
於是原本的路徑(api/<span style="color: red;">Salary</span>/AllPeople)變成了(api/<span style="color: red;">People</span>/AllPeople)。<br />
<br />
這時候權限機制判斷王小明是<span style="color: blue;">人員管理者</span>,可以訪問<span style="color: #6aa84f;">People</span>的Area,所以放行讓他通過,
殊不知在<span style="color: #6aa84f;">People</span>下沒有AllPeople的Controller,但對權限機制來說並不重要,有沒有是ControllerSelector的問題。<br />
<br />
這時候<a href="http://readily-notes.blogspot.tw/2013/10/area-web-api-route.html">AreaHttpControllerSelector</a>出馬了,經過一連串的檢查,發現<span style="color: #6aa84f;">People</span>下沒有AllPeople的Controller,沒辦法走下去,那就交給老爸(<a href="https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Http/Dispatcher/DefaultHttpControllerSelector.cs">DefaultHttpControllerSelector</a>)去處理吧。<br />
<br />
而老爸是不管這些Area的,只要符合ControllerName就好,所以找到了<span style="color: red;">Salary</span>的AllPeople,然後就跟小弟(<a href="http://dotnetinside.com/in/type/System.Web.Http/ApiControllerActionSelector/5.1.0.0">ApiControllerActionSelector</a>)說,從<span style="color: red;">Salary</span>的AllPeople找符合的Action來執行吧。<br />
<br />
接著就會發現王小明引發<strike>美麗</strike>錯誤的結果,他拿到不該拿的資料了...<strike>其實也還好</strike><br />
<br />
<br />
這樣是不行的,如果Route有接到area的話,就只能從指定的Area下找Controller,沒找到就丟錯誤出去,所以修改一下<a href="http://readily-notes.blogspot.tw/2013/10/area-web-api-route.html">AreaHttpControllerSelector</a>:
<br />
<pre class="code prettyprint">...略...
private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
{
var controllerName = base.GetControllerName(request);
var areaName = GetAreaName(request);
if (string.IsNullOrEmpty(areaName))
{
return null;
}
var type = GetControllerTypeByArea(areaName, controllerName);
if (type == null)
{
//原本回傳null是為了向老爸求救,今天兒子要獨當一面了
//return null;
throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound,
string.Format("No route providing a controller name was found to match request URI \"{0}\"", new object[] { request.RequestUri })));
}
return new HttpControllerDescriptor(_configuration, controllerName, type);
}
</pre>
<br /><br />
參考:<br />
<a href="http://readily-notes.blogspot.tw/2013/10/area-web-api-route.html">如何切換 Area 的 Web Api 路由</a><br />
<a href="https://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Http/Dispatcher/DefaultHttpControllerSelector.cs">DefaultHttpControllerSelector.cs Source Code</a><br />
<a href="http://dotnetinside.com/in/type/System.Web.Http/ApiControllerActionSelector/5.1.0.0">ApiControllerActionSelector.cs Source Code</a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-46230863579551258142014-07-11T18:51:00.000+08:002014-07-14T15:27:24.317+08:00時區到各瀏覽器顯示時間統一處理方式今天要討論的是,當日期時間資料送到瀏覽器時,顯示出來的結果往往讓人難以捉摸。<br />
<br />
<span style="color: red;">DateTime</span>跟<span style="color: red;">DateTimeOffset</span>轉成JSON以後的結果:
<br />
<pre class="code prettyprint">{
"DateTimeOffset": "2014-07-11T00:00:00+08:00",
"DateTime": "2014-07-11T00:00:00"
}
</pre>
DateTimeOffset後面多了+08:00,表示這個時間是<span style="color: red;">特定時區</span>的時間,而DateTime是沒有的。
<br />
<br />
這時候丟到JavaScript的Date裡面:
<br />
<pre class="code prettyprint"> new Date(DateTimeOffset);
new Date(DateTime);
</pre>
呈現在畫面上就會變成:<br />
<table border="1"><tbody>
<tr>
<td>Browser</td>
<td>DateTimeOffset</td>
<td>DateTime</td>
</tr>
<tr>
<td>Chrome</td>
<td>Fri Jul 11 2014 <span style="color: red;">00:00:00</span> GMT+0800 (台北標準時間)</td>
<td>Fri Jul 11 2014 <span style="color: red;">08:00:00</span> GMT+0800 (台北標準時間)</td>
</tr>
<tr>
<td>IE11</td>
<td>Fri Jul 11 2014 <span style="color: red;">00:00:00</span> GMT+0800 (台北標準時間)</td>
<td>Fri Jul 11 2014 <span style="color: red;">00:00:00</span> GMT+0800 (台北標準時間)</td>
</tr>
<tr>
<td>FireFox</td>
<td>Fri Jul 11 2014 <span style="color: red;">00:00:00</span> GMT+0800 (台北標準時間)</td>
<td>Fri Jul 11 2014 <span style="color: red;">00:00:00</span> GMT+0800 (台北標準時間)</td>
</tr>
</tbody></table>
當沒有指定時區時,顯示出來的結果因各瀏覽器定義不同,而有不同的結果。
<br />
<br />
當不想要資料庫所有欄位都記時區的時候,就需要變更一下Json的<span style="color: red;">DateTimeConverter</span>。<br />
<br />
首先宣告一個<span style="color: red;">UTCDateTimeConverter</span>,並且繼承<a href="http://samples.ncover.com/FullCoverageReport/files/GTSk914lEQLFDgKFpC7sPMPOw.html">IsoDateTimeConverter</a>
,然後在轉JSON的時候自動轉換成設定的格式
<br />
<br />
備註1:希望給瀏覽器自動轉換為Local時間,所以是轉換成UTC(國際標準時間)<br />
備註2:避免日後Newtonsoft.Json對IsoDateTimeConverter更新,所以不重寫WriteJson,使用設定轉換格式並由IsoDateTimeConverter的WriteJson進行轉換。
<br />
<pre class="code prettyprint">/// <summary>
/// DateTime/DateTimeOffset 傳換為UTC日期格式 (Ex: 2014-07-11T12:53:00+00:00).
/// </summary>
public class UTCDateTimeConverter : IsoDateTimeConverter
{
/// <summary>
/// 日期轉成Json格式
/// </summary>
/// <param name="writer" >The JsonWriter to write to.</param>
/// <param name="value" >The value.</param>
/// <param name="serializer" >The calling serializer.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//DateTimeOffset轉換時會自動加上時區,所以要先把他轉成UTC時間的DateTime
if (value.GetType() == typeof(DateTimeOffset))
{
DateTimeOffset tmpDateTimeOffset = (DateTimeOffset)value;
value = tmpDateTimeOffset.DateTime.Add(-tmpDateTimeOffset.Offset);
}
//設定想要轉換的格式
base.DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.FFFFFFFK+00:00";
//使用預設的WriteJson進行轉換
base.WriteJson(writer: writer, value: value, serializer: serializer);
}
}
</pre>
因為希望傳出去時間格式是統一的,而使用的也是WebApi,所以直接在App_Start/WebApiConfig.cs進行設定:
<br />
<pre class="code prettyprint">config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new UTCDateTimeConverter());
</pre>
<br />
轉出來的Json為:
<br />
<pre class="code prettyprint">{
"DateTimeOffset": "2014-07-10T16:00:00+00:00",
"DateTime": "2014-07-11T00:00:00+00:00"
}
</pre>
DateTimeOffset已經是被剪了8小時。
<br />
<br />
呈現在畫面上就會變成:<br />
<table border="1"><tbody>
<tr>
<td>Browser</td>
<td>DateTimeOffset</td>
<td>DateTime</td>
</tr>
<tr>
<td>Chrome</td>
<td>Fri Jul 11 2014 <span style="color: red;">00:00:00</span> GMT+0800 (台北標準時間)</td>
<td>Fri Jul 11 2014 <span style="color: red;">08:00:00</span> GMT+0800 (台北標準時間)</td>
</tr>
<tr>
<td>IE11</td>
<td>Fri Jul 11 2014 <span style="color: red;">00:00:00</span> GMT+0800 (台北標準時間)</td>
<td>Fri Jul 11 2014 <span style="color: red;">08:00:00</span> GMT+0800 (台北標準時間)</td>
</tr>
<tr>
<td>FireFox</td>
<td>Fri Jul 11 2014 <span style="color: red;">00:00:00</span> GMT+0800 (台北標準時間)</td>
<td>Fri Jul 11 2014 <span style="color: red;">08:00:00</span> GMT+0800 (台北標準時間)</td>
</tr>
</tbody></table>
<br />
<br />
瀏覽器都統一了^^
<br />
<br />
參考:<br />
<a href="http://samples.ncover.com/FullCoverageReport/files/GTSk914lEQLFDgKFpC7sPMPOw.html">IsoDateTimeConverter Source</a>
<br />
<a href="http://stackoverflow.com/questions/12936614/asp-net-web-api-date-format-in-json-does-not-serialise-successfully">Add different Json.NET DateTimeConverters through the JsonFormatter's SerializerSettings</a>
<br />
<a href="http://stackoverflow.com/questions/8639315/how-to-create-a-json-net-date-to-string-custom-converter">Create a JSON.NET Date to String custom Converter</a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-30979629137850284762014-07-08T02:00:00.000+08:002014-07-08T02:00:00.868+08:00ASP .NET MVC4 WebApi-CultureInfo-文化特性排序相信很多人都發生過,本機排序正常,一上到正式主機以後,排序出來的結果卻不一樣。<br />
<br />
其實.NET的執行緒就跟SQL Server的資料表一樣有定序問題,所以排序結果會因為設定而有所不同,基本上執行緒的定序是跟著Server預設語系的。<br />
<br />
在變更執行緒的語系之前,先看一下<a href="http://msdn.microsoft.com/zh-tw/library/system.globalization.cultureinfo.aspx">CultureInfo類別</a>。<br />
<br />
MSDN的說明是:<span style="color: red;">提供有關特定文化特性 (Culture) 的資訊 (文化特性在 Unmanaged 程式碼開發中稱為「地區設定」(Locale))。 提供的資訊包括文化特性的名稱、書寫系統、使用的曆法,以及日期和排序字串的格式。</span><br />
<br />
最後面那一句話,<span style="color: red;"><b><u>排序字串的格式</u></b></span>。<br />
<br />
了解了這個類別的用途,接著來看一下幾個語系排序的規則。<br />
<br />
<table style="border-collapse: collapse; border: 1px solid rgb(187, 187, 187); color: black; font-family: 'Microsoft JhengHei UI', 'Microsoft JhengHei', PMingLiU, MingLiU, 'Segoe UI', 'Lucida Grande', Verdana, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 17.549999237060547px; padding: 0px;">
<tbody>
<tr>
<th style="background-color: #ededed; border: 1px solid rgb(187, 187, 187); color: #636363;"><div style="color: #2a2a2a; line-height: 18px; padding-bottom: 0px;">
文化特性名稱
</div>
</th>
<th style="background-color: #ededed; border: 1px solid rgb(187, 187, 187); color: #636363;"><div style="color: #2a2a2a; line-height: 18px; padding-bottom: 0px;">
文化特性
</div>
</th>
<th style="background-color: #ededed; border: 1px solid rgb(187, 187, 187); color: #636363;"><div style="color: #2a2a2a; line-height: 18px; padding-bottom: 0px;">
預設的排序名稱和識別項
</div>
</th>
<th style="background-color: #ededed; border: 1px solid rgb(187, 187, 187); color: #636363;"><div style="color: #2a2a2a; line-height: 18px; padding-bottom: 0px;">
替代排序名稱和識別項
</div>
</th>
</tr>
<tr>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
zh-TW
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
中文 (台灣)
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
筆劃:0x00000404
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
注音符號:0x00030404
</div>
</td>
</tr>
<tr>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
zh-CN
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
中文 (中華人民共和國)
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
發音:0x00000804
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
筆劃:0x00020804
</div>
</td>
</tr>
<tr>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
ja-JP
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
日文 (日本)
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
預設:0x00000411
</div>
</td>
<td style="border: 1px solid rgb(187, 187, 187); color: #2a2a2a vertical-align: top;"><div style="line-height: 18px; padding-bottom: 0px;">
Unicode:0x00010411
</div>
</td>
</tr>
</tbody>
</table>
<br />
<div>
知道各語系排序規則以後,接著我們只需要在排序前加上一個指定就搞定了。</div>
<div>
<br /></div>
<div>
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(language);<br />
<br />
這樣是不是很簡單呢。<strike><span style="color: red; font-size: xx-small;">(明明為了排序搞了很久)</span></strike><br />
<br />
參考:<br />
<a href="http://blog.darkthread.net/post-2011-02-16-cultureinfo-and-string-compare.aspx">CultureInfo與中文字串排序</a><br />
<a href="http://msdn.microsoft.com/zh-tw/library/system.globalization.cultureinfo.aspx">CultureInfo 類別</a></div>
KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-31434173318447316812014-05-19T14:07:00.000+08:002014-05-19T14:30:22.930+08:00執行 API Controller 遇到 「Not running in a hosted service or the Development Fabric.」 問題之解決方案建置 API Controller 時,執行後遇到以下錯誤訊息:<br />
<br />
Not running in a hosted service or the Development Fabric.<br />
<br />
<b>描述</b>: 在執行目前 Web 要求的過程中發生未處理的例外狀況。請檢閱堆疊追蹤以取得錯誤的詳細資訊,以及在程式碼中產生的位置。 <br />
<br />
<b>例外狀況詳細資訊</b>: System.InvalidOperationException: Not running in a hosted service or the Development Fabric.<br />
<br />
<b>原始程式錯誤</b>: <br />
<br />
<div style="background-color: #ffffcc;">在執行目前 Web 要求期間,產生未處理的例外狀況。如需有關例外狀況來源與位置的資訊,可以使用下列的例外狀況堆疊追蹤取得。</div><br />
<b>堆疊追蹤</b>: <br />
<br />
<pre style="background-color: #ffffcc;">[InvalidOperationException: Not running in a hosted service or the Development Fabric.]
Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitor.GetDefaultStartupInfoForCurrentRoleInstance() +173
Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener..ctor() +59
[ConfigurationErrorsException: 無法建立 Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35。]
System.Diagnostics.TraceUtils.GetRuntimeObject(String className, Type baseType, String initializeData) +6707013
System.Diagnostics.TypedElement.BaseGetRuntimeObject() +45
System.Diagnostics.ListenerElement.GetRuntimeObject() +83
System.Diagnostics.ListenerElementsCollection.GetRuntimeObject() +142
System.Diagnostics.TraceInternal.get_Listeners() +181
System.Diagnostics.TraceInternal.TraceEvent(TraceEventType eventType, Int32 id, String format, Object[] args) +155
System.Diagnostics.Trace.TraceInformation(String message) +14
System.Web.Http.Tracing.SystemDiagnosticsTraceWriter.TraceMessage(TraceLevel level, String message) +157
System.Web.Http.Tracing.SystemDiagnosticsTraceWriter.Trace(HttpRequestMessage request, String category, TraceLevel level, Action`1 traceAction) +438
System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEndAsync(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Func`1 execute, Action`2 endTrace, Action`1 errorTrace) +464
System.Web.Http.Tracing.Tracers.RequestMessageHandlerTracer.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) +367
System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) +41
System.Web.Http.HttpServer.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) +390
System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) +55
System.Web.Http.WebHost.HttpControllerHandler.BeginProcessRequest(HttpContextBase httpContextBase, AsyncCallback callback, Object state) +316
System.Web.Http.WebHost.HttpControllerHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state) +77
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +301
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
</pre><br />
這因為是 Windows Azure 雲端服務專案發生的情況,所以解決方法有二:<br />
<br />
<h2>1.</h2>建置 Windows Azure 雲端服務會有兩個專案,一為 ASP.NET MVC 4 Web 應用程式,另一個是 Windows Azure Web Role 專案。必須在方案內設定 Windows Azure Web Role 專案為「啟用」,像我就是設定 ASP.NET MVC 4 Web 應用程式 為「啟用」。<br />
<br />
<h2>2.</h2>web.config 中移除追蹤接聽項 ( trace listener ):<br />
<pre class="code prettyprint"><trace>
<listeners>
<add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
name="AzureDiagnostics">
<filter type="" />
</add>
</listeners>
</trace>
</pre><br />
兩種方法都能解決此種問題,我是用第二種方法解決。<br />
David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-23762904877733235862014-05-15T12:30:00.000+08:002014-05-15T12:30:02.185+08:00Entity Framework 與 LINQ -- 篩選(Where)使用時機最近發現,使用Entity Framework的話,要注意下Where的時機,因為有可能是會在SQL端篩選,也有可能會在C#端篩選,可能會導致不同的結果,甚至發生不支援的情況。<br /><br />
拿字串A是否包含了字串B作為範例,這時候在SQL端(還沒產生成實體物件)時執行結果 跟 C#端(已從DB撈出資料)執行結果 就會發生不同的狀況。<br />
先來看一Where下在資料還沒產生成實體物件的時候<br />
C#:
<pre class="code prettyprint">
var query = db.Table.Where(x => x.Name.Contains("abc")).ToList();
</pre>
SQL執行語法會是
<pre class="code prettyprint">
SELECT[EmployeeId],[Name]
FROM [dbo].[Employee]
WHERE [Name] LIKE '%abc%'
</pre>
由此可見,資料判斷是在SQL端處理,然後把結果丟回到C#端。<br /><br />
再來是Where下在ToList()後面<br />
C#:
<pre class="code prettyprint">
var query = db.Table.ToList().Where(x => x.Name.Contains("abc"));
</pre>
SQL執行語法會是
<pre class="code prettyprint">
SELECT[EmployeeId],[Name]
FROM [dbo].[Employee]
</pre>
這時候變成了把所有Employee資料撈出來以後,在篩選資料。<br /><br />
一個是在SQL端下LIKE,一個是在C#端執行Contains,都是包含"abc",但是結果就會不一樣囉。<br /><br />
SQL的LIKE是不分辨大小寫的,所以"ABC"、"AbC"、"abc"、"abC"...都是會被認為是OK的資料。<br />
但是在C#的Contains是有區分大小寫的,所以只有"abc"才會被篩選出來。<br /><br />
如果使用IndexOf去指定不區分大小寫,但是這個只能在C#端使用<br />
錯誤的C#語法:
<pre class="code prettyprint">
var query = db.Table.Where(x => x.Name.IndexOf("abc", StringComparison.CurrentCultureIgnoreCase) >= 0).ToList();
</pre>
這樣會報錯,因為LINQ在轉SQL語法的時候會發生不支援的情況,就回歸到一開始提到的Where時機。<br />
所以我們要在取出資料後,由C#去執行篩選<br />
C#語法:
<pre class="code prettyprint">
var query = db.Table.ToList().Where(x => x.Name.IndexOf("abc", StringComparison.CurrentCultureIgnoreCase) >= 0);
</pre><br />
<span style="color: red;">PS:Contains是使用IndexOf去實做出來的,所以基本上IndexOf效能會略好於Contains</span><br /><br />
KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-30581997348779686952014-05-09T14:15:00.000+08:002014-07-22T14:03:26.942+08:00ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecount(續2) -- 中繼傳遞OData參數延續前二篇<br />
<a href="http://readily-notes.blogspot.tw/2014/05/asp-net-mvc4-webapi-odata-inlinecount.html">(ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecount)</a><br />
<a href="http://readily-notes.blogspot.tw/2014/05/asp-net-mvc4-webapi-odata-inlinecount-c.html">(ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecount(續) -- C# Model 接取$inlinecount 資料)</a><br />
<br />
範例為建立一個API,把OData參數傳給資料來源Api,然後做簡單處理或直接回傳(資料來源Api開放使用OData)。<br/><br/>
建立一個Api
<pre class="code prettyprint">
//如果有$inlinecount就會是不同的格式,所以這邊回傳Object
public Object Get()
{
return "test";
}
</pre>
取得所有OData參數
<pre class="code prettyprint">
List<string> arrParams = Request.GetQueryNameValuePairs() //所有QueryString的Name、Value集合
.Where(x => x.Key.StartsWith("$")) //只取得開頭為$
.Select(x => string.Format("&{0}={1}", x.Key, Uri.EscapeDataString(x.Value))) //組回 &Name=Value
.ToList();
</pre>
因為範例的資料來源API本身有指定參數,所以這邊會先把 & 加上,方便後面整理所有參數,請視個人情況調整加上的時機。<br/><br/>
判斷是否有設定$inlinecount=allpages<br/>
有的話就要用<a href="http://readily-notes.blogspot.tw/2014/05/asp-net-mvc4-webapi-odata-inlinecount-c.html">C# Model 接取$inlinecount 資料</a>的共用Model去接資料,沒有就用一般方式即可。<br/>
<pre class="code prettyprint">
if(arrParams.Select(x => x.ToLower()).Contains("&$inlinecount=allpages") == true)
</pre>
Select出來轉小寫以後判斷是否包含"&$inlinecount=allpages",因為沒實際測試$filter大小寫是否有區別,所以沒有在取得QueryString的時候就轉。<br/>
由組回 Name=Value 時前面有沒有加 & 來決定這邊要不要加。
<br/><br/>
組合資料來源Api的URL
<pre class="code prettyprint">
string.Format("DefauleUrl?name1=val1&name2=val2{0}", string.Join("", arrParams));
</pre>
string.Join要不要加 & 一樣由前面就決定了,串出來的Url請仔細確認參數部分的格式是不是正確的。<br/><br/>
下面是實際範例
<pre class="code prettyprint">
public Object Get()
{
List<string> arrParams = Request.GetQueryNameValuePairs().Where(x => x.Key.StartsWith("$")).Select(x => string.Format("&{0}={1}", x.Key, Uri.EscapeDataString(x.Value))).ToList();
bool IsAllpages = false;
string strParams = string.Join("", arrParams);
string strUrl = string.Format("DefauleUrl?name1=val1&name2=val2{0}", string.Join("", arrParams));
List<ModelName> Models = new List<ModelName>();
ODataByApi<ModelName> odateModel = new ODataByApi<ModelName>();
if (arrParams.Select(x => x.ToLower()).Contains("&$inlinecount=allpages"))
{
odateModel = GetData<ODataByApi<ModelName>>(strParams);
Models = odateModel.Items.ToList();
IsAllpages = true;
}
else
{
Models = GetData<List<ModelName>>(strParams);
}
//資料處理
if (Models != null)
{
//To do....
}
//有$inlinecount=allpages,丟回ODataByAp後return
if (IsAllpages == true)
{
odateModel.Items = Models;
return odateModel;
}
//否則return List
return Models;
}
</pre>
<br />
參考:<br />
<a href="http://readily-notes.blogspot.tw/2014/05/asp-net-mvc4-webapi-odata-inlinecount.html">(ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecount)</a><br />
<a href="http://readily-notes.blogspot.tw/2014/05/asp-net-mvc4-webapi-odata-inlinecount-c.html">(ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecount(續) -- C# Model 接取$inlinecount 資料)</a><br />KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-2593560499589084532014-05-07T13:05:00.000+08:002014-05-07T13:05:00.051+08:00ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecount(續) -- C# Model 接取$inlinecount 資料延續前一篇<a href="http://readily-notes.blogspot.tw/2014/05/asp-net-mvc4-webapi-odata-inlinecount.html">(ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecount)</a><br/><br/>
當C#去接API來的資料,且有設定$inlinecount,資料格式會變更成:
<pre class="code prettyprint">
{
"Items": [
{
//資料1
},
{
//資料2
},
{
//資料3
}
],
"NextPageLink": null,
"Count": 3
}
</pre>
Items存放資料,NextPageLink存放下一頁網址,Count存放數量<br/><br/>
無法使用一般Model去接資料,因此需要準備一個共用Model
<pre class="code prettyprint">
public class ODataByApi<T>
{
public ICollection<T> Items { get; set; }
public string NextPageLink { get; set; }
public int Count { get; set; }
}
</pre>
<br/>
用共用Model,並指定資料Model去接資料即可
<pre class="code prettyprint">
JsonConvert.DeserializeObject<ODataByApi<ModelName>>(strJson);
</pre><br/><br/>
參考:<br/>
<a href="http://readily-notes.blogspot.tw/2014/05/asp-net-mvc4-webapi-odata-inlinecount.html">(ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecount)</a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-30182351526802059152014-05-05T11:29:00.000+08:002014-07-22T14:47:40.893+08:00ASP .NET MVC4 WebApi -- OData 使用 與 實作$inlinecountWebApi提供各種平台取得相關資料,為了滿足各種平台不同的需求(排序、分頁、查詢等等),最直覺的方式就是指定參數給他們使用,再處理各個參數,但這樣就變成不夠彈性,不需要查詢也要傳參數的值,同時加重了前、後端開發人員的麻煩。<br />
<br />
使用OData讓前端傳入參數,不需要寫死程式,隨心所欲的完成排序、分頁、查詢等功能。<br />
先來看看OData常用參數:<br />
<table border="1">
<tbody>
<tr><td width="70px">$top</td><td>傳回前幾筆資料</td></tr>
<tr><td>$skip</td><td>跳過幾筆資料</td></tr>
<tr><td valign="top">$filter</td><td>
<li>查詢(where)<br />
eq-等於、gt-大於、lt-小於、ne-不等於
</li>
<li>
串連<br />and、or
</li>
</td></tr>
<tr><td>$orderby</td><td>排序</td></tr>
<tr><td>$inlinecount</td><td>傳回資料、總筆數、下一頁Url
<li>allpages</li>
<li>none(預設)</li>
</td></tr>
</tbody></table>
使用方式跟一般傳參數一樣,例如:<span style="color: blue;">http://{domain}/api/{controller}/{action}?$top=5&$skip=10</span><br />
<a href="http://msdn.microsoft.com/library/azure/gg312156.aspx">(更多參數說明請參考官方網站)</a><br />
<br />
在API的部分,需要設定屬性[Queryable],就可以使用OData。<br />
<span style="color: red;">備註:</span><br />
<span style="color: red;">1.許多文章都說需要使用[Queryable]搭配回傳IQuerable、AsQueryable(),經過測試後,回傳IEnumerable也是可以正常使用的。</span><br />
<span style="color: red;">2.如果不想開放所有參數、或有一些限制條件,後端人員可以在[Queryable]設定相關參數以達到目的</span><br />
<br />
這樣就可以依照各種情況由前端開發人員自行決定需要傳遞那些參數,而後端開發人員只需要專心的處理資料以確保資料正確性即可。<br />
<br />
<br />
在測試的時候發現$inlinecount一直沒辦法使用(<span style="color: red;">微軟好像不支援一些參數</span>),這樣前端在做分頁時就不知道資料總筆數,所以我們動手實作一個屬性吧<br />
<br />
<pre class="code prettyprint">public class InlineCountQueryableAttribute : QueryableAttribute
{
private static MethodInfo _createPageResult =
typeof(InlineCountQueryableAttribute)
.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
.Single(m => m.Name == "CreatePageResult");
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
HttpRequestMessage request = actionExecutedContext.Request;
HttpResponseMessage response = actionExecutedContext.Response;
IQueryable result;
if (response.IsSuccessStatusCode
&& response.TryGetContentValue<IQueryable>(out result))
{
long? inlineCount = request.GetInlineCount();
if (inlineCount != null)
{
actionExecutedContext.Response = _createPageResult.MakeGenericMethod(result.ElementType).Invoke(
null, new object[] { request, request.GetInlineCount(), request.GetNextPageLink(), result }) as HttpResponseMessage;
}
}
}
internal static HttpResponseMessage CreatePageResult<T>(HttpRequestMessage request, long? count, Uri nextpageLink, IEnumerable<T> results)
{
return request.CreateResponse(HttpStatusCode.OK, new PageResult<T>(results, nextpageLink, count));
}
}
</pre>
設定屬性由[Queryable]改為[InlineCountQueryable]即可正常使用$inlinecount參數
<br />
<br />
參考:<br />
<a href="http://msdn.microsoft.com/zh-tw/library/system.web.http.odata(v=vs.111).aspx">System.Web.Http.OData 命名空間</a><br />
<a href="http://www.odata.org/">OData官網</a><br />
<a href="http://blog.darkthread.net/post-2012-10-23-iqueryable-experiment.aspx">關於IQueryable<t>特性的小實驗</t></a><br />
<a href="http://stackoverflow.com/questions/15790695/web-api-odata-inlinecount-and-testing">Web API, OData, $inlinecount and testing</a><br />
<br />
<br />
<span style="color: red; font-size: large;"><b>2014/07/22 KaiYai補充:</b></span><br />
最近無意間發現了Bug,當程式碼出現錯誤產生Exception,Action回前端時依然會進到Attribute內執行OnActionExecuted事件,因為是Exception,所以傳進來的<span style="color: red;">HttpActionExecutedContext.Response會是null</span>,而<span style="color: #6aa84f;">HttpActionExecutedContext.Exception會是錯誤資訊</span>,如果利用原本程式碼收到的錯誤訊息不會是原本實際發生錯誤的部分,而是會出現OnActionExecuted內的錯誤:<br />
<pre class="code prettyprint"><Error>
<Message>發生錯誤。</Message>
<ExceptionMessage>並未將物件參考設定為物件的執行個體。</ExceptionMessage>
<ExceptionType>System.NullReferenceException</ExceptionType>
<StackTrace>
...略...
</StackTrace>
</Error>
</pre>
<br />
但實際上的錯誤應該是
<br />
<pre class="code prettyprint"><Error>
<Message>發生錯誤。</Message>
<ExceptionMessage>輸入字串格式不正確。</ExceptionMessage>
<ExceptionType>System.FormatException</ExceptionType>
<StackTrace>
...略...
</StackTrace>
</Error>
</pre>
<br />
這是因為response已經收到null值,指令沒有判斷到是否為null,就會發生會設定物件的錯誤。<br />
所以修正條件式加上判斷response != null<br />
<pre class="code prettyprint">if (response != null && response.IsSuccessStatusCode && response.TryGetContentValue<IQueryable>(out result))</pre>
<br />
原本想要直接判斷<span style="color: #6aa84f;">HttpActionExecutedContext.Exception != null</span>,但想了想,為什麼執行<span style="color: #e69138;">base.OnActionExecuted(actionExecutedContext);</span>沒有發生錯誤呢?所以決定看一下<a href="https://searchcode.com/codesearch/view/27742382/">QueryableAttribute</a>的Code,看到下的條件以後,毅然決然的直接照辦,<strike>有錯微軟會先被罵</strike>:
<br />
<pre class="code prettyprint">public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
...略...
HttpResponseMessage response = actionExecutedContext.Response;
if (response != null && response.IsSuccessStatusCode)
...略...
}
</pre>
<br />
<br />
參考:<br />
<a href="https://searchcode.com/codesearch/view/27742382/">QueryableAttribute Source(aspnetwebstack /src/System.Web.Http.OData/QueryableAttribute.cs)</a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com5tag:blogger.com,1999:blog-5188642618096096591.post-83491777527708633592014-04-30T11:14:00.000+08:002014-04-30T11:17:51.164+08:00.Net WebSite/WebApi 限制連線IP開發人員最怕的就是未經授權或非正常管道使用網站/網路服務<br />
<br />
小弟目前專案都是透過WebApi傳遞資料,都是一關卡一關<br />
<br />
避免有人<span style="color: red;"><strike>不小心</strike></span>知道使用後端服務的方法,所以後端的服務都必須限制連接IP<br />
<br />
<br />
首先先在Web.config(<span style="color: red;">會跟IIS限制IP and DomainName同步</span>)設定禁止所有IP連接<br />
<pre class="code prettyprint"><configuration>
<system.webserver>
<security>
<ipsecurity allowunlisted="flase">
</ipsecurity>
</security>
</system>
</configuration>
</pre>
<br />
<span style="color: red;">這樣就禁止了所有IP連線</span>,接下來為了方便本機測試功能是否正常,以及允許連線的IP,所以我們要在<span style="color: red;">ipSecurity</span>裡面去設定開放條件<br />
<pre class="code prettyprint"><configuration>
<system.webserver>
<security>
<ipsecurity allowunlisted="flase">
<add allowed="true" ipaddress="127.0.0.1" />
<add allowed="true" ipaddress="192.168.1.1" />
</ipsecurity>
</security>
</system>
</configuration>
</pre>
<br />
<br />
這時候基本上已經完成了9成<br />
<br />
剩下的一成就是要看IIS有沒有<span style="color: red;">開啟IP 位址及網域限制(IP Address and Domain Restrictions)</span>功能<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNpDCmZP-VsX1DrCBK4rucksUgasOQPCC9k0BR3n06e-zAsEC3KA39D4cMyqa4gLBio4D9VIdIxXLMnQVJm2jvZEgigxFVfbDRQG68u1HeJuhLErERhSkM-1rTqxmh3EwKJeeJP_gvbVQ/s1600/IP+Address+and+Domain+Restrictions.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNpDCmZP-VsX1DrCBK4rucksUgasOQPCC9k0BR3n06e-zAsEC3KA39D4cMyqa4gLBio4D9VIdIxXLMnQVJm2jvZEgigxFVfbDRQG68u1HeJuhLErERhSkM-1rTqxmh3EwKJeeJP_gvbVQ/s1600/IP+Address+and+Domain+Restrictions.PNG" height="182" width="320" /></a>
<br />
<strike>前幾天因為Server沒有安裝所以卡在這裡</strike><a href="http://readily-notes.blogspot.tw/2014/04/windows-azure.html">(Windows Azure 遠端桌面設定)</a><br />
<br />
沒有開啟的話,請到<span style="color: red;">開啟或關閉Windows功能</span>去設定<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJn2zJWzYRYmQMCyGcX9oLEgrOfNIldiCTqUScd9YNdXvDvI__MvE7N1gVH8HV8lV8LOzkpjpYmd9epKe_9E11tAZIWXoAsdneaSVY3zj1SybfCZ1qj7IcppHHzd069IiSRaavi2b13FA/s1600/Open+IP+Address+and+Domain+Restrictions.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJn2zJWzYRYmQMCyGcX9oLEgrOfNIldiCTqUScd9YNdXvDvI__MvE7N1gVH8HV8lV8LOzkpjpYmd9epKe_9E11tAZIWXoAsdneaSVY3zj1SybfCZ1qj7IcppHHzd069IiSRaavi2b13FA/s1600/Open+IP+Address+and+Domain+Restrictions.PNG" height="211" width="320" /></a>
<br />
以上2個動作就完成限制IP連線了<br />
<br />
<br />
參考:<br />
<a href="http://www.stokia.com/support/misc/web-config-ip-address-restriction.aspx">Web.config ipSecurity</a><br />
<a href="http://www.iis.net/configreference/system.webserver/security/ipsecurity">IP Security <ipsecurity></a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-22685853515850658482014-04-30T10:50:00.000+08:002014-04-30T10:50:25.737+08:00ASP.NET MVC 4 WebApi 與 Extjs 的結合 -- 換頁重新載入資料在 Web Form 做 Grid 換頁時,通常會先將資料把所有資料先建入 DataTable,再使用控制項接收這些資料,換頁時,就將這些資料經過處理再顯示。然後再做資料搜尋,會再去要一次資料,如此一來,在每次產生 GridView 時,都會使用同一份資料篩選,等於是網頁上有一份資料在做處理,如果資料量一大,吃的資源也大,處理與顯示時相對的就慢。<br />
<br />
使用 Extjs 做 Grid.Panel 分頁,一般來說會先將所有資料載到頁面,再做分頁,只是 Extjs 接收資料格式必須要在最根層加上總數量,這樣才能知道該頁顯示幾頁,總共有幾頁,所以格式必須符合如下:<br />
<pre class="code prettyprint">{
"totalCount": "6679",
"topics": [
{
// ....
},
{
// ....
},
{
// ....
},
{
// ....
},
{
// ....
}
// ...
]
}
</pre><br />
而符合這種格式必須在 Web Api 段做些許調整,要多寫一個屬性讓 API 能使用,可以參考:<a href="http://stackoverflow.com/questions/15790695/web-api-odata-inlinecount-and-testing" rel="nofollow">Web API, OData, $inlinecount and testing</a>,程式碼如下<br />
<br />
<pre class="code prettyprint">public class InlineCountQueryableAttribute : QueryableAttribute
{
private static MethodInfo _createPageResult =
typeof(InlineCountQueryableAttribute)
.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
.Single(m => m.Name == "CreatePageResult");
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
HttpRequestMessage request = actionExecutedContext.Request;
HttpResponseMessage response = actionExecutedContext.Response;
IQueryable result;
if (response.IsSuccessStatusCode
&& response.TryGetContentValue<IQueryable>(out result))
{
long? inlineCount = request.GetInlineCount();
if (inlineCount != null)
{
actionExecutedContext.Response = _createPageResult.MakeGenericMethod(result.ElementType).Invoke(
null, new object[] { request, request.GetInlineCount(), request.GetNextPageLink(), result }) as HttpResponseMessage;
}
}
}
internal static HttpResponseMessage CreatePageResult<T>(HttpRequestMessage request, long? count, Uri nextpageLink, IEnumerable<T> results)
{
return request.CreateResponse(HttpStatusCode.OK, new PageResult<T>(results, nextpageLink, count));
}
}
</pre><br />
在 API 加上此屬性:<br />
<pre class="code prettyprint">[InlineCountQueryable]
public IEnumerable<Employees> GetEmployees()
{
IEnumerable<Employees> employees = db.Employees.ToList();
foreach (Employees employee in employees)
{
employee.Employees1 = null;
employee.Employees2 = null;
}
return employees;
}
</pre><br />
執行結果如下:<br />
<pre class="code prettyprint">{
Count: 15,
Items: [
{ ... },
{ ... },
...
],
NextPageLink: XXX,
}
</pre><br />
這樣輸出結果與 Extjs Grid.Panel 做分頁要接收的格式已經 95% 相似了,而換頁時通常自動會帶 page 、 start 和 limit,page 感覺只是顯示用,而 start 和 limit 的部分可以使用 API 的 Queryable 來解決,它等同於 Queryable 的 $skip 和 $top:<br />
<br />
首先我們先看如何在 grid.Panel 加上分頁,加上一個屬性即可,其中 store 為資料來源:<br />
<pre class="code prettyprint">bbar: Ext.create('Ext.PagingToolbar', {
store: store,
displayInfo: true,
displayMsg: 'Displaying topics {0} - {1} of {2}',
emptyMsg: "No topics to display",
}),
</pre><br />
因為每一次換頁就會與 store 要一次資料,所以必須針對來源做修改:<br />
<pre class="code prettyprint">var store = Ext.create('Ext.data.JsonStore', {
storeId: 'store',
model: 'Model',
pageSize: 3,
proxy: {
type: 'ajax',
url: 'http://localhost:8090/api/Control?$inlinecount=allpages',
reader: {
type: 'json',
root: 'Items',
totalProperty: 'Count',
},
startParam: '$skip',
limitParam: '$top'
},
autoLoad: true,
listeners: {
load: function (store, records, options) {
}
},
});
</pre><br />
其中 PageSize 為每頁顯示數量,而 startParam、limitParam 則是改變它換頁帶的參數,最後執行就可以看到每換一次頁就會重新與 API 要一次資料,這樣一來頁面負擔不會太重,同時又做到速度與流量兼顧的結果。<br />
David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-26294833424160308062014-04-29T15:48:00.000+08:002014-04-29T15:48:47.218+08:00Windows Azure 遠端桌面設定+遠端掛載本機磁碟相信大家都遇過本機(或測試Server)Run的程式一切正常(<strike>完美</strike>),但是一上到正式(或其他)Server就一堆問題,絕大多數是因為有些東西沒有設定好(這邊指的是Server上的一些設定)
<br />
<br />
最近使用Windows Azure建立雲端服務(Cloud Service),為了限制IP使用,在Web.config設定允許IP列表,本機、同事的電腦...等等測試一切正常,但是一上到Azure就完全無效
<br />
<br />
因為也是最近才接觸Azure,只用過入口網站跟VS進行設定跟上傳,一直有個疑惑,微軟不可能鎖死Server設定(<strike>一定被罵翻</strike>),因為太多狀況了,也不可能全部開放,但是遠端桌面及一些Server設定應該是可以才對,花了一些時間終於找到了方法
<br />
<br />
首先要先設定連線的帳號密碼<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiirYGNCI8GQpx2vJAKPfBATGKO_1Fr4d8aIra4DmnEZcd_EtUJBA3UIN0qLXX4UoQn9T59ha34ISO88G_MZlDkuzcTmM6BXzSlc0UdUJpIgKZYPuEXIXQZG8-0UFBlAStHWM85yYa4SjY/s1600/setUser.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiirYGNCI8GQpx2vJAKPfBATGKO_1Fr4d8aIra4DmnEZcd_EtUJBA3UIN0qLXX4UoQn9T59ha34ISO88G_MZlDkuzcTmM6BXzSlc0UdUJpIgKZYPuEXIXQZG8-0UFBlAStHWM85yYa4SjY/s1600/setUser.PNG" height="253" width="320" /></a>
<br />
<br />
然後會開出設定遠端桌面,這時候敲入使用者名稱、密碼、到期日
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV8BxJmHXw1arZM0-2hpyPMUs1eaok6YAVJZrdLXseWO0fwIzl1ENrsB2zOoFoFy6v4CttlmOCaReq0DsId6Q1ZatKymnbfjJaiRPfKKGcZfNeK5nr98pXsGQ4HMfN_2MoTD5Dclz79EE/s1600/setUser2.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhV8BxJmHXw1arZM0-2hpyPMUs1eaok6YAVJZrdLXseWO0fwIzl1ENrsB2zOoFoFy6v4CttlmOCaReq0DsId6Q1ZatKymnbfjJaiRPfKKGcZfNeK5nr98pXsGQ4HMfN_2MoTD5Dclz79EE/s1600/setUser2.PNG" height="270" width="320" /></a>
<br />
<br />
接著將Azure設定好的遠端連線(rdp)檔下載下來
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4WTp6nIqMKifHp7AXi9Z8XwyYM-22YQN3icG0D2yhyphenhyphenVw1jXCrSSJIDAO7i2OnxAEwK_4ALla6-VC6-v0YuS9zfoyapNVHMPy5y7NIHuBcRNSyVUpYoc88i-UtbR7vRiw6awSAgyBWoCA/s1600/getRdp.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4WTp6nIqMKifHp7AXi9Z8XwyYM-22YQN3icG0D2yhyphenhyphenVw1jXCrSSJIDAO7i2OnxAEwK_4ALla6-VC6-v0YuS9zfoyapNVHMPy5y7NIHuBcRNSyVUpYoc88i-UtbR7vRiw6awSAgyBWoCA/s320/getRdp.PNG" /></a>
<br />
<br />
帳號已經幫你預設好了,敲入密碼就可以連線進去了
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFxjFgDQjwzgJYg5swn288jyIsYT-FKdGGQQKlcgrWfez8lSzcxeF8LC1XFGGPy4GrAh5bGHwFlCvJ1T5CC7FKYo5_E9XgevhexOlYkaUm3byyUeCpPzfNkpFLDdT7iN74hyphenhyphenf5N0LKvyU/s1600/link.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFxjFgDQjwzgJYg5swn288jyIsYT-FKdGGQQKlcgrWfez8lSzcxeF8LC1XFGGPy4GrAh5bGHwFlCvJ1T5CC7FKYo5_E9XgevhexOlYkaUm3byyUeCpPzfNkpFLDdT7iN74hyphenhyphenf5N0LKvyU/s320/link.PNG" /></a>
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjarngkq4g-BG-kYaKWrKtAmHXHpyVR0GqfOJWGNXNqxconQGmc78TlnWiXXDsELCv3Fya5M2u7ruw02_i_x0oPLT8elCVr0MwJKM16sGrXC6N0B-wZ7NEXhcCyslrAFLe5xVqB2DLEqsU/s1600/In.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjarngkq4g-BG-kYaKWrKtAmHXHpyVR0GqfOJWGNXNqxconQGmc78TlnWiXXDsELCv3Fya5M2u7ruw02_i_x0oPLT8elCVr0MwJKM16sGrXC6N0B-wZ7NEXhcCyslrAFLe5xVqB2DLEqsU/s320/In.PNG" /></a>
<br />
<br />
參考:
<br />
<a href="http://www.tts.bz/archives/1329">微軟Azure測試心得分享(四) :啟用Virtual Machine (中)</a>
<br />
<a href="https://www.youtube.com/watch?v=AjXrPYzN0VI">遠端連線至 Windows Azure 雲端服務</a>
<br />
<br />
<br />
<br />
<span style="color: red;">補充:
</span><br />
透過VS去上傳專案,真的是有夠慢的,只更新DLL也要傳個老半天,雲端服務又不支援FTP,也不想安裝(或自己開發)檔案傳輸工具,透過<span style="color: blue;">David Kuo</span>的協助,原來可以<span style="color: red;">透過遠端桌面掛載本機磁碟</span>的方式達到網路硬碟的功效
<br />
<br />
利用Azure下載的遠端桌面檔,設定要分享的本機磁碟
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgihLb2Q4rdEXCF49dUFNeuRRK4HfnVukrHdEjNnkVCRjG6gCMtSO0Dtwi0Fw0BH4MVBYWZQXyVczrv84mE6qvPrIaj8G1Rn70Zj38XEgZGbO3Lim5pkGtVwxrVfmKVeFuty3cFuJWL1wc/s1600/share.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgihLb2Q4rdEXCF49dUFNeuRRK4HfnVukrHdEjNnkVCRjG6gCMtSO0Dtwi0Fw0BH4MVBYWZQXyVczrv84mE6qvPrIaj8G1Rn70Zj38XEgZGbO3Lim5pkGtVwxrVfmKVeFuty3cFuJWL1wc/s320/share.PNG" /></a>
<br />
<br />
進入後就可以在檔案總管看到我們分享進來的本機磁碟
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjutpXvzSC7v6vP4fKMPbv87PIcCpzTlI7Fc6wjH7E_1BzIL_iDeDKm8QIWzz_yl7beJ9Azx-eEb1TFtHeT255t1El6nd_55KKcwi2E_3dHWhr7EKZ25-robvAnidbPER8zJnBF2prwHVI/s1600/share1.PNG" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjutpXvzSC7v6vP4fKMPbv87PIcCpzTlI7Fc6wjH7E_1BzIL_iDeDKm8QIWzz_yl7beJ9Azx-eEb1TFtHeT255t1El6nd_55KKcwi2E_3dHWhr7EKZ25-robvAnidbPER8zJnBF2prwHVI/s320/share1.PNG" /></a>
<br />
<br />
這樣傳檔案就真的方便多了
<br />
<br />
參考:
<br />
<a href="http://softwaretocloud.blogspot.tw/2013/09/ms-azure.html">遠端桌面本機與遠端檔案互傳 (MS AZURE 適用)</a>KaiYaihttp://www.blogger.com/profile/06025811349700399894noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-41190115919350489022014-04-21T13:50:00.001+08:002014-04-21T13:50:52.334+08:00GData APIs ( Google Data APIs ) - Calendar個人通常記錄未來工作事項都是習慣使用紙本記錄,因為好處是隨身、任意塗改、做不止文字的記錄,但我相信還是有更多的人較喜歡使用 Google 日曆,除了可以記錄工作事項外,且可將事項分類、重要性、附檔、結合 Gmail....等好處多多 ( 請參閱: <a href="https://www.google.com.tw/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CC8QFjAA&url=http%3A%2F%2Fwww.playpcesor.com%2F2012%2F07%2Fgoogle_25.html&ei=1alUU8_eHoaikQW_-4HgDQ&usg=AFQjCNHQ6p8lZFHkMx0YD1OjIe-H4aiCzg&sig2=02NhM-rLvfTJdJi80FQhqw" rel="nofollow">Google日曆 最佳化:在忙碌瑣事中有效率掌握最重要行程</a>)。<br />
<br />
此篇文章只提供重要程式碼,去使用 C# Windows Form 來建置一個視窗程式與 Google 日曆互動。<br />
<br />
未下載 GData APIs 且未安裝請參考:<a href="http://readily-notes.blogspot.com/2014/04/gdata-apis-google-data-apis.html">GData APIs ( Google Data APIs ) 介紹與安裝</a>。<br />
<br />
<h2>引用參考</h2><pre class="code prettyprint">using Google.GData.Calendar;
using Google.GData.Client;
using Google.GData.Extensions;
</pre><br />
<h2>建立 CalendarService</h2><pre class="code prettyprint">service = new CalendarService(projectid);
service.setUserCredentials(username, password);
</pre><br />
若無 projectid,請到 <a href="https://console.developers.google.com/project">Google Developers Console</a> 申請。<br />
<br />
<h2>取得 使用者 所有日曆</h2><pre class="code prettyprint">public void GetUserCalendars()
{
FeedQuery query = new FeedQuery();
query.Uri = new Uri("http://www.google.com/calendar/feeds/default");
AtomFeed calFeed = service.Query(query);
for (int i = 0; i < calFeed.Entries.Count; i++)
{
// Console.WriteLine(calFeed.Entries[i].Title.Text);
// do something ...
}
}
</pre><br />
<h2>取得所有日曆事件</h2><pre class="code prettyprint">public AtomEntryCollection GetAllEvents()
{
EventQuery myQuery = new EventQuery(calendarURI);
EventFeed myResultsFeed = service.Query(myQuery) as EventFeed;
entries = myResultsFeed.Entries;
return entries;
}
</pre>calendarURI 為 https://www.google.com/calendar/feeds/default/private/full。<br />
<br />
<h2>取得日曆 日期區間 事件</h2><pre class="code prettyprint">public AtomEntryCollection GetDateRangeEvents(DateTime startTime, DateTime endTime)
{
EventQuery myQuery = new EventQuery(calendarURI);
myQuery.StartTime = startTime;
myQuery.EndTime = endTime;
EventFeed myResultsFeed = service.Query(myQuery) as EventFeed;
entries = myResultsFeed.Entries;
return entries;
}
</pre><br />
<h2>建立事件</h2><pre class="code prettyprint">public void CreateEvent(string title, string content, DateTime dtStart, DateTime dtEnd)
{
EventEntry entry = new EventEntry();
entry.Title.Text = title;
entry.Content.Content = content;
When eventTime = new When(dtStart, dtEnd);
entry.Times.Add(eventTime);
Uri postUri = new Uri(calendarURI);
AtomEntry insertedEntry = service.Insert(postUri, entry);
}
</pre><br />
<h2>更新事件</h2><pre class="code prettyprint">public void UpdateEvent(EventEntry entry)
{
Uri postUri = new Uri(calendarURI);
AtomEntry insertedEntry = service.Update(entry);
}</pre><br />
<h2>實作</h2>我用以上幾個函式寫了一個視窗程式,下圖為成品<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxzlu9ONd4D3U07PiSncvZdQE588iVwrOxlb5DMsldl6j0q6wzZZFTzQnVlPAA7QGlpsrJif_wd48Tm87K1TCsN_wbP1fe5T8djDpZj7eQfOecz9KtnBCKMccP9-bhUzAwIgtjc4CpY1I/s1600/gdata-apis-calendar-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxzlu9ONd4D3U07PiSncvZdQE588iVwrOxlb5DMsldl6j0q6wzZZFTzQnVlPAA7QGlpsrJif_wd48Tm87K1TCsN_wbP1fe5T8djDpZj7eQfOecz9KtnBCKMccP9-bhUzAwIgtjc4CpY1I/s400/gdata-apis-calendar-1.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1z9BLA5i-Z8qtJE0xnWmFDMIJS-ks3yFZuKPctwNdofE61ALaz5lJ4bYbpEmd0x1y4aO-eqKNDju3cz4X-wCxdMmQWGboMjS_JLHE9tuExXSMjLmFYvC221ZQkF4ir1CgM1Peb3lzyyA/s1600/gdata-apis-calendar-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1z9BLA5i-Z8qtJE0xnWmFDMIJS-ks3yFZuKPctwNdofE61ALaz5lJ4bYbpEmd0x1y4aO-eqKNDju3cz4X-wCxdMmQWGboMjS_JLHE9tuExXSMjLmFYvC221ZQkF4ir1CgM1Peb3lzyyA/s400/gdata-apis-calendar-2.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvKISJYO4ECFvXpzpKxSrQOvHdUiFjD1vAGPnFwAT0tbtFiuhacezStT87QIky4tOqbcZEBnlUhiiEadhc6J_DlkfdIEpBRhiVJsXDZ7RXjybC8gZ6nU-oBS3K16zlszQs5hjqaABJAgA/s1600/gdata-apis-calendar-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvKISJYO4ECFvXpzpKxSrQOvHdUiFjD1vAGPnFwAT0tbtFiuhacezStT87QIky4tOqbcZEBnlUhiiEadhc6J_DlkfdIEpBRhiVJsXDZ7RXjybC8gZ6nU-oBS3K16zlszQs5hjqaABJAgA/s400/gdata-apis-calendar-3.png" /></a></div><br />
<br />
David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com3tag:blogger.com,1999:blog-5188642618096096591.post-64421640756017271812014-04-13T20:47:00.000+08:002014-04-13T20:47:00.763+08:00GData APIs ( Google Data APIs ) - Blogger現在大部分的部落格都已經開放 API 讓開發人員能使用 API 對部落格做 CRUD 的動作,痞克邦、隨意窩、Blogger,甚至包括之前關站的無名小站皆有提供,接下來就開始使用 C# 來撰寫使用 Blogger 的程式。<br />
<br />
有許多人都使用 webBrowser 來模擬瀏覽器的動作發布文章,但是 blogger 無法讓你這樣做,你可以試試看,保證你抓不到 tag。 <br />
<br />
未下載 GData APIs 且未安裝請參考:<a href="http://readily-notes.blogspot.com/2014/04/gdata-apis-google-data-apis.html">GData APIs ( Google Data APIs ) 介紹與安裝</a>。<br />
<br />
<h2>引用參考</h2>使用 Google.GData.Client.dll 。<br />
<pre class="code prettyprint">using Google.GData.Client;
</pre><br />
<h2>建立 Service</h2>username 為帳號 ( Email ),password 為密碼,若帳號有設置兩步驗證的話,請到 <a href="https://accounts.google.com/b/0/IssuedAuthSubTokens?hl=zh_TW&hide_authsub=1">應用程式專用密碼</a> 去取得一組密碼,要不然原本帳號密碼是無法使用。<br />
<pre class="code prettyprint">service = new Service("blogger", "blogger-example");
service.Credentials = new GDataCredentials(username, password);
</pre><br />
<h2>取得文章列表</h2>max-results 為參數,讓回傳的文章數最大為 500 篇。<br />
<pre class="code prettyprint">/* 列出使用者的部落格 */
public Dictionary<string, Uri> GetListUserBlogs()
{
Dictionary<string, Uri> dicBlogs = new Dictionary<string,Uri>();
FeedQuery query = new FeedQuery();
query.Uri = new Uri("http://www.blogger.com/feeds/default/blogs");
AtomFeed feed = service.Query(query);
// 發佈文章
Uri blogPostUri = null;
if (feed != null)
{
foreach (AtomEntry entry in feed.Entries)
{
for (int i = 0; i < entry.Links.Count; i++)
{
if (entry.Links[i].Rel.Equals("http://schemas.google.com/g/2005#post"))
{
blogPostUri = new Uri(entry.Links[i].HRef.ToString() + "?max-results=500");
dicBlogs.Add(entry.Title.Text, blogPostUri);
}
}
// return blogPostUri;
}
}
return dicBlogs;
}
</pre><br />
<h2>新增文章</h2>其中 tags 變數為逗點分隔的字串,isDraft 為是否為草稿。<br />
<pre class="code prettyprint">/* 建立新文章且將它送到 blogPostUri */
public AtomEntry CreatePost(Uri blogPostUri, string title, string content, string tags, DateTime dt, bool isDraft)
{
AtomEntry createdEntry = null;
if (blogPostUri != null)
{
// construct the new entry
AtomEntry newPost = new AtomEntry();
newPost.Title.Text = title;
newPost.Content = new AtomContent();
newPost.Content.Content = content;
foreach (string term in tags.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries))
newPost.Categories.Add(new AtomCategory(term, new AtomUri("http://www.blogger.com/atom/ns#")));
newPost.Published = dt;
newPost.IsDraft = isDraft;
createdEntry = service.Insert(blogPostUri, newPost);
if (createdEntry != null)
{
Console.WriteLine(" New blog post created with title: " + createdEntry.Title.Text);
}
}
return createdEntry;
}
</pre><br />
<h2>更新文章</h2><br />
<pre class="code prettyprint">/* 編輯文章 */
public void EditEntry(AtomEntry toEdit)
{
if (toEdit != null)
{
// toEdit.Title.Text = "Marriage Woes!";
toEdit = toEdit.Update();
}
}
</pre><br />
<h2>刪除文章</h2><br />
<pre class="code prettyprint">/* 刪除文章 */
public void DeleteEntry(AtomEntry toDelete)
{
if (toDelete != null)
{
toDelete.Delete();
}
}
</pre><br />
<h2>實作</h2>我用以上幾個函式寫了一個視窗程式,下圖為成品<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlEo5_AL4Tyihrs_AR-9cmHZtzBT-AEVywrE9X1mSjfKE08qui0f-XYoUl1FDfXv2ozhH7Xoc4GYgiFJchxmPTvusEbVBCrEoXFxQ-0gSaBlo1p_1rQP6nxGLkUXjdulxPg3zrmKKCECE/s1600/GData-Blogger-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlEo5_AL4Tyihrs_AR-9cmHZtzBT-AEVywrE9X1mSjfKE08qui0f-XYoUl1FDfXv2ozhH7Xoc4GYgiFJchxmPTvusEbVBCrEoXFxQ-0gSaBlo1p_1rQP6nxGLkUXjdulxPg3zrmKKCECE/s400/GData-Blogger-1.png" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOlRmfkYFhrUCoXWJJCFkQerZ1mkNjrFQVogKEMcHoWIPBf3ne6hrscV2NNKn0VKrVUoVCPyRTP6NRKHla09pE5fAIbgZknrNhN7A8azjOR2b5lqq1BjreSiKfUrRjfA3vtvNMj50DlqI/s1600/GData-Blogger-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOlRmfkYFhrUCoXWJJCFkQerZ1mkNjrFQVogKEMcHoWIPBf3ne6hrscV2NNKn0VKrVUoVCPyRTP6NRKHla09pE5fAIbgZknrNhN7A8azjOR2b5lqq1BjreSiKfUrRjfA3vtvNMj50DlqI/s400/GData-Blogger-2.png" /></a></div>David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-25774841539346328292014-04-10T10:58:00.000+08:002014-04-10T10:58:00.483+08:00GData APIs ( Google Data APIs ) 介紹與安裝GData 是一種簡單的標準協議,用於網路資料的讀寫。它結合了常見的基於 xml 的數據聚合格式( Atom 與 RSS)以及基於 Atom 發布協議的 Feed 發布體系,並擴展了部分功能用於處理查詢功能。有時,我們需要發送一個查詢請求給服務器,並得到服務器返回的相符的查詢結果,而目前的 Atom 和 RSS 標準都不具備這一功能。GData 讓使用者可以使用聚合 ( syndication ) 的機制來發送請求並接收結果,它使你可以發送數據給 Google,更新那些 Google 已經擁有的資料。<br />
<br />
GData 擴展了原有的 RSS 和 Atom 協議,使其從一種單向的聚合變成了雙向的互動,這似乎是大家都在探索的 feed 的未來發展方向,比如微軟的 SSE。<br />
<br />
Joe Gregorio 認為,GData 是將RSS、Atom,尤其是 Atom 發布協議 ( Atom Publishing Protocol )與 Amazon 的 Openserch 標準相結合;Maurice Codik 認為 GData 標準使 Google 的數據更加開放,各種應用之間可以更方便地利用這一標準來使用數據;甚至有人認為這使得基於 Google 各種應用的企業門戶雛形開始顯現。<br />
<br />
引用:<a href="http://www.ddvip.com/mc/net/protocol/3381.html">什么是GData(Google Data API)</a><br />
<br />
<h2>安裝</h2>首先到 <a href="https://code.google.com/p/google-gdata/downloads/detail?name=Google_Data_API_Setup_2.2.0.0.msi&can=2&q=">google-gdata</a> 下載並安裝,安裝後路徑在 「 C:\Program Files (x86)\Google\Google Data API SDK\Redist 」,可以找到所有的 dll,往後可以找此路徑下的 dll 引用,而 「 C:\Program Files (x86)\Google\Google Data API SDK\Documentation\Google.GData.Documentation.chm 」 就是使用文件檔。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS9YMVwO2_YqBVtjsZBkA6jRcjXRJvsh3QVkI-UgUTvSwQbzGdRX7xb2pRnm2oViYh5k3Vcl3Sa_12kg_yZZM5k0Hc3NL8WDhM6Y-4YamkVuICHvhjygZ5QRPdNt4V271CYXrvyFO_LPk/s1600/GDataAPIs-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS9YMVwO2_YqBVtjsZBkA6jRcjXRJvsh3QVkI-UgUTvSwQbzGdRX7xb2pRnm2oViYh5k3Vcl3Sa_12kg_yZZM5k0Hc3NL8WDhM6Y-4YamkVuICHvhjygZ5QRPdNt4V271CYXrvyFO_LPk/s400/GDataAPIs-1.png" /></a></div><br />
此篇文章只是稍微介紹與安裝 GData APIs,往後會有系列文章介紹如何使用 dll。David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-82711989310319879552014-04-07T19:06:00.000+08:002014-04-07T19:06:00.053+08:00ASP.NET MVC 4 Controller 使用委派 ( delegate ) 做更進階的篩選委派是<a href="http://msdn.microsoft.com/zh-tw/library/awbftdfh.aspx" rel="nofollow">事件</a>的基礎。<br />
<br />
將委派與具名方法或匿名方法建立關聯,即可具現化 (Instantiated) 委派。 如需詳細資訊,請參閱<a href="http://msdn.microsoft.com/zh-tw/library/98dc08ac.aspx" rel="nofollow">具名方法</a>和<a href="http://msdn.microsoft.com/zh-tw/library/0yw3tz5k.aspx" rel="nofollow">匿名方法</a>。<br />
委派必須以具有相容傳回型別和輸入參數的方法或 Lambda 運算式具現化。 如需方法簽章中可允許之變異數等級的詳細資訊,請參閱<a href="http://msdn.microsoft.com/zh-tw/library/dd233060.aspx" rel="nofollow">委派中的變異數 (C# 和 Visual Basic)</a>。 若要搭配匿名方法使用,就要同時宣告委派以及其相關聯的程式碼。<br />
<br />
以上引用:<a href="http://msdn.microsoft.com/zh-tw/library/900fyy8e.aspx">delegate (C# 參考)</a><br />
<br />
方便處在於可以在類別上宣告後,即可在各個方法函數內使用,可以依照方法不同,而產生結果也不同,但是只用同一委派 ( delegate ) 實現。<br />
<br />
這裡使用北風資料庫來實作委派,沒有資料庫的話可以參考:<a href="http://readily-notes.blogspot.tw/2014/01/visual-studio-2012-northwind-entity.html">Visual Studio 2012 安裝 Northwind 資料庫並建立 Entity Framework Database First ( .edmx )</a>。<br />
<br />
在 Controller 中撰寫,傳入訂單編號來搜尋訂單下的訂單細項之產品的名稱,單位價格區間。<br />
<br />
先看看資料庫關聯表:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHSAQXkTo05cY9auCmWGyd1yAZR9a9lGjUnz_pp8Qq5lpeK77Gr9lqWuDZpa3CfikFKR_sYW3SR49iuecJ-mZ5XOZ_D-ZGIJ7c12YKTMk0ojmekyl6wub9KcbRS58lT0FOtFLMivEg2Lg/s1600/ASP.NET-MVC-4-delegate-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHSAQXkTo05cY9auCmWGyd1yAZR9a9lGjUnz_pp8Qq5lpeK77Gr9lqWuDZpa3CfikFKR_sYW3SR49iuecJ-mZ5XOZ_D-ZGIJ7c12YKTMk0ojmekyl6wub9KcbRS58lT0FOtFLMivEg2Lg/s400/ASP.NET-MVC-4-delegate-1.png" /></a></div><br />
先宣告委派,並且決定傳入傳出參數,關鍵字、最小價格、最大價格:<br />
<pre class="code prettyprint">delegate bool SearchProduct(Order_Details order_detail, string keyword, decimal mixprice, decimal minprice);
</pre><br />
類別內宣告委派:<br />
<pre class="code prettyprint">SearchProduct sp;
</pre><br />
最後在 Controller 實作並且使用委派:<br />
<pre class="code prettyprint">[HttpGet]
public IEnumerable<Order_Details> SearchProduct(int orderid, string keyword, decimal minprice, decimal maxprice)
{
sp = (o, k, min, max) =>
{
return o.Products.ProductName.Contains(k) ||
o.Products.UnitPrice >= min &&
o.Products.UnitPrice <= max;
};
var Order_Details = db.Order_Details
.Include(x => x.Products)
.Where(x => x.Orders.OrderID == orderid)
.ToList()
.Where(x => sp(x, keyword, minprice, maxprice));
foreach (Order_Details _order_detail in Order_Details)
{
_order_detail.Products.Order_Details = null;
}
return Order_Details;
}
</pre><br />
執行後呼叫 API - http://localhost:8090/api/Product/SearchProduct?orderid=10285&keyword=Ch&maxprice=18&minprice=17,得到結果:<br />
<pre class="code prettyprint">[
{
"OrderID": 10285,
"ProductID": 1,
"UnitPrice": 14.4,
"Quantity": 45,
"Discount": 0.2,
"Orders": null,
"Products": {
"ProductID": 1,
"ProductName": "Chai",
"SupplierID": 1,
"CategoryID": 1,
"QuantityPerUnit": "10 boxes x 20 bags",
"UnitPrice": 18,
"UnitsInStock": 39,
"UnitsOnOrder": 0,
"ReorderLevel": 10,
"Discontinued": false,
"Categories": null,
"Order_Details": null,
"Suppliers": null
}
}
]
</pre><br />
為何不在 LINQ 內使用條件判斷? 其實我已經實作過了,會出現「運算式樹狀架構可能不含指派運算子」錯誤,且必須要實例化出來才可以做,以下錯誤程式碼:<br />
<pre class="code prettyprint">[HttpGet]
public IEnumerable<Order_Details> SearchProduct(int orderid, string keyword, decimal minprice, decimal maxprice)
{
Products _product;
var Order_Details = db.Order_Details
.Include(x => x.Products)
.Where(x => x.Orders.OrderID == orderid &&
(
(_product = x.Products) != null &&
_product.ProductName.Contains(keyword) ||
_product.UnitPrice >= minprice ||
_product.UnitPrice <= maxprice
)
).ToList();
return Order_Details;
}
</pre>David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-41360301226927836372014-04-04T15:52:00.000+08:002014-04-04T15:52:00.674+08:00ASP.NET MVC 4 Model 中 Virtual 的作用在 View 端專寫時,顯示某一張表的資料,若未使用到相對應的關聯表,則不載入此表資料;若使用,則載入。這個作用就是延遲載入 ( Lazy Load ),使用再載入,很類似非同步的概念,而在 Model 關聯成員加上 Virtual 關鍵字,就可以做到延遲載入的作用。<br />
<br />
寫個例子來表示一下差別,資料庫使用北風資料庫利用 Database first 建置,可以參考 <a href="http://readily-notes.blogspot.tw/2014/01/visual-studio-2012-northwind-entity.html">Visual Studio 2012 安裝 Northwind 資料庫並建立 Entity Framework Database First ( .edmx )</a>。<br />
<br />
接著建置 CRUD 的介面,我是使用 Products 這張資料表建置,可以參考:<center><iframe width="420" height="315" src="//www.youtube.com/embed/53SjZumVTKM?t=2m2s" frameborder="0" allowfullscreen></iframe></center><br />
建置完成後,顯示頁面如下:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmuAw5Z1YSMQPt4VsPPBRXSktNm4zlvjKOSz6JvXUFEJ0-x6gDzHGbTAWmLtUrLqUnQLiZLEhyXLBTL6ch0TcY3ki08t5e7XlEBoYQWzcMzxgMJ1GfYaY6VwbQZfdzjoRgX_PBvcwikNw/s1600/ASP.NET-MVC-4-Virtual-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmuAw5Z1YSMQPt4VsPPBRXSktNm4zlvjKOSz6JvXUFEJ0-x6gDzHGbTAWmLtUrLqUnQLiZLEhyXLBTL6ch0TcY3ki08t5e7XlEBoYQWzcMzxgMJ1GfYaY6VwbQZfdzjoRgX_PBvcwikNw/s400/ASP.NET-MVC-4-Virtual-2.png" /></a></div><br />
Products Model 內容如下,建置出來就已經有 virtual :<br />
<pre class="code prettyprint">public partial class Products
{
public Products()
{
this.Order_Details = new HashSet<Order_Details>();
}
public int ProductID { get; set; }
public string ProductName { get; set; }
public Nullable<int> SupplierID { get; set; }
public Nullable<int> CategoryID { get; set; }
public string QuantityPerUnit { get; set; }
public Nullable<decimal> UnitPrice { get; set; }
public Nullable<short> UnitsInStock { get; set; }
public Nullable<short> UnitsOnOrder { get; set; }
public Nullable<short> ReorderLevel { get; set; }
public bool Discontinued { get; set; }
public virtual Categories Categories { get; set; }
public virtual ICollection<Order_Details> Order_Details { get; set; }
public virtual Suppliers Suppliers { get; set; }
}
</pre><br />
在 ProductsController 內 Get 程式碼如下:<br />
<pre class="code prettyprint">public ActionResult Index()
{
var products = db.Products;
return View(products.ToList());
}
</pre><br />
接著將 Products Model 內的關聯成員的 virtual 關鍵字拿掉後執行畫面如下:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqiI6slfS7gY03r2oQBCst8GNBCcHqkA6DOHFiu_DyTfOAOBSSvlNek_reBmvHO2WYyY91I9ADcnHcCOhaIGhQ-iTVvi3se_1dBde_DnR2KhBYnU4eno8XsLUQ0zPPJT7T6KngilBOZMQ/s1600/ASP.NET-MVC-4-Virtual-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqiI6slfS7gY03r2oQBCst8GNBCcHqkA6DOHFiu_DyTfOAOBSSvlNek_reBmvHO2WYyY91I9ADcnHcCOhaIGhQ-iTVvi3se_1dBde_DnR2KhBYnU4eno8XsLUQ0zPPJT7T6KngilBOZMQ/s400/ASP.NET-MVC-4-Virtual-1.png" /></a></div><br />
看出差別了嗎? CategoryName 不會顯示出來,我們回到 View 端看一下它的程式碼:<br />
<pre class="code prettyprint">@Html.DisplayNameFor(model => model.Categories.CategoryName)
</pre><br />
這就是所謂的延遲載入的差異,如果將 virtual 關鍵字拿掉,就使用到此關聯成員時,就不會載入;而若有 virtual 關鍵字,則會載入。在 Controller 端其實都沒有變,而是 virtual 使它變得更靈活。<br />
<br />
如果真的不加也有另外的做法,就是在資料讀取時,就關聯表的資料 Include 進來:<br />
<pre class="code prettyprint">public ActionResult Index()
{
var products = db.Products.Include(x => x.Categories);
return View(products.ToList());
}
</pre>David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-66188020811856916832014-04-01T10:39:00.000+08:002014-04-01T10:39:00.521+08:00ASP.NET MVC 4 遇到「參數 @objname 模稜兩可或是所宣告的 @objtype (COLUMN) 有誤。」 之解決方案今天 MVC 在做 code first 自動移轉時遇到「參數 @objname 模稜兩可或是所宣告的 @objtype (COLUMN) 有誤。」,如下圖<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBCf7KVyWsP8-Kc5KUqpKI1JT7mrf0i9MrAkAzTGJB6AVF_BJBPCdXuOiWcwN2N6QgRrkCtRcEGErjThMoIzbMA1Tn_hy7UA7oNcb4k-FH2aOi5LxyANTE129canRyZyGlGIZf7wgf5UQ/s1600/ASP.NET-MVC-4-Error-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBCf7KVyWsP8-Kc5KUqpKI1JT7mrf0i9MrAkAzTGJB6AVF_BJBPCdXuOiWcwN2N6QgRrkCtRcEGErjThMoIzbMA1Tn_hy7UA7oNcb4k-FH2aOi5LxyANTE129canRyZyGlGIZf7wgf5UQ/s400/ASP.NET-MVC-4-Error-1.png" /></a></div><br />
怪了,平常改動資料欄位,也不會產生錯誤,檢查了一下,我的程式碼只是將一對多關聯改成一對一 ( 以下為示意 ):<br />
<pre class="code prettyprint">public class A
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid AId { get; set; }
public string Name { get; set; }
// 修改前
// public ICollection<B> B { get; set; }
// 修改後
public B B { get; set; }
}
public class B
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid BId { get; set; }
// ...
}
</pre><br />
後來上網搜尋了一下, <a href="http://stackoverflow.com/questions/14848385/ef-codefirst-either-the-parameter-objname-is-ambiguous-or-the-claimed-objtype">EF CodeFirst: Either the parameter @objname is ambiguous or the claimed @objtype (COLUMN) is wrong</a> 有說明這是因為欄位在 rename 時發生錯誤,這不太可能吧!我應該只是將 B 的 A_AId 轉移到 A 的 B_BId,好像沒有 rename 的動作。<br />
<br />
接著我將它的移轉檔案叫出來,在「套件管理器主控台」使用指令 Add-Migration Test 將檔案產出,這才發現問題:<br />
<pre class="code prettyprint">public partial class Test : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.B", "A_AId", "dbo.A");
DropIndex("dbo.B", new[] { "A_AId" });
RenameColumn(table: "dbo.A", name: "A_AId", newName: "B_BId");
AddForeignKey("dbo.A", "B_BId", "dbo.B", "BId");
CreateIndex("dbo.A", "B_BId");
}
public override void Down()
{
DropIndex("dbo.A", new[] { "B_BId" });
DropForeignKey("dbo.A", "B_BId", "dbo.B");
RenameColumn(table: "dbo.A", name: "B_BId", newName: "A_AId");
CreateIndex("dbo.B", "A_AId");
AddForeignKey("dbo.B", "A_AId", "dbo.A", "AId");
}
}
</pre><br />
Up 在 Migration ( 轉移 ) 時,會執行,而如果發生錯誤則會使用 Down RollBack。<br />
<br />
問題就是發生在 Up 的第三行 - RenameColumn( ... ),在 A 資料表並無 B_BId 欄位,但居然要把 B_BId 命名成為 A_AId ? 所以這一看就是有問題,重新執行後問題依舊存在。<br />
<br />
所以建議先把一對多關連註解掉運行 API,再將一對一關聯加入在運行 API 後就會正常,或者你也可以就移轉檔案改動:<br />
<pre class=code prettyprint"">public override void Up()
{
DropForeignKey("dbo.B", "A_AId", "dbo.A");
DropIndex("dbo.B", new[] { "A_AId" });
DropColumn("dbo.B", "A_AId");
AddColumn("dbo.A", "B_BId", c => c.Guid());
AddForeignKey("dbo.A", "B_BId", "dbo.B", "BId");
CreateIndex("dbo.A", "B_BId");
}
</pre><br />
前者解決方法會比自己修改移轉檔案來得好,因為你還是要看程式碼找出錯誤在哪裡,比較花時間。David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0tag:blogger.com,1999:blog-5188642618096096591.post-17669128983834756072014-03-30T19:28:00.000+08:002014-03-30T19:28:00.556+08:00ASP.NET MVC 4 WebApi 與 Extjs 的結合 -- Message Box ( 訊息框 )通常使用一般網頁,填妥一張表格要送出,可能會將頁面導回主檔頁面,或者是在頁面上顯示文字說明送出完成,也可以像這篇主題一樣,彈出訊息讓使用者知道動作已經完成,可以按下確定,繼續下一個動作。<br />
<br />
<h2>訊息框</h2>首先就來建立一個基本的訊息框:<br />
<pre class="code prettyprint">Ext.MessageBox.alert('提示', '儲存成功');
</pre><br />
或者,在按下訊息框後再處理另外的指令,可以這樣處理:<br />
<pre class="code prettyprint">Ext.MessageBox.alert('提示', '儲存成功', callback);
function callback() {
alert('視窗關閉');
}
</pre><br />
執行後畫面如下:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivUIkMNlnoYAjhI28AqeH9Mks8sAGY035426dXQJdHWPYhoKx_vdJ_8u-Zw5Ymasywwl-TAfo4IsqLctikVGpxohoVmY2Txk61uGsXCCCBuCn8bQYCDsHlVaah-IJ6X3CKglMcO7phf00/s1600/extjs-messagebox-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivUIkMNlnoYAjhI28AqeH9Mks8sAGY035426dXQJdHWPYhoKx_vdJ_8u-Zw5Ymasywwl-TAfo4IsqLctikVGpxohoVmY2Txk61uGsXCCCBuCn8bQYCDsHlVaah-IJ6X3CKglMcO7phf00/s400/extjs-messagebox-1.png" /></a></div><br />
<h2>確認框</h2><pre class="code prettyprint">Ext.MessageBox.confirm("確認","確定儲存");
</pre><br />
執行後畫面如下:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj62iMdGUGeCqjQVK2EDMOaYIStOBLznpvpVdw7pymNRxBV8uBppLEKDH0t3B1X5VejtwCX6gD5LFVBnIExHYt567_ADO6z0aVych7aSbmNSGIRMTOPCpTrurDaOPIrYbgpwuB0sjIyFHo/s1600/extjs-messagebox-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj62iMdGUGeCqjQVK2EDMOaYIStOBLznpvpVdw7pymNRxBV8uBppLEKDH0t3B1X5VejtwCX6gD5LFVBnIExHYt567_ADO6z0aVych7aSbmNSGIRMTOPCpTrurDaOPIrYbgpwuB0sjIyFHo/s400/extjs-messagebox-2.png" /></a></div><br />
這裡可以結合小弟先前的 <a href="http://readily-notes.blogspot.tw/2014/02/aspnet-mvc-4-webapi-extjs_24.html">ASP.NET MVC 4 WebApi 與 Extjs 的結合 -- 動態複製表單並送出</a> 的文章,再送出時加上確認框,如果確認則送出,取消則不送出。按鈕結果狀態有二, yes 或 no。<br />
<br />
在送出表單時可以修改成:<br />
<pre class="code prettyprint">Ext.MessageBox.confirm("確認", "確定儲存", function (btn) {
if (btn == 'yes') {
Ext.getCmp('form').getForm().submit({
// 傳送到 url
url: 'http://localhost:8090/api/Product/Post',
method: 'POST'
});
}
});
</pre><br />
而 API 就會接收到 Form 來的訊息<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3cIk-cx_OBiIclliRTxXBF8eonxdWcxuu0Bwo7BGUTFbQXVhiZi_hy3LzlwJCRBnVnsJdxxKf0vi5IH7ezW4gYiA6uFsCjulveXjlMVaqnlkhdW-D74YqVI98oYsKlsU4LBCjTDIbmGo/s1600/extjs-messagebox-3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3cIk-cx_OBiIclliRTxXBF8eonxdWcxuu0Bwo7BGUTFbQXVhiZi_hy3LzlwJCRBnVnsJdxxKf0vi5IH7ezW4gYiA6uFsCjulveXjlMVaqnlkhdW-D74YqVI98oYsKlsU4LBCjTDIbmGo/s400/extjs-messagebox-3.png" /></a></div><br />
<h2>修改按鈕顯示文字</h2>上面的例子在彈出視窗後,按鈕文字都是英文的,如何修改按鈕顯示文字,透過以下指令:<br />
<pre class="code prettyprint">Ext.MessageBox.buttonText = {
ok: "確定",
cancel: "取消",
yes: "是",
no: "否"
};
</pre><br />
而確認框中,就會顯示:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivpK_mmn56-JFtWGrKdhyphenhyphen46CUib6CyCBY5qfTmjkinbTeNuMIm8o7hDAkvi11uoniEExEYkprXrDdg3HhgI4LPYDlXw143aTw9RACxBWEt8IWxSoeclRVO_Ofvpp8Kk5Zx9tGjiaRn_nc/s1600/extjs-messagebox-4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivpK_mmn56-JFtWGrKdhyphenhyphen46CUib6CyCBY5qfTmjkinbTeNuMIm8o7hDAkvi11uoniEExEYkprXrDdg3HhgI4LPYDlXw143aTw9RACxBWEt8IWxSoeclRVO_Ofvpp8Kk5Zx9tGjiaRn_nc/s400/extjs-messagebox-4.png" /></a></div><br />
<h2>輸入框</h2>輸入空可以用在密碼再次確認的情況,將密碼送回到 API 檢查,視使用者是否有權限進入下一頁。<br />
<pre class="code prettyprint">Ext.MessageBox.prompt("輸入", "您的密碼:", function (btn, text) {
});
</pre><br />
按鈕結果狀態有二,ok 與 cancel, btn 為選擇狀態;text 為輸入文字。<br />
<br />
<h2>自訂義訊息框</h2>參數設定如以下程式碼,稍微看一下註解應該就差不多了:<br />
<pre class="code prettyprint">Ext.MessageBox.show({
title: '標題',
msg: "自訂義訊息框",
// 寬度
width: 300,
// 多行輸入
multiline: true,
// 關閉按鈕
closable: false,
// 顯示圖示
icon: Ext.MessageBox.INFO,
// 按鈕型態
buttons: Ext.MessageBox.YESNOCANCEL,
fn: function(btn, text) {
}
});
</pre><br />
icon 可以設定成: INFO ( 訊息圖示 )、WARNING ( 警告圖示 )、QUESTION ( 詢問圖示 )、 ERROR ( 錯誤圖示 ) <br />
buttons 可以設定成: OK ( 確定 )、 CANCEL ( 取消 )、OKCANCEL( 確定和取消 )、YESNO ( 是和否 )、YESNOCANCEL ( 是和否和取消 )<br />
<br />
<h2>進度框</h2>進度框也是自訂義訊息框的一種,它只需要把屬性 progress 改為 true 就可以了。<br />
<br />
進度框可以使用 Ext.MessageBox.updateProgress 設定進度與文字,以下參考 <a href="http://jiangduxi.iteye.com/blog/1102991">ExtJs学习之弹出框,提示框,输入框等框</a>。<br />
<br />
<pre class="code prettyprint">Ext.MessageBox.show({
title: '上傳中',
msg: "正在上傳檔案...",
progressText: '正在初始化...',
// 寬度
width: 300,
// 關閉按鈕
closable: false,
// 顯示進度
progress: true,
fn: function(btn,text) {
}
});
var f = function (v) {
return function () {
if (v == 22) {
Ext.MessageBox.hide();
Ext.MessageBox.alert('完成', '所有項目上傳完成');
} else {
var i = v / 21;
Ext.MessageBox.updateProgress(i, Math.round(100 * i) + '% 已完成');
}
};
};
for (var i = 0; i < 23; i++) {
setTimeout(f(i), i * 500);
}
</pre><br />
執行後如下圖:<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIGi7fJ_kOjz3iPQSZ4aJpSHbLoJrDLTcdFBK9FMUqDL4mMG1LU213M9BpTh4tMqbz72VWwIoOCn7L8smugh4JBUbiNN_-05ViC0nE1rxjQMgrXHB10B0hB1dc-LlqpSs8Auxa0hVXzPw/s1600/extjs-messagebox-10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIGi7fJ_kOjz3iPQSZ4aJpSHbLoJrDLTcdFBK9FMUqDL4mMG1LU213M9BpTh4tMqbz72VWwIoOCn7L8smugh4JBUbiNN_-05ViC0nE1rxjQMgrXHB10B0hB1dc-LlqpSs8Auxa0hVXzPw/s400/extjs-messagebox-10.png" /></a></div><br />
參考與引用:<a href="http://jiangduxi.iteye.com/blog/1102991">ExtJs学习之弹出框,提示框,输入框等框</a>、<a href="http://www.blogjava.net/java-he/archive/2008/07/05/212765.html">ExtJS 入门学习之 messagebox篇</a>David Kuohttp://www.blogger.com/profile/15431572855600878682noreply@blogger.com0