前言:
关于不同框架实现同一个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.不可跨层调用;
满足以上规则,就可以认为这个项目是三层了。
(*给出的)优点:
实际是这样的
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)
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
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
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;
}
}
}
}
}
数据由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
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:
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.添加资源文件:
添加代码:
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、多用英文问问题!