November 30, 2010

NHibernate - 設定檔範例 for SQL Server/Oracle/PostgreSQL

以下提供SQL Server  2008Oracle 10gPostgreSQL 8.4的設定檔範例,需設定在Web.config/App.config
1.設定configSections區段
    <configSections>
        <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
    </configSections> 
2.設定hibernate-configuration區段
(a) SQL Server 2008
     <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
        <session-factory>
            <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
            <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>
            <property name="dialect">NHibernate.Dialect.MsSql2008Dialect</property>
            <property name="connection.connection_string">Data Source=10.0.0.10;Initial Catalog=dbname;Persist Security Info=True;User ID=username;Password=password;</property>

            <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
            <mapping assembly="NHibernateTest.Data"/>
        </session-factory>
    </hibernate-configuration>
(b) Oracle 10g
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
        <session-factory>
            <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
            <property name="connection.driver_class">NHibernate.Driver.OracleClientDriver</property>
            <property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>
            <property name="connection.connection_string">Data Source=XXX;Persist Security Info=True;User ID=username;Password=password;</property>

            <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
            <mapping assembly="NHibernateTest.Data"/>
        </session-factory>
    </hibernate-configuration>
(c) PostgreSQL 8.4
    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
        <session-factory>
            <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
            <property name="connection.driver_class">NHibernate.Driver.NpgsqlDriver</property>
            <property name="dialect">NHibernate.Dialect.PostgreSQL82Dialect</property>
            <property name="connection.connection_string">Server=10.0.0.10;Port=5432;Database=dbname;User ID=username;Password=password;</property>

            <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
            <mapping assembly="NHibernateTest.Data"/>
        </session-factory>
    </hibernate-configuration>
也可將連線字串的設定移到connectionStrings區段中,如以下範例
     <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
        <session-factory>
            <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
            <property name="connection.driver_class">NHibernate.Driver.NpgsqlDriver</property>
            <property name="dialect">NHibernate.Dialect.PostgreSQL82Dialect</property>
            <property name="connection.connection_string_name">PostgreSQL</property>
            <property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu</property>
            <mapping assembly="NHibernateTest.Data"/>
        </session-factory>
    </hibernate-configuration>
    <connectionStrings>
        <add name="PostgreSQL" connectionString="Server=10.0.0.10;Port=5432;Database=dbname;User ID=username;Password=password;"/>
    </connectionStrings> 
備註
1.筆者測試用的PostgreSQL版本為8.4,但在NHibernate 2.1.2支援的dialect只到8.2,可設定的dialect可參考http://nhforge.org/doc/nh/en/index.html#configuration-optional-dialects
2.<mapping assembly="NHibernateTest.Data"/>為對應到資料庫所使用的domain entities編譯檔

November 25, 2010

NHibernate - 啟用設定檔與物件關聯對應檔的Intellisense

NHibernate的設定檔及物件關聯對應檔(object/relational mapping file,副檔名為*.hbm.xml)皆為XML格式的檔案。

對應檔的schema其實頗複雜的,如果要用手刻可能會瘋掉,好在NHibernate提供了兩支XML Schema - nhibernate-configuration.xsdnhibernate-mapping.xsd可供我們加入Visual Studio啟動其intellisense的功能
nhibernate-configuration.xsd是NHibernate設定檔的schema
nhibernate-mapping.xsd是NHibernate物件關聯對應檔的schema
這兩個檔案可於NHibernate的sourceforge連結http://sourceforge.net/projects/nhibernate/files/NHibernate/2.1.2GA/NHibernate-2.1.2.GA-bin.zip/download下載

下載後可於壓縮檔內的Required_Bins資料夾下找到這兩支xsd檔

接下來將這兩支xsd檔放至路徑C:\Program Files (x86)\Microsoft Visual Studio 9.0\Xml\Schemas下,重新啟動VS後,於編輯設定檔與物件關聯即可使用intellisense

以上說明使用的系統環境為Windows 7 64bit, Visual Studio 2008, NHibernate 2.1.2

DataGridView的ScrollBars屬性設定後無作用?

系統環境:Windows 7 x64, Visual Studio 2008
問題描述
DataGridViewScrollBars屬性設定為BothDock設為Fill,下邊連接著一個StatusStripDock設定為Bottom,執行程式後當DataGridView寬度大於Form寬度時,horizontal scrollbar卻無法顯示出來


解決方法
將StatusStrip在版面上的配置移到最下層才不會蓋住DataGridView,執行後可正確顯示出horizontal scrollbar



November 18, 2010

NHibernate - 相關資源

最近因為想嘗試以NHibernate來設計專案中的Data Access Layer,所以陸續記錄一下收集到的資源

NHibernate官方網址:http://nhforge.org/

NHibernate下載位址:http://sourceforge.net/projects/nhibernate/

NHibernate線上手冊:http://nhforge.org/doc/nh/en/index.html

NHibernate線上手冊(PDF、CHM及HTML三種格式)下載位址:http://sourceforge.net/projects/nhibernate/files/NHibernate/2.1.2GA/NHibernate-2.1.2.GA-reference.zip/download 

NHibernate How to:http://nhforge.org/wikis/howtonh/default.aspx

NHibernate Contrib下載位址:http://sourceforge.net/projects/nhcontrib/

NHibernate Burrow:http://nhforge.org/wikis/burrow/default.aspx

Fluent NHibernate:http://fluentnhibernate.org/

November 16, 2010

去除UTF-8 BOM的方法

今天在處理一份格式為UTF-8的XML資料時,用XDocument去剖析這份XML,明明格式"看起來"就沒問題,但是一執行XDocument.Parse就出現"在根層次的資料無效。 第 1 行,位置 1。"的錯誤訊息,可是第一行第一個字元不就是個小於符號(<)嗎?。研究了一下發現了這錯誤訊息很有可能是所剖析的XML其二進位資料被塞入了BOM (what is BOM?)
檢查了一下這份XML發現的確被塞入了三個bytes的UTF-8 BOM

接下來就是想辦法把它拿掉了,以下提供一範例參考自http://efreedom.com/Question/1-2070661/Change-XML-String-XDocumentParse-Reads並做修改
                byte[] bytes = Encoding.UTF8.GetBytes(xml);
                if (bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf)
                {
                    string byteOrderMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
                    xml = xml.Remove(0, byteOrderMarkUtf8.Length);
                }
 與參考網址不同的地方是if判斷式的寫法,因為實測之後發現若XML不含BOM字元的話XML的第一個字元會被截掉而造成錯誤,故改以檢查byte方式進行判斷
其中的Encoding.UTF8.GetPreamble() 用意是在取得UTF-8 BOM的二進位字元(EF BB BF)

XML結構描述定義(XSD)轉物件類別的方法(2) - 使用Xsd2Code

接續前一篇文章XML結構描述定義(XSD)轉物件類別的方法(1) - 使用xsd.exe,本篇要介紹另一個XSD轉物件類別的工具 - Xsd2Code

Xsd2Code是一個Codeplex上的Visual Studio Add-in,安裝後可直接在VS中右鍵選取*.xsd檔進行轉換成類別的動作,如下

點選Run Xsd2Code generation會出現如下畫面

 在Options標籤頁畫面中所做的設定將會影響到輸出的類別內容,以下針對小弟有測試過的幾個設定做簡單的說明
CustomUsings:新增除了預設以外會使用到的命名空間
Language:產出類別的語言,C#VB.NET
NameSpace:產出類別的命名空間
CollectionObjectType:針對XSD內設定為unbouned的元素輸出時要使用的集合型別(Arrary、List、IList, etc.)
EnableInitializeFields:初始化欄位值,例如私有變數。初始化會發生在建構式或屬性(做lazy loading/deferred loading時用)裡。
PropertyParams->AutomaticProperties:自動實作屬性,這是C# 3.0開始有支援的一個feature,如public string Name {get;set;},而不必透過私有變數來存取屬性。使用這個設定前,TargetFramework一定要設為NET30以上版本

以下為使用Xsd2Code產生的類別範例
 // ------------------------------------------------------------------------------
//  <auto-generated>
//    Generated by Xsd2Code. Version 3.4.0.38967
//    <NameSpace>Xsd2Entities</NameSpace><Collection>List</Collection><codeType>CSharp</codeType><EnableDataBinding>False</EnableDataBinding><EnableLazyLoading>False</EnableLazyLoading><TrackingChangesEnable>False</TrackingChangesEnable><GenTrackingClasses>False</GenTrackingClasses><HidePrivateFieldInIDE>False</HidePrivateFieldInIDE><EnableSummaryComment>False</EnableSummaryComment><VirtualProp>False</VirtualProp><IncludeSerializeMethod>False</IncludeSerializeMethod><UseBaseClass>False</UseBaseClass><GenBaseClass>False</GenBaseClass><GenerateCloneMethod>False</GenerateCloneMethod><GenerateDataContracts>False</GenerateDataContracts><CodeBaseTag>Net35</CodeBaseTag><SerializeMethodName>Serialize</SerializeMethodName><DeserializeMethodName>Deserialize</DeserializeMethodName><SaveToFileMethodName>SaveToFile</SaveToFileMethodName><LoadFromFileMethodName>LoadFromFile</LoadFromFileMethodName><GenerateXMLAttributes>False</GenerateXMLAttributes><EnableEncoding>False</EnableEncoding><AutomaticProperties>False</AutomaticProperties><GenerateShouldSerialize>False</GenerateShouldSerialize><DisableDebug>False</DisableDebug><PropNameSpecified>Default</PropNameSpecified><Encoder>UTF8</Encoder><CustomUsings></CustomUsings><ExcludeIncludedTypes>False</ExcludeIncludedTypes><EnableInitializeFields>True</EnableInitializeFields>
//  </auto-generated>
// ------------------------------------------------------------------------------
namespace Xsd2Entities {
    using System;
    using System.Diagnostics;
    using System.Xml.Serialization;
    using System.Collections;
    using System.Xml.Schema;
    using System.ComponentModel;
    using System.Collections.Generic;


    public partial class BooksForm {
    
        private List<BookForm> bookField;
    
        public BooksForm() {
            this.bookField = new List<BookForm>();
        }
    
        public List<BookForm> book {
            get {
                return this.bookField;
            }
            set {
                this.bookField = value;
            }
        }
    }

    public partial class BookForm {
    
        private string authorField;
    
        private string titleField;
    
        private string genreField;
    
        private float priceField;
    
        private System.DateTime pub_dateField;
    
        private string reviewField;
    
        private string idField;
    
        public string author {
            get {
                return this.authorField;
            }
            set {
                this.authorField = value;
            }
        }
    
        public string title {
            get {
                return this.titleField;
            }
            set {
                this.titleField = value;
            }
        }
    
        public string genre {
            get {
                return this.genreField;
            }
            set {
                this.genreField = value;
            }
        }
    
        public float price {
            get {
                return this.priceField;
            }
            set {
                this.priceField = value;
            }
        }
    
        public System.DateTime pub_date {
            get {
                return this.pub_dateField;
            }
            set {
                this.pub_dateField = value;
            }
        }
    
        public string review {
            get {
                return this.reviewField;
            }
            set {
                this.reviewField = value;
            }
        }
    
        public string id {
            get {
                return this.idField;
            }
            set {
                this.idField = value;
            }
        }
    }
}

November 15, 2010

XML結構描述定義(XSD)轉物件類別的方法(1) - 使用xsd.exe

在實作某些以XML為基礎的標準時,我們常會以XmlDocumentXmlWriter或.NET Framework3.5起支援的 XDocument類別來幫助我們產生符合的XML,通常一個well-defined的標準幾乎也都會附上其所參考的結構描述定義檔(*.xsd)

相較於使用XmlDocumentXmlWriterXDocument建立XML的方式,在實作較為大型的標準時,如果我們能以物件導向的方式將資料設定給某個物件後再將此物件序列化成XML,這樣的作法是否較為"美觀"也比較好維護/除錯。不過前提是,我們需要有一份(或以上)的XML結構描述定義檔。以下將分兩篇文章介紹兩種方法,可以將XSD轉換為物件類別

系統環境:Windows 7 x64, Visual Studio 2008
使用.NET Framework內建的xsd.exe工具(開啟Visual Studio 2008 命令提示字元)
xsd.exe工具詳細的用法可參考XML Schema Definition Tool (Xsd.exe)
利用此工具,我們可以一行command line就可以將XSD檔(參考http://msdn.microsoft.com/en-us/library/ms764613(VS.85).aspx)轉換出一支類別檔。
結構描述定義檔內容如下


  

  
    
      
      
  

  
    
      
      
      
      
      
      
    
    
  
執行以下指令

 /c指的是輸出類別檔 ,/o指的是輸出類別檔存放的位置。

產出的class如下
//------------------------------------------------------------------------------
// 
//     這段程式碼是由工具產生的。
//     執行階段版本:2.0.50727.4952
//
//     對這個檔案所做的變更可能會造成錯誤的行為,而且如果重新產生程式碼,
//     變更將會遺失。
// 
//------------------------------------------------------------------------------

using System.Xml.Serialization;

// 
// 此原始程式碼由 xsd 版本=2.0.50727.3038 自動產生。
// 


[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:books")]
[System.Xml.Serialization.XmlRootAttribute("books", Namespace="urn:books", IsNullable=false)]
public partial class BooksForm {
    
    private BookForm[] bookField;
    
    [System.Xml.Serialization.XmlElementAttribute("book", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public BookForm[] book {
        get {
            return this.bookField;
        }
        set {
            this.bookField = value;
        }
    }
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:books")]
public partial class BookForm {
    
    private string authorField;
    
    private string titleField;
    
    private string genreField;
    
    private float priceField;
    
    private System.DateTime pub_dateField;
    
    private string reviewField;
    
    private string idField;
    
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string author {
        get {
            return this.authorField;
        }
        set {
            this.authorField = value;
        }
    }
    
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string title {
        get {
            return this.titleField;
        }
        set {
            this.titleField = value;
        }
    }
    
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string genre {
        get {
            return this.genreField;
        }
        set {
            this.genreField = value;
        }
    }
    
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public float price {
        get {
            return this.priceField;
        }
        set {
            this.priceField = value;
        }
    }
    
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified, DataType="date")]
    public System.DateTime pub_date {
        get {
            return this.pub_dateField;
        }
        set {
            this.pub_dateField = value;
        }
    }
    
    [System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public string review {
        get {
            return this.reviewField;
        }
        set {
            this.reviewField = value;
        }
    }
    
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string id {
        get {
            return this.idField;
        }
        set {
            this.idField = value;
        }
    }
}
 接下來我們即可以物件的方式來操作並透過XmlSerializer將物件序列化為XML,如下
            BooksForm bf = new BooksForm();

            BookForm[] bfArray = new BookForm[]
            {
                new BookForm(){ author = "Brad Abrams, Krzysztof Cwalina", title= "Framework Design Guidelines", price = 44.45F, pub_date = new DateTime(2008,11,1)},
                new BookForm(){ author = "Judith Bishop", title = "C# 3.0 Design Patterns", price = 26.39F, pub_date = new DateTime(2008,1,11)},
                new BookForm(){ author = "Dino Esposito, Andrea Saltarello", title = "Microsoft .NET Architecting Applications for the Enterprise (PRO-Developer)", price = 28.72F, pub_date = new DateTime(2008,12,23)}
            };

            bf.book = bfArray;
            XmlSerializer serializer = new XmlSerializer(bf.GetType());
            using (MemoryStream stream = new MemoryStream())
            {
                serializer.Serialize(stream, bf);
                Console.Write(Encoding.UTF8.GetString(stream.ToArray()));
            }
            
            Console.ReadKey(true);
最終產出的XML如下

備註
  1. 若要轉換的xsd還有參考到別的xsd檔,請記得一併加入指令中做處理
  2. 多個xsd一起做轉換時,若有互相參考或繼承,會有轉換順序的問題,請將基底的xsd放在最前依序排列做轉換,如xsd D:\base.xsd D:\derivedxsd1.xsd :D\derivedxsd2.xsd /c

November 12, 2010

使用XDocument驗証XML結構(範例)

using System.Xml.Linq;
using System.Xml.Schema;

XDocument xDoc = XDocument.Parse("要驗証的XML字串");
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(string.Empty, "XML結構描述檔位址(*.xsd)");
xDoc.Validate(schemas, null);

如果驗証失敗可以try-catch取得XmlSchemaValidationException內的錯誤訊息或是在XDoc.Validate的第二個參數中使用delegate交給特定function來處理

November 5, 2010

如何驗証GUID格式是否有效

GUID很常用, 尤其是拿來當primary key,我們很常用GUID來取得DB中相對應的資料。在大部份的情況下我們不會讓使用者輸入GUID來取得對應資料。

預設在.NET中以System.Guid.NewGuid()可以產生一長度為36的GUID字串,就像在SQL Server中將預設值設為newid()產出的GUID字串一樣,如a7c1e7ea-9e6b-4067-9278-0a1f78bbbb44
剛好手邊的案子就需要使用者輸入GUID來跟系統做認證授權並取得資料,使用者輸入的GUID長度或格式若有誤,頂多找不到資料,但因字串長,比對格式或長度有沒有錯眼力不夠好可能也得花些時間所以就會希望有個防呆機制,驗証GUID格式是否符合預期,並適時回應給使用者。

一個quick-and-dirty的解法,就是用try-catch方式,將使用者輸入的GUID字串帶入System.Guid的建構式中,若Guid類別無法被初始化,就會丟出exception並被catch到,如下code snippet
public bool IsGuidValid(string inputGuid)
        {
            try
            {
                Guid guid = new Guid(inputGuid);
                return true;
            }
            catch
            {
                return false;
            }
        }
這是最快的方式,卻也濫用了try-catch

比較好的做法是使用Regular Expression去檢查GUID的格式,如下
public static bool IsGuidValid(string inputGuid)
        {
            string pattern = @"^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$";
            return Regex.IsMatch(inputGuid, pattern);
        }
而在.NET 4.0中,提供了新的Guid.TryParseGuid.TryParseExact方法讓我們可以判斷GUID是否有效,可以簡化上述的兩個做法

November 4, 2010

Web測試錄製器無法使用?

系統環境:Windows 7 x64, Visual Studio 2008
問題描述:在VS新增Web測試後並嘗試瀏覽本機網站後,發現Web測試錄製器被disabled,並顯示"Web測試錄製器必須從Visual Studio內部啟動"

但有趣的是我的的確確是從VS啟動的

明查暗訪之後發現開啟VS時需以系統管理員身份開啟VS並載入方案/專案即可正常使用


不過每次都要以系統管理員身份開啟VS再載方案有點麻煩,直接以系統管理員身份開啟方案/專案檔不就得了,但實際會發現右鍵選單看不到這個選項

可以將啟動VS方案/專案檔的VSLauncher.exe(目錄為C:\Program Files (x86)\Common Files\microsoft shared\MSEnv,以Windows 7 x64為例)設為以系統管理員身份執行

接著執行方案/專案即可以系統管理員身份開啟