前面的一篇博文DebugLZQ使用三層架構實現了TaskVision,並在后面利用Webservice代替ClassLibrary實現SQL Server 2008數據庫操作提供程序。關於三層架構的理解請參考前一篇博文。
寫這篇博文的目的在於,強調我前面一篇博文中提到但是今天又遇到且花了我一定時間去解決的問題。即config問題。需要將DAL層調用WCF服務生產的app.config文件拷貝到UI工程中,注意不是WCF工程的config文件!
否則程序會報告:The type initializer for 'DAL.DataAccess' threw an exception. 很奇怪的錯誤!
baidu+Google很久,找到了些許思路。故寫下來,方便后來人。
UI工程、BLL和前面博文相同。
程序的結構如下:

WCF IService.cs如下:
View Code
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using System.Data; namespace AdoDotNetWcfServiceLibrary { // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together. [ServiceContract] public interface IService1 { [OperationContract] string GetData(int value); [OperationContract] CompositeType GetDataUsingDataContract(CompositeType composite); // TODO: Add your service operations here [OperationContract] DataTable GetDataTable(string sqlText, params string[] parameters); [OperationContract] int ExecuteNonQuery(string sqlText, params string[] parameters); [OperationContract] bool ExecuteReader(string sqlText, params string[] parameters); } // Use a data contract as illustrated in the sample below to add composite types to service operations [DataContract] public class CompositeType { bool boolValue = true; string stringValue = "Hello "; [DataMember] public bool BoolValue { get { return boolValue; } set { boolValue = value; } } [DataMember] public string StringValue { get { return stringValue; } set { stringValue = value; } } } }
WCF Service1.cs如下:
View Code
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using System.Data; using System.Data.SqlClient; namespace AdoDotNetWcfServiceLibrary { // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together. public class Service1 : IService1 { public string GetData(int value) { return string.Format("You entered: {0}", value); } public CompositeType GetDataUsingDataContract(CompositeType composite) { if (composite == null) { throw new ArgumentNullException("composite"); } if (composite.BoolValue) { composite.StringValue += "Suffix"; } return composite; } public const string connectionString = @"server=LocalHost;database=TaskVision;Trusted_Connection=SSPI"; public DataTable GetDataTable(string sqlText, params string[] parameters) { using (SqlConnection conn = new SqlConnection(connectionString)) { SqlDataAdapter sda = new SqlDataAdapter(sqlText, conn); for (int i = 0; i < parameters.Length; i = i + 2) { sda.SelectCommand.Parameters.Add(new SqlParameter(parameters[i], parameters[i + 1])); } DataTable dt = new DataTable(); dt.TableName = "MyTable";//OMG! sda.Fill(dt); return dt; } } public int ExecuteNonQuery(string sqlText, params string[] parameters) { using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand cmd = new SqlCommand(sqlText, conn); for (int i = 0; i < parameters.Length; i = i + 2) { cmd.Parameters.Add(new SqlParameter(parameters[i], parameters[i + 1])); } conn.Open(); int temp = cmd.ExecuteNonQuery(); return temp; } } public bool ExecuteReader(string sqlText, params string[] parameters) { using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); SqlCommand cmd = new SqlCommand(sqlText, conn); for (int i = 0; i < parameters.Length; i = i + 2) { cmd.Parameters.Add(new SqlParameter(parameters[i], parameters[i + 1])); } using (SqlDataReader reader = cmd.ExecuteReader()) { if (reader.Read()) return true; return false; } } } } }
DAL如下:
View Code
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.SqlClient; using System.Collections; namespace DAL { public static class DataAccess { //wcfclient public static ServiceReference1.Service1Client client = new ServiceReference1.Service1Client(); public static DataTable GetDataTable(string sqlText, params SqlParameter[] parameters) { //return AdoDotNetClassLibrary.SQLHelper.GetDataTable(sqlText, parameters); ArrayList ps = new ArrayList(); for (int i = 0; i < parameters.Length; i++) { ps.Add(parameters[i].ParameterName); ps.Add(parameters[i].Value); } string[] ps1 = (string[])ps.ToArray(typeof(string)); return client.GetDataTable(sqlText, ps1); } public static int ExecuteNonQuery(string sqlText, params SqlParameter[] parameters) { //return AdoDotNetClassLibrary.SQLHelper.ExecuteNonQuery(sqlText,parameters); ArrayList ps = new ArrayList(); for (int i = 0; i < parameters.Length; i++) { ps.Add(parameters[i].ParameterName); ps.Add(parameters[i].Value); } string[] ps1 = (string[])ps.ToArray(typeof(string)); return client.ExecuteNonQuery(sqlText, ps1); } public static bool ExecuteReader(string sqlText, params SqlParameter[] parameters) { //return AdoDotNetClassLibrary.SQLHelper.ExecuteReader(sqlText, parameters); ArrayList ps = new ArrayList(); for (int i = 0; i < parameters.Length; i++) { ps.Add(parameters[i].ParameterName); ps.Add(parameters[i].Value); } string[] ps1 = (string[])ps.ToArray(typeof(string)); return client.ExecuteReader(sqlText, ps1); } } }
得益於三層架構帶來的好處,DAL層只更改了1條語句!其它層保持不變。
程序運行部分界面如下:


再次強調一下,需要將DAL項目調用WCF服務生產的config文件(不是WCF項目的config文件)拷貝到UI工程中去!
文章寫完,回過頭來再想想:The type initializer for 'DAL.DataAccess' threw an exception. 回過頭來再想想這個異常,問題出在config這個文件也就不難理解了,因為不是這個地方的問題,還能是其他的什么問題呢?
伍華聰的這幾篇博文:《Winform開發框架之框架演化》、《Winform開發框架之混合型框架的實現》、《Winform開發框架之混合型框架的實現》感覺是給N層架構起了個混合框架的名字,有興趣的博友也可以看下。
注意:三層架構固然有點多多,但是也要考慮級聯修改、性能降低、開發成本增加等缺點。
