2013/12/2

[ASP.NET] 在 App_Code 下存取外部 Assembly

App_Code 是一個 ASP.NET 網站專案的特殊子目錄。如果你的專案不是 Web Site 專案而是 Web Application 專案, 你並不需要、也不應該特別建立一個 App_Code 子目錄來存放你的程式碼 -- 除非是為了某種特殊的目的

例如, 如果你希望幫網站加入動態產生版本的功能的話, 那麼你可以建立 App_Code 子目錄 (在這裡都使用 Web Application 專案), 並且在這個子目錄下隨便建立一個如下的類別檔:

using System;
using System.Web;
using System.Web.UI;
using System.Web.Configuration;
using System.Web.Compilation;
using System.CodeDom;
using System.Reflection;

[assembly: AssemblyVersion("0.9.*")]
namespace DbModel
{
    public class Helper
    {
         ...
    }
}

然後, 你可以使用以下程式碼取出以上類別檔的動態版本號碼, 當作的網站的版本號碼:

using System;
using System.Reflection;
using DbManager;
using System.Threading;

...
Assembly web = Assembly.Load("App_Code");
AssemblyName webName = web.GetName();
lbVersion.Text = string.Format("網站版本: {0}.{1}.{2}", webName.Version.Major, webName.Version.Minor, webName.Version.Build);
//lbVersion.Text += webName.Version.ToString();

如此一來, 在 lbVersion 處顯示的網站版本, 就會依照 0.9.* 的格式逐次編號 (例如 0.9.5084.24242), 而且每次網站編譯後, 它的版本號碼都會比上一次大, 所以可以作為每次發行時的版號參考依據。

照理說, 對於 Web Application 專案而言, App_Code 的作用應該即止於此。

然而, 如果你想擴充這個類別的功能, 難道不行嗎?

我剛好就是這樣想的。我就把 App_Code 當作其它子目錄一樣用了; 我在上面那個 Helper 類別中加上了許多功能, 直到遇到問題為止。什麼問題呢? 我突然發現它無法存取參考進來的 dll。

例如, 我在同一個方案中建立了一個另一個 DbModel 專案, 並且已經把參考加入了網站的 reference, 但是偏偏只有在 App_Code 之下的  Helper 類別無法存取那個 reference。甚至, 如果我在程式開頭加上 using DbModel, 那麼即使編譯能過, 在執行時它還是發出一個找不到參考的錯誤! 看起來, 這個 App_Code 在 Web Application 專案裡確實仍然是一個特殊的子目錄!

其實我只需要把程式移到 App_Code 之外就行了。但是我發揮了鍥而不捨的精神, 非把它克服為止, 所以才有這篇文章的出現。

我要做的事情並不複雜, 我只想要取出 DbModel 命名空間之下某個類別的某個欄位的預設值而已。

於是我花了一點時間去研究和 trial and error, 發現我可以把那個外部 dll 載入, 再設法呼叫它的欄位值, 以下就是我的程式碼 (寫在 Helper 類別裡面):

Assembly a = Assembly.Load("DbModel");
Type myType = a.GetType("DbModel.MyData");
FieldInfo field = myType.GetField("MyString");
string myString = (string)field.GetValueDirect(__makeref(myType));
return myString;

在上述程式中, 我使用了 GetField 去取出欄位。如果你想取出的不是欄位的話, 你也可以使用 GetProperty 和 GetMethod 方式取出屬性和方法; 而方法則可以再使用 Invoke 方法去予以呼叫。

不過上述程式中最困難的技巧在於 GetValueDirect 方法。我發現我只能使用 __makeref 這個 undocumented 函式才能產生一個 TypedReference 物件以作為參考。雖然說使用 undocumented 函式並不是值得推薦的方法, 但是我實在已經找不到其它更好的方法了。

以上僅供參考。

沒有留言:

張貼留言