2017年3月12日 星期日

如何擅用c# 的泛型 物件儲存oracle

當大家已經無法擺脫entityFramework的好處時,偏偏有些專案用一些特殊的資料庫或無法針對專案升級的老舊程式,此時只能自已下海寫傳統的SQL語法,但又不想每個欄位自已新增每個欄位的Parameter… 於是擅用c#的泛型就很重要囉! 因為只要擅用物件屬性,就讓它自已組SQL囉。

public bool OrcaleInsertData<T>(string tableName, T insertData) {
            OracleConnection oracleConn = (OracleConnection)Connection;
            try
            {
                if (oracleConn.State == ConnectionState.Closed)
                    oracleConn.Open();
                OracleCommand cmd = new OracleCommand();
                cmd.Connection = oracleConn;
                cmd.CommandType = CommandType.Text;
                StringBuilder strColumn = new StringBuilder();
                StringBuilder strSQLValue = new StringBuilder();
                Type type = typeof(T); //取得傳入物件的所有屬性
                List<OracleParameter> oracleParams = new List<OracleParameter>();
                //逐一的取物件的所有屬性值
foreach (PropertyInfo prop in type.GetProperties())
                {
                    object value = prop.GetValue(insertData);//逐一取得屬性值
                    if (value == null)
                        continue;
                    strColumn.Append(prop.Name + ","); //取得物件屬性名稱,視為資料庫的欄位名稱
                    strSQLValue.AppendFormat(":"+prop.Name + ",");//新增參數,ORACLE的參數為:
//如果物件的型態是允許NULL時,需要用以下的方式才能取得物件型態,否則只會囘傳nullable
                    var underlyingtype = Nullable.GetUnderlyingType(prop.PropertyType);
                    var reflactType = underlyingtype ?? prop.PropertyType;
                    OracleType columnType = getOrcaleType(reflactType.Name);//轉換型態                 
                    oracleParams.Add(new OracleParameter() {
                         ParameterName = prop.Name,
                         OracleType= columnType,
                         Value = prop.GetValue(insertData)//取得物件值
                    });
                }
                strColumn.Remove(strColumn.Length - 1, 1);
                strSQLValue.Remove(strSQLValue.Length - 1, 1);//remove the last,               
                cmd.Parameters.AddRange(oracleParams.ToArray());
                cmd.CommandText = string.Format("insert into {0} ( {1} ) values ({2})", tableName, strColumn.ToString(), strSQLValue.ToString());
                cmd.ExecuteNonQuery();
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
}

以下的範例只用來轉換型別,因為要針對Oracle的型別作轉換用途,目前只有簡單列出此專案所用到的欄位型態,如各位還有其他欄位型態,請自行增加囉~
     private OracleType getOrcaleType(string type) {
            switch (type.ToUpper()) {
                case "STRING":
                    return OracleType.Char;
                case "INT":
                    return OracleType.Int32;
                case "DECIMAL":
                    return OracleType.Double;
                case "DATETIME":
                    return OracleType.DateTime;
                case "BOOL":
                    return OracleType.Blob;
                default:
                    return OracleType.Char;
            }
        }



透過<T>就能傳入各種物件,再由Typeof(T)取得此物件的屬性和相對值,甚至屬性的型態都能清詳的列出,所以只需要把底層作好,以後不論任何物件都能呼叫此函式,多麼的方便~

 C# 本身也有推出OracleentityFramework的相關套件,但需要另外安裝…不過只支援到哪一版本entityFramework 本人就不清楚了,請各位自行研究囉!

2017年2月13日 星期一

簡易介紹TypeScript


如果大家對c#愛不釋手的話… 那TypeScript 就會立刻上手,因為TypeScript之父正是C# 的創始人,所以物件導向的類別、繼承、封裝、實作、介面…等更具有親和力的寫法,而且許多火紅的JavaScript (angular2, node.js, react.js ) 框架也都擁抱TypeScript ,所以我們好好瞭解TypeScript的奧妙吧!

一、介面
interface Person {
  firstname: string;
  lastname: string;
}

function greeter(person : Person) {
return "Hello, " + person.firstname + " " + person.lastname;
}

var user = {firstname: "Jane",lastname:"test"}; //如果不按照介面實作firstnamelastname 會失敗

document.body.innerHTML = greeter(user);  

TypeScript 允許靜態檢查型別,所以輸入的物件中缺少了某些屬性,會自動出現錯提示,如果想允許某些屬性是非強制性輸入值,可加上? 就不用強制檢查屬性了
interface Person {
   firstname: string;
   lastname ?: string;
}
var user = {
   firstname: "Jane"
};

二、繼承
class Animal {
name:string;
constructor(theName: string) { this.name = theName; }
     move(meters: number = 0) {
        alert(this.name + " moved " + meters + "m.");
    }
}
class Snake extends Animal {
constructor(name: string) { super(name); }
     move(meters = 5) {
        alert("Slithering...");
        super.move(meters);
    }
}

class Horse extends Animal {
constructor(name: string) { super(name); }
     move(meters = 45) {
        alert("Galloping...");
        super.move(meters);
    }
}
var sam = new Snake("Sammy the Python");
var tom: Animal = new Horse("Tommy the Palomino");                                                             
sam.move();
tom.move(34);

就如同物件導件的語法,透過繼承也擁有父類的特性,可以不斷的擴充新的類別,覆寫原父類別的方法,具有彈性的寫法,令人非常喜愛,當然有具有封裝屬性的功能(Publicprivate) ,如果有同樣繼承相同的父類別,還能互相給值例如: sam = tom , 但是如果繼承的類別有新增屬性,就會無法相互指派值。


三、泛型

如同c#的語法<T>,允許傳入各種不同的物件型別,不用一開始決定什麼類別, 有別早期的宣告any, 雖然也允許傳入任何的型別的值,但卻失去型別檢查的意義,但有了泛型仍保有原先傳入的型別,仍具有靜態檢查的優勢。
class Greeter<T> {
  greeting: T;
  constructor(message: T) {
    this.greeting = message;
  }
  greet() {
    return this.greeting;
  }
}

let greeter = new Greeter<string>("Hello, world");
let button = document.createElement('button');
button.textContent = "Say Hello";
button.onclick = function() {
    alert(greeter.greet());
}

document.body.appendChild(button);

以上是簡單介紹TypeScript的優點,但缺點就是需要編譯後才能執行javaScript,不過目前已有不少工具直接支援自動編譯,目前visual studio 2015visual Studio Code 已完全內建TypeScript


2017年2月9日 星期四

EntityFramework 常見問題

相信很多人對EntiyFramework 愛不釋手,但因為本人最近發生某些悲劇, 在不瞭解entityFramework 某些特性下,卻時容易踩到地雷… 所以先寫下來提醒自已囉!!!

一、設定linq SQL一樣的with(nolock)的語法

context.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");// like with(nolock) sql, avoid deadlock

二、設定延長讀取SQLTimeOut時間

context.Database.CommandTimeout = 1800;

三、EntityFramework 做了分頁處理,但仍然會出現SystemOutMemory ?

public override void DoJob()
{
context.Database.CommandTimeout = 1800;
context.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");// like with(nolock) sql, avoid deadlock

_db = new MallDbContext();
int totalCount = context.v_orderinfo4back.Count();
int settingPerCount = 1000000;

int totalPageingCount = countPaging(totalCount, settingPerCount);
var oders = (from b in context.v_orderinfo4back
select new
{
    ORDERID = b.ORDERID,
    SUPPLIERID = b.supplierid
}).AsEnumerable().Select(q=> new vOrderInfo4Back() {
    ORDERID = Convert.ToDouble(q.ORDERID),
    SUPPLIERID = Convert.ToDouble(q.SUPPLIERID)
});
for (int i = 0; i <= totalPageingCount - 1; i++)
{
    intSkipNumber = (i * settingPerCount);
    if (surplusCount < settingPerCount) 
     {
        settingPerCount = surplusCount;
        var orders = oders.OrderBy(q => q.CREATEDDATE).Skip(intSkipNumber).Take(settingPerCount).AsQueryable();
        surplusCount -= settingPerCount;
    }
}


有分頁處理,但該資料表將近1千萬筆,卻出現了SystemOutMemory等錯誤訊息,於是做了微幅調整


for (int i = 0; i <= totalPageingCount - 1; i++)
{
  intSkipNumber = (i * settingPerCount);
  if (surplusCount < settingPerCount)
   {
    settingPerCount = surplusCount;
    var oders = (from b in context.v_orderinfo4back.AsNoTracking().OrderBy(q =>
    q.CREATEDDATE).Skip(intSkipNumber).Take(settingPerCount)
    select new
    {
      ORDERID = b.ORDERID,
      SUPPLIERID = b.supplierid,
    }).AsEnumerable().Select(q => new vOrderInfo4Back()
    {
      ORDERID = Convert.ToDouble(q.ORDERID),
      SUPPLIERID = Convert.ToDouble(q.SUPPLIERID),
    }).AsQueryable();
    surplusCount -= settingPerCount;           
}

將實體資料分頁加上 AsNoTracking() 後就不會再出現systemOutMemory的錯誤訊息,傑克… 真神奇…

四、如何手動編修*.edmx

如果想手動修改 *.edmx的檔案,可按滑鼠右鍵-->開啟方式

 選擇XML 文件

五、如何取得驗證失敗的訊息


catch (DbEntityValidationException dbex)
                    {
                        foreach (var validationErrors indbex.EntityValidationErrors)
                        {
                            foreach (var validationError invalidationErrors.ValidationErrors)
                            {
                                Exception errorMsg = newException(validationError.PropertyName + ":" + validationError.ErrorMessage);
                                _log.WarnException("【大批維護商品】更新關聯商品時發生錯誤", errorMsg);
                            }
                        }
                    }

以上謝謝各位的參觀~