首先我想还是给大家过一遍数据存储的流程吧。选个最简单的例子,用户登录,将用户从数据库中取出来。
首先需要在SQL数据库中编写存储过程,选存储过程+传递参数用SqlParameter是因为,除非是ADO.NET有漏洞,那么就绝对不会发生SQL注入。据我所知SQL注入发生在借用用户输入拼接生成SQL语句的地方。其次执行数据操作的性能也提高了。
Code
CREATE PROCEDURE [dbo].[EndUserLogin_Select]
@Email nvarchar(50),
@Password nvarchar(50)
AS
SELECT EndUserID,
EndUserTypeID,
UserName,
dt_EndUser.AddressID,
dt_EndUser.ContactInformationID,
Password,
IsSubscribed,
Phone,
Phone2,
Fax,
Email
FROM dt_EndUser
INNER JOIN dt_ContactInformation
ON dt_ContactInformation.ContactInformationID=dt_EndUser.ContactInformationID
WHERE Email=@Email
AND Password=@Password
AND EndUserTypeID=1
说明下,现在电子商务很流行采用邮箱或手机号来作为账号,而本网站就是采用的邮箱作为登录账号,所有有两个参数一个是@Email 和 @Password。这里因为邮箱在dt_ContactInformation表中所以查找的时候要将dt_EndUser和dt_ContactInformation合并起来通过ContactInformationID字段,EndUserTypeID=1代表用户类型是会员。
还记得我们电子商务之框架分析(一)时在Shop.DataAccess类库下的StoredProcedure类吗?接下来就在该类枚举型Name里面加入EndUserLogin_Select即存储过程名称。
然后我们就可以在Shop.DataAccess类库下实现与数据库的连接,在该类库下首先创建一个Select文件夹,所有对数据库进行Select操作的类都可以放在该文件夹下(当然你这里自己可以在细分)。在Select文件夹下创建一个EndUserLoginSelectData类
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Shop.Common;
using System.Data.SqlClient;
using System.Data;
namespace Shop.DataAccess.Select
{
/// <summary>
/// 继承了基类DataAccessBase
/// </summary>
public class EndUserLoginSelectData:DataAccessBase
{
//属性
public EndUserEntity EndUser{set;get;}
/// <summary>
/// 构造函数将存储过程的名称赋值给该类StoredprocedureName属性
/// </summary>
public EndUserLoginSelectData()
{
this.StoredprocedureName = StoredProcedure.Name.EndUserLogin_Select.ToString();
}
public DataSet Get()
{
DataSet ds;
//完成将添加SqlParameter
EndUserLoginSelectDataParameters enduserloginselectdataparameters = new EndUserLoginSelectDataParameters(this.EndUser);
DataBaseHelper dbhelper = new DataBaseHelper(this.StoredprocedureName);
//执行对数据库的操作
ds=dbhelper.Run(base.ConnectionString, enduserloginselectdataparameters.Parameters);
return ds;
}
}
/// <summary>
/// 参数类
/// </summary>
public class EndUserLoginSelectDataParameters
{
public EndUserEntity EndUser { set; get; }
public SqlParameter[] Parameters { set; get; }
public EndUserLoginSelectDataParameters(EndUserEntity enduser)
{
this.EndUser = enduser;
Build();
}
private void Build()
{
SqlParameter[] parameters =
{
new SqlParameter("@Email",EndUser.UserContactInformation.Email),
new SqlParameter("@Passwrod",EndUser.Password)
};
this.Parameters = parameters;
}
}
}
这段代码有一定的难度,因为它封装了许多东西。基类DataAccessBase的代码在电子商务之框架分析(一)已经给出了。那么该类有3个属性分别为:
EndUser,
StoredprocedureName,
ConnectionString
这里构造函数只是初始化StoredprocedureName属性,将我们刚才建立的存储过程名称赋值给该属性。
这里也没有采用平常(个人的认为)写法,直接编写SqlParameter,而是较为复杂的编写了一个EndUserLoginSelectDataParameters类,该类完成了SqlPrameter的建立。最后调用个dbhelper.Run(base.ConnectionString, enduserloginselectdataparameters.Parameters)(this.ConnectionString也可以)就可以得到查询的数据。dbhelper.Run也在电子商务之框架分析(一)中有具体代码,Run是一个重载的函数,其实就是调用的SQLHlper类中的一些方法来完成对数据库的操作。
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Shop.Common;
using Shop.DataAccess.Select;
using System.Data;
namespace Shop.BusinessLogic
{
public class ProcessEndUserLogin:IBusinessLogic
{
public EndUserEntity EndUser { set; get; }
public DataSet ResultSet { set; get; }
public bool IsAuthenticated { set; get; }
public ProcessEndUserLogin()
{
}
#region IBusinessLogic 成员
public void Invoke()
{
EndUserLoginSelectData enduserloginselectdata = new EndUserLoginSelectData();
enduserloginselectdata.EndUser = this.EndUser;
this.ResultSet = enduserloginselectdata.Get();
if (ResultSet.Tables[0].Rows.Count != 0)
{
this.IsAuthenticated = true;
EndUser.EndUserID = int.Parse(this.ResultSet.Tables[0].Rows[0]["EndUserID"].ToString());
EndUser.UserName=this.ResultSet.Tables[0].Rows[0]["UserName"].ToString();
EndUser.AddressID = int.Parse(this.ResultSet.Tables[0].Rows[0]["AddressID"].ToString());
EndUser.ContactInformationID = int.Parse(this.ResultSet.Tables[0].Rows[0]["ContactInformationID"].ToString());
EndUser.Password=ResultSet.Tables[0].Rows[0]["Password"].ToString();
ProcessGetAddress getaddress = new ProcessGetAddress();
getaddress.Address.AddressID = EndUser.AddressID;
getaddress.Invoke();
EndUser.UserAddress = getaddress.Address;
ProcessGetContactInformation getcontactinformation = new ProcessGetContactInformation();
getcontactinformation.ContactInformation.ContactInformationID = EndUser.ContactInformationID;
getcontactinformation.Invoke();
EndUser.UserContactInformation = getcontactinformation.ContactInformation;
}
}
#endregion
}
}
接下来就要在业务逻辑层(Shop.BusinessLogic)添加一个ProcessAddEndUser类
该类实现了IBusinessLogic接口(该接口也在电子商务之框架分析(一)给出了)这已经很清楚了就是先要给该类属性EndUser赋值(两个参数)然后调用上面的Get方法就得到了该用户的相关信息(this.ResultSet),查看该DataSet类型数据表存不存在数据,如果没有就说明账号或密码错误,有将这些信息赋值诶EndUser类型的数据。下面的就是得到用户一些信息又去数据库查询一些而外的信息,这里就不再详细讲解。
最后在页面层的Login页面的.cs文件加入一些代码
Code
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Shop.Common;
using Shop.BusinessLogic;
public partial class Login : BasePage
{
protected void Page_Load( object sender , EventArgs e )
{
textUsername.Focus();
}
protected void commandLogin_Click( object sender , EventArgs e )
{
if ( IsValid )
{
EndUserEntity enduser = new EndUserEntity();
ProcessEndUserLogin processlogin = new ProcessEndUserLogin();
enduser.UserContactInformation.Email = textUsername.Text;
enduser.Password = textPassword.Text;
processlogin.EndUser = enduser;
try
{
processlogin.Invoke();
}
catch
{
Response.Redirect( "ErrorPage.aspx" );
}
if ( processlogin.IsAuthenticated )
{
Response.Cookies["Authenticated"].Value = "True";
base.CurrentEndUser = processlogin.EndUser;
if ( Request.Cookies["ReturnURL"] != null )
{
Response.Redirect( Request.Cookies["ReturnURL"].Value );
}
else
{
Response.Redirect("Account/CustomerOrders.aspx");
}
}
else
{
labelMessage.Text = "Invalid login!";
}
}
}
}
这里我想要说明一点就是try...catch的用法,用得非常好,如果 processlogin.Invoke();就是我们刚才写的那么多代码里发生的错误或异常在这里就可以轻松捕抓到并向用户提示页面出错,我个人以前在这方面会忽略,所以认为这里很经典
,注意到这里用户的密码没有加密,我们也可自己调用System.Web.Security.FormsAuthencation.HashPasswordForStoringInConfigFile()进行加密。下面的一些代码会涉及到很多内容我会在下面的内容和大家一起分析(注意到页面继承的BasePage类,而不是我们经常看到System.Web.UI.Page类)另外要强调的是,要注意引用这些程序集,例如我们在Shop.DataAccess添加引用项目Shop.Common类库,Shop.BusinessLogic添加引用Shop.DataAccess类库以及Shop.Common类库等等相信大家这个都会吧 -_-