樂透下期號碼預測(3)-完結

為甚麼沈寂了這麼久才寫這篇,那是因為我發現,樂透是不可預測的。其實有學過統計學的我早應該知道了。
如果你還是想試試看預測,那麼可以參考這一系列排列組合的文章,應該會很有幫助:

Porting guide

今天看到jpobst這篇文章:Porting Guide
他說,他上星期在Mono wiki上寫了一篇Guide: Porting Winforms Applications,內文提到如何將NClass移植到 Linux Mono 上的過程。
整個移植的過程相當簡單,首先利用MoMA (Mono Migration Analyzer)去分析NClass,得知NClass使用了哪些類別,而這些類別(內部的method)在Mono裡面是否已經被實做、有沒有使用 P/Invoke 等技術等等,接著再去補足Mono的類別或改寫 NClass(別忘記寄patch給原作者啊),這樣就完成整個移植的過程。
對於想把 Windows 上的 .Net 應用程式移植到 Linux 的人,這是一篇相當具有參考性的文章。

ActiveRecord

ActiveRecord是一套相當不錯的 Pattern library,主要是實做ActiveRecord pattern,底層則是NHibernate
大致的原理主要是在 Entity class 的屬性上加上 Attribute,Pattern 再根據這些資訊與設定來作 Object-relation mapping
官方網站提供了一份很簡潔的指引:Getting started with ActiveRecord
很簡單,也很清楚,但是如果你想要更深入了解一些的話,我建議不妨參考大陸網友Yuhen的這一系列文章:

或是Oren Eini的系列文章:

或是Hamilton Verissimo的系列文章:

我覺得都寫的相當不錯。
原本官方有提供很不錯的工具:ActiveRecord Generator,可以幫你從建好的 Database schema 去產生 entity class,省去你手動寫 code 的痛苦。
只是現在因為人力不足的關係,暫時停止繼續開發。
如果你想試試看所謂的ORM,不妨試試看這個 Library,可以讓你少寫掉很多很多 code。

利用 Gmail SMTP server 來寄信

詳情可以參考這篇:Send E-Mail from your .NET application using your GMail Account,內文提供了 .Net 1.1/2.0 的方法。

我自己用 .Net 2,試的結果,發現有這個錯誤訊息出現:”Must issue a STARTTLS command first”
Google 大神告訴我,有蠻多人詢問這個問題
仔細研讀之後,發現是我自己忘了幫 SmtpClient 設置 EnableSsl 屬性為 true。

加上之後,又丟出 “The requested feature is not implemented.”。
咦?沒實做,難道我用的Mono 1.2.3.1還沒實做這部份?
Reflector反組譯出來看之後,果然…真的還沒實做 SSL 傳輸的部份。
希望下一版會加進去…這樣就可以在 Linux 下使用了。
以下是程式碼,它是一個命令列的程式,讓你可以指定必要的欄位後寄送郵件。
打 MailSharp -h 可以得到使用說明。

/**
* MailSharp
*
* Reference:
* 	Send E-Mail from your .NET application using your GMail Account - The Code Project - C# Programming <http://www.codeproject.com/useritems/SendMailUsingGmailAccount.asp>
*/
using System;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Mail;
using System.Net.Mime;
using System.Threading;
using System.ComponentModel;
namespace MailSharp {
public class MailSharpConsole {
public static string showUsage() {
StringBuilder sb = new StringBuilder();
sb.AppendLine( "Usage: MailSharp [options] ToAddress" );
sb.AppendLine( "\t-a File to attach." );
sb.AppendLine( "\t-f From address" );
sb.AppendLine( "\t-b Body message" );
sb.AppendLine( "\t-s Subject");
sb.AppendLine( "\t-S SMTP server host" );
sb.AppendLine( "\t-U Username for SMTP server authentication" );
sb.AppendLine( "\t-p Password for SMTP server authentication" );
sb.AppendLine( "\t-l Use SSL" );
sb.AppendLine( "\t-h Help/Usage");
sb.AppendLine();
sb.AppendLine( "Example:" );
sb.AppendLine( "\tFor GMail" );
sb.AppendLine( "\tMailSharp -f your_name@gmail.com -b Test -s Test -S smtp.gmail.com -P 587 -l -U your_gmail -p your_gmail_password someone@somewhere.com" );
return sb.ToString();
}
public static void Main(string[] args) {
MailAddress from = null;
MailAddress to = null;
MailMessage message = new MailMessage();
SmtpClient client = new SmtpClient();
NetworkCredential myCred = new NetworkCredential();
bool bShowUsage=false;
try {
if ( args.Length > 0 ) {
for ( int i=0; i<args.Length; i++ ) {
switch ( args[i] ) {
case "-S": // server host
i++;
if ( i<args.Length ) {
client.Host = args[i];
} else
throw new Exception( "-S was specified, but no value." );
break;
case "-P": // server port
i++;
if ( i<args.Length ) {
client.Port=Convert.ToInt32( args[i] );
} else
throw new Exception( "-P was specified, but no value." );
break;
case "-T": // timeout
i++;
if ( i<args.Length ) {
client.Timeout = Convert.ToInt32( args[i] );
} else
throw new Exception( "-T was specified, but no value." );
break;
case "-U": // username for smtp server authentication
i++;
if ( i<args.Length )
myCred.UserName = args[i];
else
throw new Exception( "-U was specified, but no value." );
break;
case "-p": // password for smtp server authentication
i++;
if ( i<args.Length )
myCred.Password = args[i];
else
throw new Exception( "-p was specified, but no value." );
break;
case "-l": // use SSL
client.EnableSsl = true;
break;
case "-s": // subject
i++;	// next one is subject.
if ( i<args.Length ) {
message.Subject = args[i];
message.SubjectEncoding = System.Text.Encoding.UTF8;
} else
throw new Exception( "-s was specified, but no value." );
break;
case "-a":	// attachment
i++;	// next one is attachment filename.
if ( i<args.Length ) {
// Add attachment.
Attachment data = new Attachment( args[i], MediaTypeNames.Application.Octet);
message.Attachments.Add(data);
} else
throw new Exception( "-a was specified, but no value." );
break;
case "-b":	// body message.
i++;	// next one is body message
if ( i<args.Length ) {
message.Body = args[i];
message.BodyEncoding =  System.Text.Encoding.UTF8;
} else
throw new Exception( "-b was specified, but no value." );
break;
case "-f":	// from address
i++;
if ( i<args.Length ) {
// Specify the e-mail sender.
// Create a mailing address that includes a UTF8 character
// in the display name.
// from = new MailAddress( "someone@gmail.com", "someone", System.Text.Encoding.UTF8);
from = new MailAddress( args[i] );
} else
throw new Exception( "-f was specified, but no value." );
break;
case "-h":	// show help/usage
bShowUsage=true;
break;
default:
to = new MailAddress( args[i] );
break;
}
}
} else
throw new Exception("No arguments.");
} catch ( Exception ex ) {
Console.WriteLine( ex.Message );
bShowUsage = true;
}
try {
if ( bShowUsage == true )
throw new Exception( showUsage() );
if ( from==null )
throw new Exception( "Must specify from address (-f)." );
// Set destinations for the e-mail message.
if ( to == null )
throw new Exception("At least, must specify to address");
if ( client.Host == string.Empty )
throw new Exception("Must specify SMTP Server (-S)." );
// Specify the message content.
message.From = from;
message.To.Add( to );
// Credentials are necessary if the server requires the client
// to authenticate before it will send e-mail on the client's behalf.
//client.UseDefaultCredentials = false;
client.Credentials = myCred;
// Send.
// If you need asynchronous sample, please visit the reference above.
client.Send(message);
Console.WriteLine("Done.");
} catch ( Exception ex ) {
Console.WriteLine( "Exception was raised when sending...");
Console.WriteLine( ex.Message );
} finally {
// Clean up.
message.Dispose();
}
}
}
}

如果你打算在 Linux 下服用的話,可以搭配這個 script,免去你每次前面都要打 mono 的麻煩。

#!/bin/sh
exec /usr/bin/mono $MONO_OPTIONS "MailSharp.exe" "$@"

Mono Cecil

這篇文章:Using Cecil from IronPython再次勾起我去年初看到 Mono.Cecil 時的回憶。
什麼是Mono.Cecil??以下翻譯自Nauman Leghari’s Blog : Fun with IronPython and Cecil

“Cecil 是由 Jb Evain (http://evain.net/blog/)所撰寫的類別庫,可以用來產生或注射自訂程序到以ECMA CIL撰寫的程序和類別庫。它提供了對泛型的完整支援以及對除錯資訊的部份支援。簡單的說好了,用了 Cecil,你可以載入已經存在的組件,瀏覽裡面所有的型別,即時修改它們並保存修改過的組件。”

看起來是個很神奇的東西吧~這兩篇文章介紹了如何以IronPython去使用Cecil,是很不錯的指引文章:

此外也可以參考官方提供的Cecil FAQ

如何存取SQLite

如果你還在找SQLite的 ADO.Net driver 的話,別找了。
因為Mono就提供了一個:SQLite at Mono

不管你是在 Windows 或是在 Linux 上,也不管你是用 Microsoft .Net Framework 或是 Mono,都可以直接拿他的 Mono.Data.SqliteClient.dll 來使用~
使用方法也很簡單:

  • 連接字串:”URI=file:/path/to/file,version=3″。URI指定檔案位置,version則是指定SQLite database版本。
  • 從使用範例可以看出,跟 .Net framework 提供的 ADO.Net driver 用法並沒有什麼差別(範例摘錄自SQLite at Mono):
     using System;
    using System.Data;
    using Mono.Data.SqliteClient;
    public class Test
    {
    public static void Main(string[] args)
    {
    string connectionString = "URI=file:SqliteTest.db";
    IDbConnection dbcon;
    dbcon = (IDbConnection) new SqliteConnection(connectionString);
    dbcon.Open();
    IDbCommand dbcmd = dbcon.CreateCommand();
    // requires a table to be created named employee
    // with columns firstname and lastname
    // such as,
    //        CREATE TABLE employee (
    //           firstname varchar(32),
    //           lastname varchar(32));
    string sql =
    "SELECT firstname, lastname " +
    "FROM employee";
    dbcmd.CommandText = sql;
    IDataReader reader = dbcmd.ExecuteReader();
    while(reader.Read()) {
    string FirstName = reader.GetString (0);
    string LastName = reader.GetString (1);
    Console.WriteLine("Name: " +
    FirstName + " " + LastName);
    }
    // clean up
    reader.Close();
    reader = null;
    dbcmd.Dispose();
    dbcmd = null;
    dbcon.Close();
    dbcon = null;
    }
    }
    

如果你想找一個SQLite管理工具,我個人推薦使用SQLiteSpy,既小又方便而且還免安裝。

讀取網頁(4)

更快的方法,就是直接利用 .Net 2.0 提供的 WebBrowser 控制項。
不過,如果用這方法,有兩個缺點:

  1. .Net 1.1 不適用,因為 class library 沒提供。
  2. 當直接使用 DocumentText 屬性的時候,WebBrowser 沒有轉換編碼,而是直接以 ascii 傳回。
  3. 必須是 Windows form 應用程式

那麼,我們要怎麼自行轉換編碼呢?
首先利用 Reflector 來反組譯一下 DocumentText 屬性,這下就可以很清楚看到他是以 StreamReader 去讀取 DocumentStream。
所以當我們確信網頁的編碼是 UTF-8 時,就可以這麼寫了:

Stream documentStream = webBrowser1.DocumentStream;
if (documentStream == null)
return "";
StreamReader reader = new StreamReader(documentStream, Encoding.UTF8);
documentStream.Position = 0;
string documentText = reader.ReadToEnd();