[.NET 三层架构(ADO.NET)+Web Service+多语言]WinForm三层架构重新实现TaskVision,外加WebService提供数据和多语言

前言:                                                 

关于不同框架实现同一个TaskVision:

前面DebugLZQ先是用WPF(没有使用MVVM,因为前面使用MVVM实现过过点餐系统),因而这个关键点就放在了WPF的Binding上面;

然后用普通的Winform,没有加入任何模式,实现了相同的功能。因此这个重点放在了DataGridView的总结,以及WinForm自定义控件实现类似WPF控件上面。

本篇博文使用标准的三层架构,重新实现这个TaskVision。因而重点放在三层架构方面、为了体现三层的各层间低耦合的特点,博文下半部分会将DAL换成WebService,并实现多语言。数据库依然是原来的SQL Server 2008.

标准的是这样的:                                         

在软件体系设计中,分层式结构式最常见也是最重要的一种结构。MS推荐的分层结构一般分为三层,从上到下依次为UI、BLL、DAL。

理解软件分层的概念有助于理解各种大量应用的模式结构,如MVC、MVVM等。以及GOF其他的一些等等。理解了三层,其他的理解起来会方便很多,因为其中贯通的都是:表现层的解耦。个人理解:模式间的区别是:根据具体的技术框架特点,决定表现层解耦方法的不同,由不断的最佳实践,总结出了各种不同的模式。即前面DebugLZQ也说的:表现层的持续解耦,带来的模式的转变!

传统的三层式这样的:

解释一下:UI层调用BLL层,传递的参数为UI层控件的属性值;BLL层调用DAL层,并加入相应的逻辑;DAL层则负责对数据库访问的封装(DAL层直接封装数据库访问,箭头所示)。

我理解的规则

1.UI层不包括任何BL;

2.设计从BLL出发,而不是从UI出发,BLL实现所有的BL。

3.DAL应该做到一定程度上与系统无关。即这一层可抽离。

4.不可跨层调用;

满足以上规则,就可以认为这个项目是三层了。

(维基百科给出的)优点

  1、开发人员可以只关注整个结构中的其中某一层;
  2、可以很容易的用新的实现来替换原有层次的实现;
  3、可以降低层与层之间的依赖;
  4、有利于标准化;
  5、利于各层逻辑的复用。
  6、结构更加的明确
  7、在后期维护的时候,极大地降低了维护成本和维护时间
(维基百科给出的)缺点
  1、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。
  2、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。
  3、增加了开发成本。
关于引用百科的解释:确实是这样,不需要我去另外理解、解释。但是关于模式的理解是个人的。 

 实际是这样的                             

实际使用中通常会
1.加入Model。Model用来作为参数,在UI和BLL间传递。耦合度进一步降低!
2.将DAL直接访问数据库改成调用其他数据库访问类库或服务,耦合度进一步降低!
因此,针对DebugLZQ这个版本的TaskVison,你看到的所谓三层可能是这样的“5层”。如下:
各层代码如下,以登陆为例:
UI:Login.cs
View Code
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Models;

namespace TaskVision_V2
{
    public partial class Login : Form
    {
        public Login()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Models.UserLoginModel userLoginModel = new UserLoginModel() {  UserName=txtUserName.Text.Trim(), Password=txtPassword.Text, RememberPassword=checkBox1.Checked};
            if (BLL.UserService.Login(userLoginModel))
            {
                //FormMain formMain = new FormMain();
                //formMain.Show();
                Global.userName = txtUserName.Text;
                this.DialogResult = DialogResult.OK;
            }
            else
            {
                MessageBox.Show("用户名或密码错误!");
            }            
        }

        private void button2_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

Models:UserLoginModel.cs(叫UserModel更贴切,Model对应数据库表+判断逻辑控件value)

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Models
{
    public class UserLoginModel
    {
        public string Id { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public bool RememberPassword { get; set; }
    }
}

BLL:UserService.cs

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;

namespace BLL
{
    public class UserService
    {
        /// <summary>
        /// User Login
        /// </summary>
        private static string loginSQLText = "select * from tb_UserInfo where UserName=@UserName and Password=@Password";
        public static bool Login(Models.UserLoginModel userLoginModel)
        {
            if (userLoginModel.RememberPassword)
            {
                //todo:
            }
            SqlParameter[] parameters = new SqlParameter[] { new SqlParameter("@UserName",userLoginModel.UserName),new SqlParameter("@Password",userLoginModel.Password)};
            return DAL.DataAccess.ExecuteReader(loginSQLText, parameters);
        }
        /// <summary>
        /// Get All User
        /// </summary>
        private static string getUserSQLText = "select distinct UserName from tb_UserInfo";
        public static DataTable GetUser()
        {
            return DAL.DataAccess.GetDataTable(getUserSQLText);
        }
    }
}

DAL:DataAccess.cs

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;

namespace DAL
{
    public static class DataAccess
    {
        public static DataTable GetDataTable(string sqlText, params SqlParameter[] parameters)
        {
            return AdoDotNetClassLibrary.SQLHelper.GetDataTable(sqlText, parameters);
        }

        public static int ExecuteNonQuery(string sqlText,params SqlParameter[] parameters)
        {
            return AdoDotNetClassLibrary.SQLHelper.ExecuteNonQuery(sqlText,parameters);
        }

        public static bool ExecuteReader(string sqlText, SqlParameter[] parameters)
        {
            return AdoDotNetClassLibrary.SQLHelper.ExecuteReader(sqlText, parameters);
        }       
    }
}

AdoDotNetClassLibrary:SQLHelper.cs

using System.Data;
using System.Data.SqlClient;

namespace AdoDotNetClassLibrary
{
    public static class SQLHelper
    {
        public const string connectionString = @"server=LocalHost;database=TaskVision;Trusted_Connection=SSPI";

        public static DataTable GetDataTable(string sqlText,params SqlParameter[] parameters)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                SqlDataAdapter sda = new SqlDataAdapter(sqlText, conn);
                foreach (SqlParameter parameter in parameters)
                {
                    sda.SelectCommand.Parameters.Add(parameter);
                }
                DataTable dt = new DataTable();
                sda.Fill(dt);

                return dt;
            }
        }

        public static int ExecuteNonQuery(string sqlText,params SqlParameter[] parameters)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                SqlCommand cmd = new SqlCommand(sqlText, conn);
                foreach (SqlParameter parameter in parameters)
                {
                    cmd.Parameters.Add(parameter);
                }
                conn.Open();
                int temp = cmd.ExecuteNonQuery();
                return temp;
            }
        }

        public static bool ExecuteReader(string sqlText, params SqlParameter[] parameters)
        {
            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
                SqlCommand cmd = new SqlCommand(sqlText, conn);
                foreach (SqlParameter parameter in parameters)
                {
                    cmd.Parameters.Add(parameter);
                }
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    if (reader.Read())
                        return true;
                    return false;
                }
            }
        }

    }
}
View Code

数据由Webservice提供                   

注意点:

由于webservice序列化问题,SqlParameter不能直接传递,因此改用strng代替下;

http://forums.asp.net/t/1335298.aspx!需要为DataTable设置名称,dt.TableName="myTable";

需要将DAL引用webservice生成的config文件拷贝到UI工程中(注意:不是webservice项目的config)。否则会报告无法找到EndPoint运行时错误。

由此只要:

添加:AdoDotNetWebService--service1.asmx 

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Data;
using System.Data.SqlClient;
using System.Collections;

namespace AdoDotNetWebService
{
    /// <summary>
    /// Summary description for Service1
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    // [System.Web.Script.Services.ScriptService]
    public class Service1 : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "Hello World";
        }        
        public const string connectionString = @"server=LocalHost;database=TaskVision;Trusted_Connection=SSPI";
      

        [WebMethod]
        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;
            }
        }

        [WebMethod]
        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;
            }
        }



        [WebMethod]
        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
    {
        //webserviceclient
        public static ServiceReference1.Service1SoapClient client = new ServiceReference1.Service1SoapClient();

        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);
        }
    }
}

其他层保持不变!

 实现多语言                           

 非常简单。步骤如下:

1.设置Form的Localizable属性为True。
2.为每个Form添加对应的资源文件,命名有要求:以Login.cs窗体为例,资源名为Login.en.resx/Login.zh-CHS.resx(需要查阅具体语言的代码)。添加完成后VS自动将该资源文件添加到对应的窗体下。
3.编辑添加的资源文件,注意:不同String类型资源文件的Name在不同的resx中必须相同。目的是为了保证编码方便。
4.在Form_Load中根据CurrentUICulture为 Form的控件Text赋值(是用的是resx中string的Name,赋的是Value)

以Login为例。

1.设置Localizable属性为True。

2.添加资源文件:

添加代码:

View Code
        private void Login_Load(object sender, EventArgs e)
        {
            Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en");
            UpDataMainFormUILanguage();           
        }

        private void UpDataMainFormUILanguage()
        {
            ResourceManager rm = new ResourceManager(typeof(Login));
            UpdateLoginUILanguage(rm);
        }

        private void UpdateLoginUILanguage(ResourceManager rm)
        {
            this.Text = rm.GetString("Login");
            lblUserName.Text = rm.GetString("UserName");
            lblPassword.Text = rm.GetString("Password");
            checkBox1.Text = rm.GetString("RememberPassword");
            btnLogin.Text = rm.GetString("Login");
            btnCancel.Text = rm.GetString("Cancel");
        }

效果如下:

小结:     

博文重点在于说明三层架构:UI层构建Model,传递给BLL层,BLL层利用UI层传过来的Model的属性作为参数调用DAL层,DAL层是对具体数据库访问方式的封装,真正的数据库访问则由具体的类库、服务等提供。关于三层架构也许很多人的理解都可能不够标准,我是这么认为的。附加介绍了WebService作为数据库提供程序的方法,需要注意的地方。以及实现多语言的一种参考方法。

 感触:  

Baidu不靠谱!多问Google、多用英文问问题! 

posted @ 2013-04-30 21:45  DebugLZQ  阅读(5391)  评论(2编辑  收藏  举报