星期二, 10月 14, 2014

entity framework 通用欄位更新小技巧

今天跟大家分享小技巧,有做過專案的人一定會遇到,有些專案的 DB 一定會開幾個必有的欄位,例如:
CreateDateTime (新增時間)
CreateBy (新增人員)
UpdateDateTime (更新時間)
UpdateBy (更新的人)
IsEanbed (是否可用)
像這些欄位,其實蠻討厭的,因為在每次的新增修改都要去處理..

//Create 
var db = new MyDB();
var user = Help.GetUser();
db.Users.Add(new User(){
 CreateDateTime = DateTime.Now,
 CreateBy = user.Id,
 UpdateDateTime = DateTime.Now,
 UpdateBy = User.Id,
 IsEanbed = true
});
db.SaveChanges();

//Update
var db = new MyDB();
var user = Help.GetUser();
var dbUser = db.Users.First();
db.UpdateDateTime = DateTime.Now;
db.UpdateBy = User.Id;
db.SaveChanges();

像這些 code 就很討厭,所以我的作法就在最後的 SaveChange 這個 method 前統一處理,可以參考下列網址 http://msdn.microsoft.com/zh-tw/library/vstudio/cc716714%28v=vs.100%29.aspx
public override int SaveChanges(SaveOptions options)
{
    foreach (ObjectStateEntry entry in ObjectStateManager
      .GetObjectStateEntries(EntityState.Added | EntityState.Modified))
    {
 // Validate the objects in the Added and Modified state
 // if the validation fails throw an exeption.
 //create
 var cShadow = new
 {
     CreateDateTime = DateTime.Now,
     CreateBy = userInfo.ID,
     UpdateDateTime = DateTime.Now,
     UpdateBy = userInfo.ID,
     IsEanbed = true
 };
 var createdData = Context.ObjectStateManager
                     .GetObjectStateEntries(EntityState.Added);
 foreach (var item in createdData)
 {
     item.Entity.InjectFrom(cShadow);
 }
 
 //update
 var mShadow = new
 {
     UpdateDateTime = DateTime.Now,
     UpdateBy = userInfo.ID
 };
 var modifiedData = Context.ObjectStateManager
                     .GetObjectStateEntries(EntityState.Modified);
 foreach (var item in modifiedData)
 {
     item.Entity.InjectFrom(mShadow);
 }
 
 return base.SaveChanges(options);
}

還有我有用 ValueInjecter 這自動注入的 library,是因為它可以幫我們自動反射注入,反射這部分就不用自己做了,大概就這樣,這小技巧,分享一下!

Reference
http://msdn.microsoft.com/zh-tw/library/vstudio/cc716714%28v=vs.100%29.aspx
http://valueinjecter.codeplex.com/

星期六, 3月 29, 2014

TypeScript 界面(四)

上一篇談了 TypeScript 的函式,今天來聊聊 TypeScript 的界面(Interface),使用界面是撰寫物件導向程式中很入門磚,界面就像是對使用者訂立了契約,有定義就必須要實作(Implement),界面屬於高階的抽象,所以程式耦合性低,程式不會緊緊黏在一起,程式容易維護也不易出錯,TypeScript 提供了界面這個重要的特性,讓 JavaScript 可以用強型別的概念來撰寫程式,用例子來說明吧..

首先來個最簡單的例子

function Test(obj: { name: string }) {
    console.log(obj.name);
}

var myObj = { name: "Bibby" };
Test(myObj);

var myObj1 = { name: "Bibby", age: 18 };
Test(myObj1);

這個例子裡面,我限制 obj 一定要丟入一個有 name 屬性的一個物件,所以 myObj 跟 myObj1 都是可以當參數來丟進來的,上列的例子只是一種界面宣告的方法,TypeScript 有提供一個關鍵字來宣告界面,修改一下上面的例子

interface IPerson {
    name: string;
}

function Test(obj: IPerson) {
    console.log(obj.name);
}

var myObj = { name: "Bibby" };
Test(myObj);

var myObj1 = { name: "Bibby", age: 18 };
Test(myObj1);

使用 interface 的關鍵字,命名一個 IPerson 的界面,這樣可以做到跟上一個例子的一樣效果,不過注意一下,這裡的 myObj 跟 myObj1 只要屬性跟界面所要求的一樣就可以被允許,而不需要直接的實作(implement),這跟其他強型別的語言是不同的

可選屬性(Optional Properties)

interface IPerson {
    name: string;
    age?: number;
}

function Test(obj: IPerson) {
    console.log(obj.name);

    if (obj.age) {
        console.log(obj.age);
    }
}

var myObj = { name: "Bibby" };
Test(myObj);

var myObj1 = { name: "Bibby", age: 18 };
Test(myObj1);

看一下 IPerson 的 age 這個屬性,加個問號(?)代表這個屬性不一定要實作,這個特性可以讓你的界面設計更靈活,不過要小心點使用這個特性,所實作的程式也記得判斷是否有這屬性,個人建議是不要大量使用,可以考慮把 option 的 method 再拉一個界面出來來代替

函式(Function Type)

剛剛有提到,界面就像契約一樣,來看一下怎麼實作在函式上

interface IShow {
    (a1: string, a2: string): void;
}

var func: IShow = (a, b) => {
    console.log(a, b);
};

func("Bibby", "123");
func("Bibby", 123); // get errors

陣列(Array Type)

也可以實作在陣列上

interface IStringArray {
    [index: number]: string;    
}

interface INumberArray {
    [index: number]: number;
}

var myStringArray: IStringArray = ["Bibby", "Ruby"];
var myNumberArray: INumberArray = [123, 456];

型別(Class)

如果你有寫過物件導向的語言,這應該是最熟悉的用法了

interface IPerson {
    name: string;
    age: number;
    birthday: Date;
    print(): void;
}

class Person implements IPerson {

    name: string;
    age: number;
    birthday: Date;

    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    print() {
        console.log(this.name);
        console.log(this.age);
        console.log(this.birthday);
    }

}

var b = new Person("Bibby", 22);
b.birthday = new Date(2010, 10, 10);
b.print();

偷懶一點可以這樣寫

interface IPerson {
    name: string;
    age: number;
    birthday: Date;
    print(): void;
}

class Person implements IPerson {

    birthday: Date;

    constructor(public name: string, public age: number) { }

    print() {
        console.log(this.name);
        console.log(this.age);
        console.log(this.birthday);
    }
}

var b = new Person("Bibby", 22);
b.birthday = new Date(2010, 10, 10);
b.print();

用建構式的快速寫法把屬性宣告出來

延伸界面(Extending Interfaces)

在上面了例子,有提到把 option 的 method 再拉一個界面出來來代替,就是如下寫法

interface IColor {
    color: string;
}

interface ISolid {
    description: string;
}

interface ISquare extends ISolid {
    length: number;
}

class Square implements ISquare, IColor {
    description: string;
    length: number;
    color: string;
}

var square = new Square();
square.description = "red";
square.length = 10;

方法很簡單,就是用逗點(,)還實現多重實作的方式

最後提一下,如果你想要你的界面是不能被實作,可以用下面的方法

interface INotImplementInterface {
    new (hour: number, minute: number);
}

class Clock {
    currentTime: Date;
    constructor(h: number, m: number) { }
}

var clock: INotImplementInterface = Clock;
var newClock = new clock(7, 30);

這裡在 INotImplementInterface 用 new 這關鍵字來作為建構式的限制,也是這個原因就不能拿來實做了,但是還是可以拿來當約束界面用

TypeScript 的界面(interface)這部份就講到這了,下一篇來聚焦物件導向中,最好玩的類別(class) ^^

Reference
http://www.typescriptlang.org/
http://typescript.codeplex.com/wikipage?title=Interfaces%20in%20TypeScript&referringTitle=Documentation

星期三, 1月 22, 2014

TypeScript 函式 (三)

之前我們談過了 TypeScript 的型別了,今天來談談 TypeScript 的函式(Function)。Bibby 在學 JavaScript 的過程中,函式是我最早使用到,一開始就只用函式處理前端的簡單運算,然後在 HTML 裡呼叫,在網際網路還很單純的年代,這樣寫就很無敵了..

<input type="submit" value="submit" onclick="confirm('確定')" />

function confirmMsg(msg){
    if(confirm(msg)){
        return true;
    }
    return false
}

不過時代的變遷,前端的複雜度已經超乎想像,以前的寫法不是說不能用,是如果專案很大,很多人一起共同開發,或是前端的程式越來越複雜,這種寫法就不適合,模組化(module)的需求及立即函式(IIFE)寫法就開始會用在專案上了,這部份之後再花時間討論,今天來看一下 TypeScript 函式的部份

命名函式(named function) 和匿名函式(anonymous function)
 
在 TypeScript 裡,函式有命名函式(named function)跟匿名函式(anonymous function),這跟 JavaScript 是一樣的

//named function
function Hello(name) {
    console.log("Hello, " + name);
}
//anonymous function
var myHello= (name) => {
    console.log("Hello, " + name);
};

不過在寫 TypeScirpt 函式時,通常不會這樣寫,因為 TypeScript 是有型別的,應該要把型別加進去,讓程式更嚴謹一點,會在參數還有回傳值部份加入型別,這樣 TypeScript 編譯器在儲存時就會去檢查型別是否如預期的這樣

//named function
function Hello(name: string): void {
    console.log("Hello, " + name);
}
//anonymous function
var myHello= (name: string): void => {
    console.log("Hello, " + name);
};

但是 TypeScript 編譯器是很聰明的,型別推斷會自行判斷回傳的型別為何,所以實務通常不會在所有地方加上型別

function Hello(name: string) {
    console.log("Hello, " + name);
}
var myHello = (name: string) => {
    console.log("Hello, " + name);
};
var myHello1: (name: string) => void = (name) => {
    console.log("Hello, " + name);
};

可選(option)和預設(default)參數

接下來,來看一下參數這一部份,一般的寫法

function Test(param1: string, param2: string, param3: string) {
    console.log(param1 + param2 + param3);
}
Test("AAA", "BBB", "CCC");

在 C# 或是一些近代的語言,對於函式的參數都有可選(option)和預設(default)這些便利的特性,TypeScript 也有提供,如果要使用可選(option)參數的方式,可以使用關鍵字 "?" 這樣寫

function Test(param1: string, param2: string, param3?: string) {
    if (param3)
        console.log(param1 + param2 + param3);
    else
        console.log(param1 + param2);
}
Test("AAA", "BBB", "CCC");
Test("AAA", "BBB");

那如果要用預設值(default)的方式,直接在參數後面加上 "=" 多少值

function Test(param1: string, param2: string, param3= "123") {
    console.log(param1 + param2 + param3);
}
Test("AAA", "BBB", "CCC");
Test("AAA", "BBB");

Rest Parameters
 
有時候,在設計函式時是,因為需要靈活而無法估計有多少參數的,C# 可以用 params 的前贅詞來使用,JavaScript 可以用 arguments 來處理,在 TypeScript 裡也有支援這種方式,關鍵是在參數加上前贅詞"..."來宣告參數,型別為 Array

function GoGoGo(param1: string, param2: string, ...paramN: string[]) {
    console.log(param1, paramN.join(""));
}
GoGoGo("AAA", "BBB", "CCC","DDD");
GoGoGo("AAA", "BBB", "CCC");
GoGoGo("AAA", "BBB");

多載(overload)
 
JavaScript 的函式裡,沒有型別的概念,參數要怎麼給都行,所以本質裡就是多載(overload)的感覺,只需要在函式內部處理好各個參數型別和是否有輸入的問題就行,在 TypeScript 裡,寫法會有點點不同,會把各個預期的參數輸入值先宣告,讓最後一個函式做統一處理

function GoGoGo(x: number, ...y: number[])
function GoGoGo(x: string)
function GoGoGo(x: { Name: string; Age: number })
function GoGoGo(x: any, ...y: number[]) {
    if (typeof x == "number") {
        var all = 0;
        for (var i = 0; i < y.length; i++) {
            all += y[i];
        }
        console.log(x + all);
    }

    if (typeof x == "string") {
        console.log(x);
    }

    if (typeof x == "object") {
        console.log("Name:" + x.Name + ", Age:" + x.Age);
    }
}
GoGoGo(1, 3, 5, 7, 9);
GoGoGo("TestTest");
GoGoGo({ Name: "Bibby", Age: 21 });

以上就是 TypeScript 對於函式的一些特性,TypeScript 在函式的部份,原則上跟原生的 JavaScript 沒有相差太多,對於函式的寫法一些改良,加入一些特性,讓程式寫的更不容易出錯也更直覺。

下一篇,來聊聊 TypeScript 的 Interface

Reference
http://www.typescriptlang.org/

星期四, 1月 09, 2014

TypeScript 型別 (二)

TypeScript 相遇 (一) 這篇文章,提到了使用 TypeScript 來寫 JavaScript 的優點好處,也知道TypeScript 是一個有型別概念的語言,所以今天就從型別(Type)來討論 TypeScript

在討論 TypeScript 的型別前,先看一下 JavaScript 的內建型別(PredefinedType)
  • String
  • Number
  • Boolean
  • Object
  • Array
  • Null
  • Undefined
有寫 JavaScript 應該對對上列這些型別不陌生,稍微比較一下 TypeScirpt 的內建型別(PredefinedType)
  • String
  • Number
  • Boolean
  • Any
  • Void
跟 JavaScript 差不多,在寫 TypeScript 這些型別一定會常用到,接下來,用簡單的範例來介紹

String

var name: string = "Bibby";
name = 'Bibby1';

String 是最常用到的型別,上列範例是一個宣告為 string 的 name 的變數,用 (") 或是 (') 都行,TypeScrip Compiler 都會幫我們處理,但是寫法建議還是還是統一一種

Number

var height: number = 6; 
height = 3.2; 

宣告為 number 的 height 變數,在 TypeScript 裡面,Number 型別都為浮點數

Boolean

var isDone: boolean = false; 

宣告為 boolean 的 isDone 變數,只能塞入 true / false

Array 

var list: number[] = [1, 2, 3]; 

在 TypeScript 裡面,Array 的宣告方式,只要在型別的後面加上 [] 就行了,也可以用另外一種方式來達成

var list1: Array<number> = [1, 2, 3]; 

這種宣告方式比較像 C# 裡面的泛型

Any 

var a1: any = 123; 
a1 = "StringString"; 
a1 = false; 

由例子可以知道,any 型別可以塞入不同型別的內容,通常在使用 Thrird-Party 的 Library 或想要寫出比較「活」的語法時,這時 Any 就可以派上用場了,不過小心 Any 的使用方式,它會讓你程式靈活,也會讓你程式混亂

var list: any[] = [1, true, "free"]; 
list[1] = 100; 

any 也可以放在 array 上面的,讓 array 塞入任何值

Void 

var a1 = (): void=> { 
    alert("test"); 
}; 
function a2(): void { 
    alert("test1"); 
} 

Void 對比於 Any,Void 是不會回傳任何內容的
內建基本型別就是上列這些,接下來再看一下 TypeScript 另外提供的

Enum 

enum Color { Red, Green, Blue }; 
var c: Color = Color.Green; 

基本的 Enum 用法

enum Color { Red = 1, Green, Blue }; 
var c: Color = Color.Green; 

Enum 預設為 0,你也可以給預設的數字

enum Color { Red = 1, Green = 2, Blue = 4 }; 
var c: Color = Color.Green; 

或是全部手動給它加上去

//=====javascript=====
var Color; 
(function (Color) { 
Color[Color["Red"] = 0] = "Red"; 
Color[Color["Green"] = 1] = "Green"; 
Color[Color["Blue"] = 2] = "Blue"; 
})(Color || (Color = {})); 
; 
var c = 1 /* Green */; 

上列就是 TypeScirpt 編譯出來的 JavaScript

TypeScript 的 Enum 跟 C# 的 Enum 用法幾乎一樣,有 Enum 使用,就可以寫出更好維護的 Code 了

另外再提一點,寫 TypeScript 的時候不一定都要宣告型別,TypeScript 會很聰明的使用型別推斷(Type inference)的方式來猜出型別,例如下面這一段

var a1 = 2; 
a1 = "Bibby"; 

一開始使型別是 Number,之後變成 String,這樣 TypeScript 的編譯器就會「該該叫」了,跟你說不要惡搞 ^^||

以上就是 TypeScript 的基本型別以及基本用法,下一篇文章,再來談談關於 TypeScript 的裡面的 Functions

Reference:
http://msdn.microsoft.com/en-us/library/7wkd9z69%28v=vs.94%29.aspx
http://zh.wikipedia.org/wiki/Javascript
http://www.typescriptlang.org/