8/22/2013
2:54:00 PM 0

[Enterprise Library] Data Access Application Block - Query

最近在瀏覽網路文章時發現微軟的 Enterprise Library, 雖然不是什麼新東西(2005年就發表第一版), 目前的版本是 6.0(April 2013),但發現 Enterprise Library 竟然是 open source 的, 看了一些範例程式發現裡頭大量使用一些 Design Pattern 來簡化目前程式,激起了我研究他的興趣, 因為工作的關係現在比較少機會接觸到微軟的程式, 但他裡頭的設計方式是值得學習和研究的, 想要了解這 Library 第一步驟當然要先學會如何使用它, 先從連接資料庫的部分開始

首先 Refrences 加入 Microsoft.Practices.EnterpriseLibrary.Data

using directive
using Microsoft.Practices.EnterpriseLibrary.Data;
在不使用 LINQ 和 Entity Framework 的狀況下,如果要將資料庫裡的資料轉變成為強型別的物件

1. 在未使用 Enterprise Library 之前原本的做法
public List<Customer> FindCustById1(String connName, String id)
{
    String sql = @"SELECT * FROM CUSTOMERS WHERE CUSTOMERID = :ID";
    String connString = ConfigurationManager.ConnectionStrings[connName].ConnectionString;

    using (OracleConnection connection = new OracleConnection(connString))
    using (OracleCommand cmd = new OracleCommand(sql, connection))
    {

        connection.Open();

        cmd.Parameters.Add("ID", id);

        List<Customer> data = new List<Customer>();

        using (OracleDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
        {
            while (reader.Read())
            {
                Customer item = new Customer();

                item.CustomerId = reader["CUSTOMERID"] == DBNull.Value ? "" : reader["CUSTOMERID"].ToString();
                item.CompanyName = reader["COMPANYNAME"] == DBNull.Value ? "" : reader["COMPANYNAME"].ToString();
                item.Fax = reader["FAX"] == DBNull.Value ? "" : reader["FAX"].ToString();
                item.Address = reader["ADDRESS"] == DBNull.Value ? "" : reader["ADDRESS"].ToString()
                data.Add(item);
            }
        }
        return data;
    }
}
2. 使用 Enterprise Library,主要精簡了取 Connection 和 物件換的部分,將 Coonection 的建立和關閉封裝在 Database 物件內, 並利用反射將 reader 取得的資料放入 Customer 內
public List<Customer> FindCustById2(String connName, String id)
{
    String sql = @"SELECT * FROM CUSTOMERS WHERE CUSTOMERID = :ID";
    Database db = DatabaseFactory.CreateDatabase(connName);

    using (DbCommand cmd = db.GetSqlStringCommand(sql))
    {
        db.AddInParameter(cmd, "ID", DbType.String, id);

        List<Customer> data = new List<Customer>();

        using (IDataReader reader = db.ExecuteReader(cmd))
        {
            while (reader.Read())
            {
                Customer item = MapBuilder<Customer>.BuildAllProperties().MapRow(reader);

                data.Add(item);
            }
        }

        return data;
    }
}
3. 使用 Enterprise Library 進階版, 更進一步的將 reader 轉成物件的部分簡化了, 但是要多寫一個 class, C# 目前(2013年)似乎無法用匿名的方式 implements interface
public List<Customer> FindCustById3(String connName, String id)
{
    String sql = @"SELECT * FROM CUSTOMERS WHERE CUSTOMERID = :ID";
    Database db = DatabaseFactory.CreateDatabase(connName);

    DataAccessor<Customer> accessor = db.CreateSqlStringAccessor<Customer>(sql, new MyParameterMapper());
    IEnumerable<Customer> result = accessor.Execute(id);

    return new List<Customer>(result);
}
  
class MyParameterMapper : IParameterMapper
{
    public void AssignParameters(DbCommand command, object[] parameterValues)
    {
        DbParameter parameter = command.CreateParameter();
        parameter.ParameterName = ":ID";
        parameter.Value = parameterValues[0];
        command.Parameters.Add(parameter);
    }
}
Enterprise Library 的 Database 物件感覺跟 Spring 的 JdbcTemplate 有點類似, 兩者都將 connection 的開關封裝起來, 並可自定 RowMapper 物件, 建立轉換的規則

使用 Enterprise Library 查詢 Table 內全部資料, 在沒有查詢條件的狀況下程式碼更精簡了
public List<Customer> FindAllCust(String connectiopnName)
{
    String sql = @"SELECT * FROM CUSTOMERS";
    Database db = DatabaseFactory.CreateDatabase(connectiopnName);

    IEnumerable<Customer> result = db.ExecuteSqlStringAccessor<Customer>(sql);

    return new List<Customer>(result);
}
總結 : 使用 Enterprise Library 優點
1.簡化存取資料庫的程式碼
2.封裝 connction 開關動作,避免忘記關閉的情形
3.將 conneection 物件包裹起來, 避免程式碼和特定資料庫的 connection 實作產生依賴, 在使用標準的 ANSI SQL 情形下, 只要改改 config 就可以達到抽換不同種類資料庫的目標

Customer.cs
using System;

namespace TestEnterpriseLibrary
{
    class Customer
    {
        public String CustomerId 
        {
            get;
            set;
        }

        public String CompanyName
        {
            get;
            set;
        }

        public String Fax
        {
            get;
            set;
        }

        public String Address
        {
            get;
            set;
        }
    }
}
App.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connStrings>
    <add name="testdb" connString="Data Source=test;User Id=testdb;Password=testdb;" providerName="Oracle.DataAccess.Client" />
  </connStrings>
</configuration>
8/16/2013
1:56:00 PM 0

JavaScript function 的兩種宣告方式

寫法一
function subtract(a, b){
    return a - b;
};
寫法二
var subtract = function (a, b) {
    return a - b;    
};

這是兩種不同的 function 宣告方式

第一種寫法是 Function Declaration

產生 execute context 階段時 Function Declaration 就已存到 VO(Variable Object), 所以在執行階段(Execution Stage), 函式就已先存在, 造成的結果是程式呼叫的位置可以比程式宣告更前面
var result = subtract(1,2);

function subtract(a, b){
    return a - b;
};

第二種寫法是 Function Expression

Function Expression 寫法在執行階段才建立,並且不會被存在變數物件(VO)中, 可依照條件建立 Function Expression
var num = 11;

var test = (num % 2 == 0
    ? function () { alert("The number is even."); }
    : function () { alert("The number is odd."); }
);
  
test();

兩種寫法的差異只有在執行階段產生先後的差別, 而效能會根據所使用的 Javascript engine 而有不同的結果, 至於哪種寫法比較好? 我認為要根據個人習慣和程式碼的易讀和維護來做選擇了

國外高手做的 function declaration vs function expression 效能測試網頁, 有興趣可以玩玩看
http://jsperf.com/function-declaration-vs-function-expression

做個綜合練習
function outside1(){
    function inside1() {
        return 0;
    }
    return inside1();
    function inside1() {
        return 1;
    }
}
alert(outside1());


alert(outside2());
function outside2(){
    var inside2 = function() {
        return 0;
    };
    return inside2();
    var inside2 = function() {
        return 1;
    };
}


function outside3(){
    return inside3();
    var inside3 = function() {
        return 0;
    };
    var inside3 = function() {
        return 1;
    };
}
alert(outside3());
執行結果  1,0,TypeError: undefined is not a function

網路上看到一個有趣的例子 http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);
執行結果是1

這例子直覺上應該是印出10, 因為 function b 第一行就改變了全域變數 b, 這例子利用 Function Declaration  在執行階段前就已產生的特性, 讓人稍不注意就產生了混淆

原因在前面有說過 function Declaration 會造成執行階段前函式就已存在, 所以處理 function b 的 function context 時, 區域變數 a 就已經先被建立起來, 而這時此變數指向一個函式,當執行階段處理 a = 10; interpreter 會先檢查目前的 execute context 有沒有這個變數, 結果發現了 function a, 因此把 10 指定給他, 所以 a = 10, 是改變了區域變數 a  而不是全域變數的 a, 在 a = 10 前後加上 alert 就可證明這一點

這下面這種狀況就不可使用 Function Declaration 寫法
if (true) {
  function sayHello() {
    return 'Hello 1';
  }
}
else {
  function sayHello() {
    return 'Hello 2';
  }
}
sayHello();
必須使用 Function Expression 的寫法
var sayHello;
if (true) {
  sayHello = function() {
    return 'Hello 1';
  };
}
else {
  sayHello = function() {
    return 'Hello 2';
  };
}
sayHello();
8/14/2013
3:03:00 PM 0

Javascript execution context

什麼是 execution context ?

一個 execution context 包含了一些資訊, 而這些資訊在程式執行期間是必要的, 而這些資訊包含
  • LexicalEnvironment
  • VariableEnvironment
  • ThisBinding
當 Javascript 程式執行時, 會產生 execution context, 每段可執行的程式都有其對應的 execution context, 運作中的 execution contexts 像一個 logical stack, 而最上層的 Stack 就是正在執行的 execution context


產生 Execution Context 依照程式的不同,而會有三類不同的狀況

 1. Entering Global Code
var i = 0;
function test(){console.log("test");}


2. Entering Function Code
function factorial(num)
{
    if (num < 0) return -1;
    else if (num == 0) return 1;
 
    var result = num;
    while (num-- > 2) {
        result *= num;
    }
    return result;
}

factorial(10);
第一次呼叫 factorial


遞迴呼叫(第二次之後), 要注意的是每次 Return 時會退出當時的 execution context, 也就是會從 stack pop 出去, 所以遞迴呼叫並不會造成 stack 一直往上堆


 3. Entering Eval Code
當 eval 執行, 前一個 active execution context, 會被當成 calling context 的參考
> eval('var x = 22');
undefined
> x
22
> this.x
22
> window.x
22
> eval('(function() {var y=33; console.log(y);})()');//前一個 active execution context 是匿名 function
33
undefined
> y
ReferenceError: y is not defined

參考資料
http://www.ecma-international.org/ecma-262/5.1/
8/02/2013
4:53:00 PM 0

Javascript Prototype Chain

Javascript 使用 prototype 來達到類似其他語言的繼承概念, 要了解 Javascript 的繼承, 首先要了解 prototype chain, 當我們建立一個 Javascript 物件, 並使用 Javascript 屬性時, 會先從物件內部所擁有的屬性開始尋找, 如果找不到屬性的名稱,就會從 prototype 所指向的物件繼續搜尋, 一層一層的往內搜尋, 這就是原型鏈搜尋

首先我們使用 function 來建構一個物件 (Object Constructor)
function Earth(){};
接著建立一個物件實例(Object Instances)
var e1 = new Earth();
建立實例的過程中不單單是設定 name 屬性, 還把 Employee 所繼承的物件帶給 e1

new 的動作相當於
    var e1 = {};
    e1.__proto__ = Earth.prototype;//__proto__  Non-standard
    Earth.call(e1);
使用 Chrome Developer Tools 可看出 e1 的屬性和方法










測試 e1.constructor (關於 constructor 參考 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor)
> e1.constructor
[Function: Earth]
e1 沒有 constructor 屬性, 所以往內部查找 __proto__ 正好有這屬性, 所以輸出 __proto__.constructor, 由此可推論  e1.constructor == e1.__proto__.constructor
> e1.constructor == e1.__proto__.constructor
true
> Earth.prototype.constructor == e1.constructor
true
從上面的例子可看出 prototype 屬性會決定繼承的物件, 改變 prototype 屬性後, 再 new 出來的物件繼承關係也跟著改變


接下來說明 instanceof, instanceof 在 Java 中也有這個功能, 作用也類似,
Javascript 的 instanceof 會從 prototype chain 查找, 是否包含建構物件的 prototype, MDN 中有實做出類似 instanceof  內部運作的方式, 可參考 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model?redirectlocale=en-US&redirectslug=JavaScript%2FGuide%2FDetails_of_the_Object_Model#Determining_instance_relationships

做個綜合測試
> function X(){}
undefined
> X.constructor //所有 function 都是 Function object
function Function() { [native code] }
> X.prototype.constructor //參考到 X
function X(){}
> var o1 = new X()
undefined
> o1 instanceof X
true
> o1.constructor //等於 o1.__proto__.constructor
function X(){}
> X.prototype = [] //改變 X.prototype 參考到陣列
[]
> var o2 = new X()
undefined
> o1 instanceof X // 相當於 o1.__proto__ == X.prototype
false
> X.prototype.isPrototypeOf(o1);
false
> o2 instanceof X // 相當於 o2.__proto__ == X.prototype
true
> o1 instanceof Array
false
> o2 instanceof Array
true
> X.constructor
function Function() { [native code] }
> X.prototype.constructor
function Array() { [native code] }
> o1.constructor
function X(){}
> o2.constructor
function Array() { [native code] }
所有 function 都是 Function object 的實例, 所以 X.constructor 指向 Function, X.prototype.constructor 指向 X,
建立物件會沿著 prototype chain 尋找, 因此
 var o1 = new X();
相當於
    var o1 = {};
    o1.__proto__ = X.prototype;//reference X
    X.call(o1);
而 X.prototype = []; 改變了 prototype 的參考對象, 因此 X 的 prototype chain 改變了
    var o2 = {};
    o2.__proto__ = X.prototype;//reference Array
    X.call(o2);
要怎麼讓 o1 instanceof X 變成 true 呢? 只要讓 o1.__proto__ 和 X.prototype 一致就行了
> o1.__proto__ = X.prototype;
[]
> o1 instanceof X;
true
> X.prototype.isPrototypeOf(o1);
true