前面的一篇博文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如下:
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如下:
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如下:
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层架构起了个混合框架的名字,有兴趣的博友也可以看下。
注意:三层架构固然有点多多,但是也要考虑级联修改、性能降低、开发成本增加等缺点。