這是一個用來記錄 Log的Class,只需要設定 filePath 大致上就可以運作了
public static class TraceLog
{
private static object _lockLog = new object();
private static int _numberOfLines = 0;
public static string filePath = @"C:\MyService_LOG_DIR";
public static string serviceName = "MyService"; // 服務名稱
public static int maxLines = 65536; // Log 行數限制
public static int traceLevel = 3; // value is 1~9
public static void Write(int pTraceLevel, string pSource, string message)
{
if (pTraceLevel <= traceLevel)
Write(pSource + ":" + message);
}
public static void Write(string format, params object[] message)
{
Write(string.Format(format, message));
}
public static void Write(string message)
{
if (string.IsNullOrEmpty(filePath))
{
filePath = Directory.GetCurrentDirectory();
}
// 選擇檔案名稱
string filename = filePath + string.Format("\\{0:yyyy}\\{0:MM}\\{1}.{0:yyyy-MM-dd}.txt",
DateTime.Now,
serviceName);
FileInfo finfo = new FileInfo(filename);
if (finfo.Directory.Exists == false)
{
finfo.Directory.Create();
}
if (_numberOfLines == 0)
if (File.Exists(filename))
_numberOfLines = File.ReadAllLines(filename).Length;
else
_numberOfLines = 0;
if (_numberOfLines >= maxLines)
{
// 檔案夾搬移
for (int i = 10; i > 0; i--)
{
string currFile = filename.Substring(0, filename.Length - 4) + "." + i.ToString() + ".txt";
if (File.Exists(currFile))
{
if (i == 10)
{
File.Delete(currFile);
continue;
}
FileInfo f = new FileInfo(currFile);
f.MoveTo(currFile.Substring(0, filename.Length - 4) + "." + (i + 1).ToString() + ".txt");
}
}
// 重新計算行數
finfo.MoveTo(filename.Substring(0, filename.Length - 4) + ".1.txt");
_numberOfLines = 0;
}
// 訊息格式化
string writeString = string.Format("{0:yyyy/MM/dd HH:mm:ss.ff} {1} {2}",
DateTime.Now,
serviceName + "." + System.Threading.Thread.CurrentThread.ManagedThreadId,
message) + Environment.NewLine;
// 寫Log前,先Lock
lock (_lockLog)
{
File.AppendAllText(filename, writeString, Encoding.Unicode);
}
_numberOfLines++;
}
}
2017年8月23日 星期三
Windows 服務
適用於 vs2012
Step 1. 檔案>>新增>>專案,選擇 [Windows服務]
Step 2. 點選 Service1.cs的 Design模式,按右鍵 -> Add Installer (加入安裝程式)
Step 3. 點選 serviceInstaller1,這裡需要做一些設定
1) DisplayName 就是服務要顯示的名稱
2) Description 服務描述
3) ServiceName 服務的唯一名稱
4) StartType 啓動方式,初始值爲Manual (手動),當然要設定Automatic (自動) 呀
5) DelayedAutoStart 爲開機之後是否延遲啓動,設定為False
Step 4. 點選 serviceProcessInstaller1
設定只要改一個,Account 這裡決定服務的帳號與權限,我們可以設定爲LocalSystem (就是最大權限)
Step 5. 點選 Service1.cs 相關初始程式碼
裏頭主要包含了服務 OnStart,以及 OnStop,也就是服務開啟與結束所執行的動作
以下範例中的 TraceLog 是小太陽自行撰寫的紀錄Log Class,這個Class在這裡
public Service1()
{
InitializeComponent();
this.AutoLog = false; // 因為沒有使用,所以關閉
}
protected override void OnStart(string[] args)
{
TraceLog.Write("Service OnStart");
}
protected override void OnStop()
{
TraceLog.Write("Service OnStop");
}
Step 6. 接下來就是對程式進行編譯 [F6]
Step 7. 開啟 cmd視窗,必須要是[適用於 VS2012 的開發人員命令提示字元]
安裝服務
C:\MyService\bin\Debug>InstallUtil MyService.exe
解安裝服務
C:\MyService\bin\Debug>InstallUtil /u MyService.exe
開啟服務,這邊輸入的是 Step3 所設定的ServiceName
C:\MyService\bin\Debug>net start MyService
停止服務
C:\MyService\bin\Debug>net stop MyService
例外狀況:
Issue 1. 如果 InstallUtil 出現 「不是有效的 Win32 應用程式」,那是因為安裝的環境的問題
1) 請確認編譯時,專案屬性中的 平台目標設為 x86(或Any CPU);目標 Framework 設為 .Net Framework4 以下
2) 確認機器有安裝所需的.Net Framework 版本
Issue 2. 如果沒有 Visual Studio 命令提示字元,必須使用cmd 到指定目錄下執行
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>InstallUtil.exe "C:\MyService\bin\Debug\MyService.exe"
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>net start MyService
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>net stop MyService
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>InstallUtil.exe /u "C:\MyService\bin\Debug\MyService.exe"
Step 1. 檔案>>新增>>專案,選擇 [Windows服務]
Step 2. 點選 Service1.cs的 Design模式,按右鍵 -> Add Installer (加入安裝程式)
Step 3. 點選 serviceInstaller1,這裡需要做一些設定
1) DisplayName 就是服務要顯示的名稱
2) Description 服務描述
3) ServiceName 服務的唯一名稱
4) StartType 啓動方式,初始值爲Manual (手動),當然要設定Automatic (自動) 呀
5) DelayedAutoStart 爲開機之後是否延遲啓動,設定為False
Step 4. 點選 serviceProcessInstaller1
設定只要改一個,Account 這裡決定服務的帳號與權限,我們可以設定爲LocalSystem (就是最大權限)
Step 5. 點選 Service1.cs 相關初始程式碼
裏頭主要包含了服務 OnStart,以及 OnStop,也就是服務開啟與結束所執行的動作
以下範例中的 TraceLog 是小太陽自行撰寫的紀錄Log Class,這個Class在這裡
public Service1()
{
InitializeComponent();
this.AutoLog = false; // 因為沒有使用,所以關閉
}
protected override void OnStart(string[] args)
{
TraceLog.Write("Service OnStart");
}
protected override void OnStop()
{
TraceLog.Write("Service OnStop");
}
Step 6. 接下來就是對程式進行編譯 [F6]
Step 7. 開啟 cmd視窗,必須要是[適用於 VS2012 的開發人員命令提示字元]
安裝服務
C:\MyService\bin\Debug>InstallUtil MyService.exe
解安裝服務
C:\MyService\bin\Debug>InstallUtil /u MyService.exe
開啟服務,這邊輸入的是 Step3 所設定的ServiceName
C:\MyService\bin\Debug>net start MyService
停止服務
C:\MyService\bin\Debug>net stop MyService
例外狀況:
Issue 1. 如果 InstallUtil 出現 「不是有效的 Win32 應用程式」,那是因為安裝的環境的問題
1) 請確認編譯時,專案屬性中的 平台目標設為 x86(或Any CPU);目標 Framework 設為 .Net Framework4 以下
2) 確認機器有安裝所需的.Net Framework 版本
Issue 2. 如果沒有 Visual Studio 命令提示字元,必須使用cmd 到指定目錄下執行
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>InstallUtil.exe "C:\MyService\bin\Debug\MyService.exe"
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>net start MyService
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>net stop MyService
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319>InstallUtil.exe /u "C:\MyService\bin\Debug\MyService.exe"
2017年5月26日 星期五
C# WebAPI 與 Angular2 透過 Http.post 傳遞參數做資料新增
之前一篇 提到,透過 http.get 來進行資料的 Query,Query 參數主要是透過網址列來傳遞。
而為了符合 RESTful 架構風格,因此在新增資料時,我們使用的是 http.post
承接上一篇的例子,在這邊我們仍以 ValuesController.cs 作為例子
Step 1. 同樣的步驟,我們在必須先建一個類別來接收 http.post 傳遞過來的參數,這邊我們命名為 MyData
public class MyData
{
public string fab { get; set; }
public string shop { get; set; }
public string date { get; set; }
public string qty { get; set; }
}
Step 2. 將 POST預設的 code 標註起來,因為傳遞的參數不是一的字串(string),而是剛剛建立的 MyData 類別。
Http.post 就是從Post Body 取得參數,前面加的是[FromBody]。function 裡的內容就是透過 SQL 語法去將資料存到資料庫裡。
//// POST api/values
//public void Post([FromBody]string value)
//{
//}
// POST api/values
public void Post([FromBody]MyData value)
{
Console.Write(value);
}
前端 Angular2的部分也很簡單,幾乎只是將原本的 get 改成 post 再多傳遞一個變數"body"
Step 3. 我們在網頁上多加一個按鈕,讓這個按鈕觸發 save()
save(){
let options = new RequestOptions();
let headers = new Headers({ 'Content-Type': 'application/json',
'Accept': 'q=0.8;application/json;q=0.9' });
options.headers = headers;
let body = {
'fab': 'Fab01',
'shop': 'Shop01',
'date': '2017/05/26',
'qty': 100
}
let url = '/api/values/';
this._http.post(url,body, options)
.subscribe(next => console.log('ok'));
}
如此,即完成 Http.post 的實作。
這裡可以透過 Chrome 瀏覽器,打開 F12 編輯視窗,選擇 Network 可以看到傳送出去的http body如下:
Request Payload:
{ "fab": "Fab01", "shop": "Shop01", "date": "2017/05/26", "qty": 100 }
另外也可以 Fiddler 來查看
而為了符合 RESTful 架構風格,因此在新增資料時,我們使用的是 http.post
承接上一篇的例子,在這邊我們仍以 ValuesController.cs 作為例子
Step 1. 同樣的步驟,我們在必須先建一個類別來接收 http.post 傳遞過來的參數,這邊我們命名為 MyData
public class MyData
{
public string fab { get; set; }
public string shop { get; set; }
public string date { get; set; }
public string qty { get; set; }
}
Step 2. 將 POST預設的 code 標註起來,因為傳遞的參數不是一的字串(string),而是剛剛建立的 MyData 類別。
Http.post 就是從Post Body 取得參數,前面加的是[FromBody]。function 裡的內容就是透過 SQL 語法去將資料存到資料庫裡。
//// POST api/values
//public void Post([FromBody]string value)
//{
//}
// POST api/values
public void Post([FromBody]MyData value)
{
Console.Write(value);
}
前端 Angular2的部分也很簡單,幾乎只是將原本的 get 改成 post 再多傳遞一個變數"body"
Step 3. 我們在網頁上多加一個按鈕,讓這個按鈕觸發 save()
save(){
let options = new RequestOptions();
let headers = new Headers({ 'Content-Type': 'application/json',
'Accept': 'q=0.8;application/json;q=0.9' });
options.headers = headers;
let body = {
'fab': 'Fab01',
'shop': 'Shop01',
'date': '2017/05/26',
'qty': 100
}
let url = '/api/values/';
this._http.post(url,body, options)
.subscribe(next => console.log('ok'));
}
如此,即完成 Http.post 的實作。
這裡可以透過 Chrome 瀏覽器,打開 F12 編輯視窗,選擇 Network 可以看到傳送出去的http body如下:
Request Payload:
{ "fab": "Fab01", "shop": "Shop01", "date": "2017/05/26", "qty": 100 }
另外也可以 Fiddler 來查看
2017年4月25日 星期二
建立 C# WebAPI 與 Angular2 做 Http.get 溝通
因為這個部分是小太陽一開始學習 Angular2 首要想完成的地方,中間當然遇到了許多難關,一一的克服後,撰寫這篇文章希望對大家有所幫助!
本範例是前端網頁傳遞多個Query參數,到後端Web API 搜尋資料庫後,將 DataTable 回傳到前端的範例。
Step 1. 建立 C# Web API (vs2012)
1) 新增專案 >> Web >> ASP.NET MVC 4 WEB 應用程式
2) 範本選取 Web API
Web API專案完成建立後,會預設建立一個 ValuesController.cs,我們以此Controller 來進行範例。
Step 2. 因為要傳遞多個參數,所以會用一個類別將參數接起來,所以這邊會建一個 QueryParams 類別,用來接參數。
public class QueryParams
{
public string fab { get; set; }
public string shop { get; set; }
public string date { get; set; } // YYYY/MM/DD - YYYY/MM/DD
}
Step 3. Http.get 是透過 URL 來傳遞參數,所以在接參數的前面加一個 [FromUri],如果是 Http.post 就是從Post Body 取得參數,前面加的是[FromBody]。本範例就不著墨在DBConnector上面了,小太陽用的是 Oracle.ManagedDataAccess。
// GET api/values
//public IEnumerable<string> Get()
//{
// return new string[] { "value1", "value2" };
//}
public DataTable Get([FromUri]QueryParams queryParams)
{
DBConnector db = new DBConnector("MyDB");
string strSQL = @"select '{0}', '{1}', '{2}' from dual";
strSQL = string.Format(strSQL, queryParams.fab
, queryParams.shop
, queryParams.date);
string errorStr = "";
DataTable dt = db.selectSQL(strSQL, out errorStr);
return dt;
}
接下來是前端 Angular2的部分,假設你已經透過這篇建好專案了 ng2 建立新專案
並且解決 Cross Domain問題 解決 ng2 的 Cross Domain 問題
Step 4. 那麼我們開始在 app.component.ts 來呼叫 Http.get 吧
import { Component, OnInit } from '@angular/core';
import {URLSearchParams, Headers, Http, RequestOptions} from '@angular/http';
@Component({
selector: 'app-root',
templateUrl: `
<h1>
<pre>{{data|async|json}}</pre>
</h1>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
data: any;
constructor(private http: Http){}
ngOnInit(){
let options = new RequestOptions();
let headers = new Headers({ 'Content-Type': 'application/json',
'Accept': 'q=0.8;application/json;q=0.9' });
options.headers = headers;
let params: URLSearchParams = new URLSearchParams();
params.set('fab', 'myFab');
params.set('shop', 'myShop');
params.set('date', '2017/04/24');
options.search = params;
let url = '/api/values/';
this.data = this.http.get(url,options).map(response => response.json());
}
}
如果是使用 Chrome 瀏覽器,打開 F12 編輯視窗,選擇 Network 可以看到傳送出去的網址如下:
這時候在Web API 中設中斷點就可以看到傳進來的參數。
而Angular2 的畫面也會將回傳回來的 DataTable轉為 json放到 data 變數裡,下面是最後網頁呈現的結果:
本範例是前端網頁傳遞多個Query參數,到後端Web API 搜尋資料庫後,將 DataTable 回傳到前端的範例。
Step 1. 建立 C# Web API (vs2012)
1) 新增專案 >> Web >> ASP.NET MVC 4 WEB 應用程式
2) 範本選取 Web API
Web API專案完成建立後,會預設建立一個 ValuesController.cs,我們以此Controller 來進行範例。
Step 2. 因為要傳遞多個參數,所以會用一個類別將參數接起來,所以這邊會建一個 QueryParams 類別,用來接參數。
public class QueryParams
{
public string fab { get; set; }
public string shop { get; set; }
public string date { get; set; } // YYYY/MM/DD - YYYY/MM/DD
}
Step 3. Http.get 是透過 URL 來傳遞參數,所以在接參數的前面加一個 [FromUri],如果是 Http.post 就是從Post Body 取得參數,前面加的是[FromBody]。本範例就不著墨在DBConnector上面了,小太陽用的是 Oracle.ManagedDataAccess。
// GET api/values
//public IEnumerable<string> Get()
//{
// return new string[] { "value1", "value2" };
//}
public DataTable Get([FromUri]QueryParams queryParams)
{
DBConnector db = new DBConnector("MyDB");
string strSQL = @"select '{0}', '{1}', '{2}' from dual";
strSQL = string.Format(strSQL, queryParams.fab
, queryParams.shop
, queryParams.date);
string errorStr = "";
DataTable dt = db.selectSQL(strSQL, out errorStr);
return dt;
}
接下來是前端 Angular2的部分,假設你已經透過這篇建好專案了 ng2 建立新專案
並且解決 Cross Domain問題 解決 ng2 的 Cross Domain 問題
Step 4. 那麼我們開始在 app.component.ts 來呼叫 Http.get 吧
import { Component, OnInit } from '@angular/core';
import {URLSearchParams, Headers, Http, RequestOptions} from '@angular/http';
@Component({
selector: 'app-root',
templateUrl: `
<h1>
<pre>{{data|async|json}}</pre>
</h1>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
data: any;
constructor(private http: Http){}
ngOnInit(){
let options = new RequestOptions();
let headers = new Headers({ 'Content-Type': 'application/json',
'Accept': 'q=0.8;application/json;q=0.9' });
options.headers = headers;
let params: URLSearchParams = new URLSearchParams();
params.set('fab', 'myFab');
params.set('shop', 'myShop');
params.set('date', '2017/04/24');
options.search = params;
let url = '/api/values/';
this.data = this.http.get(url,options).map(response => response.json());
}
}
如果是使用 Chrome 瀏覽器,打開 F12 編輯視窗,選擇 Network 可以看到傳送出去的網址如下:
Request URL:
http://localhost:4200/api/values/?fab=myFab&shop=myShop&date=2017/04/24
這時候在Web API 中設中斷點就可以看到傳進來的參數。
而Angular2 的畫面也會將回傳回來的 DataTable轉為 json放到 data 變數裡,下面是最後網頁呈現的結果:
[
{
"'MYFAB'": "myFab",
"'MYSHOP'": "myShop",
"'2017/04/24'": "2017/04/24"
}
]
2017年3月23日 星期四
Web Service 傳遞自定物件
測試了一下,如何使用 Web Service 傳定自定的物件
1) Web Service 端
Step 1. 先自定一個物件,名叫 Hero,設定為可序列化類別
一定要加入 public Hero() { } 這行,不然會出現「無法序列化 XXX.Hero,因為它沒有參數建構函式。」的錯誤
[Serializable]
public class Hero
{
public int id { get; set; }
public string name { get; set; }
public Hero() { }
public Hero(int id, string name)
{
this.id = id;
this.name = name;
}
}
Step 2. 撰寫 Web Service 方法
[WebMethod]
public List<Hero> GetHeroes()
{
List<Hero> lstHero = new List<Hero>();
lstHero .Add(new Hero(1, "Kyle"));
lstHero .Add(new Hero(2, "Paul"));
return lstHero ;
}
2) Web Client 端
上面的 Web 參考名為 localhost,類別為 WebService1
用下面這兩段就可以將 Hero 物件接回去了
localhost.WebService1 CallWebService = new localhost.WebService1();
localhost.Hero[] heroes = CallWebService.GetHeroes();
後續也可以將 localhost.Hero[] 轉回為自定的物件 Hero
List<Hero> lstHero = new List<Hero>();
for (int i = 0; i < heroes.Length; i++)
{
lstHero.Add(new Hero(heroes[i].id, heroes[i].name));
}
1) Web Service 端
Step 1. 先自定一個物件,名叫 Hero,設定為可序列化類別
一定要加入 public Hero() { } 這行,不然會出現「無法序列化 XXX.Hero,因為它沒有參數建構函式。」的錯誤
[Serializable]
public class Hero
{
public int id { get; set; }
public string name { get; set; }
public Hero() { }
public Hero(int id, string name)
{
this.id = id;
this.name = name;
}
}
Step 2. 撰寫 Web Service 方法
[WebMethod]
public List<Hero> GetHeroes()
{
List<Hero> lstHero = new List<Hero>();
lstHero .Add(new Hero(1, "Kyle"));
lstHero .Add(new Hero(2, "Paul"));
return lstHero ;
}
2) Web Client 端
上面的 Web 參考名為 localhost,類別為 WebService1
用下面這兩段就可以將 Hero 物件接回去了
localhost.WebService1 CallWebService = new localhost.WebService1();
localhost.Hero[] heroes = CallWebService.GetHeroes();
後續也可以將 localhost.Hero[] 轉回為自定的物件 Hero
List<Hero> lstHero = new List<Hero>();
for (int i = 0; i < heroes.Length; i++)
{
lstHero.Add(new Hero(heroes[i].id, heroes[i].name));
}
2017年2月21日 星期二
一些基本的程式應用
1) 在一串數字中將重複的刪除
bool bIsDup = false;
int[] iRawData = { 0, 3, 5, 3, 2, 1, 0 };
List<int> lstAns = new List<int>();
for (int i = 0; i < iRawData.Count(); i++)
{
bIsDup = false;
foreach (var a in lstAns)
if (iRawData[i] == a)
{
bIsDup = true;
break;
}
if (!bIsDup)
lstAns.Add(iRawData[i]);
}
iRawData = lstAns.ToArray();
2) 在一個數組裡找出兩個數,使得他們的和為給定的數值 (例如7)
int[] iRawData = { 0, 3, 5, 3, 2, 1, 0, 9, 8, 3, 2, 1, 7, 6, 5};
int iAns = 7;
List<KeyValuePair<int,int>> lstAns = new List<KeyValuePair<int,int>>();
for (int i = 0; i < iRawData.Count(); i++)
{
for (int j = 0; j < iRawData.Count(); j++)
{
if (iRawData[i]+iRawData[j] == iAns)
lstAns.Add(new KeyValuePair<int,int>(iRawData[i],iRawData[j]));
}
}
3)
bool bIsDup = false;
int[] iRawData = { 0, 3, 5, 3, 2, 1, 0 };
List<int> lstAns = new List<int>();
for (int i = 0; i < iRawData.Count(); i++)
{
bIsDup = false;
foreach (var a in lstAns)
if (iRawData[i] == a)
{
bIsDup = true;
break;
}
if (!bIsDup)
lstAns.Add(iRawData[i]);
}
iRawData = lstAns.ToArray();
2) 在一個數組裡找出兩個數,使得他們的和為給定的數值 (例如7)
int[] iRawData = { 0, 3, 5, 3, 2, 1, 0, 9, 8, 3, 2, 1, 7, 6, 5};
int iAns = 7;
List<KeyValuePair<int,int>> lstAns = new List<KeyValuePair<int,int>>();
for (int i = 0; i < iRawData.Count(); i++)
{
for (int j = 0; j < iRawData.Count(); j++)
{
if (iRawData[i]+iRawData[j] == iAns)
lstAns.Add(new KeyValuePair<int,int>(iRawData[i],iRawData[j]));
}
}
3)
訂閱:
文章 (Atom)
