引言
昨天加了一天班,今天闲来无事,就在想如何将之前的三层和最近一直在学的设计模式给联系在一起,然后就动手弄了个下面的小demo。
项目结构
项目各个层实现
Wolfy.Model层中有一个抽象类BaseModel.cs,User.cs是用户实体类,继承与BaseModel类,是用于类型安全考虑的,让各实体类有个统一的父类,在其他层使用的时候,可以使用里氏替换原则的考虑。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.Model
{
/// <summary>
/// 该抽象类为所有实体类的父类,
/// 所有实体类继承该抽象类, 为保持类型一致而设计的父类,也是出于安全性的考虑
/// </summary>
public abstract class BaseModel
{
}
}
BaseModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.Model
{
/// <summary>
/// 实体类user继承自BaseModel
/// 调用时就可以通过BaseModel model=new UserModel();
/// </summary>
public class UserModel : BaseModel
{
public int Id { get; set; }
public string UserName { set; get; }
public string Password { set; get; }
}
}
UserModel
Wolfy.FactoryDAL层是用于反射获取实例,其中只有一个类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.FactoryDAL
{
public class DataAccess<T> where T : class
{
//获取配置路径
private static readonly string path = System.Configuration.ConfigurationManager.AppSettings["DAL"];
private DataAccess() { }
/// <summary>
/// 创建实例 反射创建实例
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static T CreateDAL(string type)
{
string className = string.Format(path + ".{0}", type);
try
{
return (T)System.Reflection.Assembly.Load(path).CreateInstance(className); }
catch (Exception ex)
{
throw new Exception(ex.Message.ToString());
}
}
}
}
DataAccess
Wolfy.IDAL层依赖与Wolfy.Model,其中包含一个基接口IBaseDAL.cs,还有一个用于定义一些基接口中没有方法的接口IUserDAL,继承基接口IBaseDAL<T>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.IDAL
{
/// <summary>
/// 所有的dal基本都有增删改查等功能,提取到dal接口层,
/// 所有实现该接口的类必须实现所有的未实现的成员
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IBaseDAL<T> where T : Model.BaseModel, new()
{
bool Add(T model);
bool Detele(int ID);
bool Update(T model);
T GetModel(int ID);
}
}
IBaseDAL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.IDAL
{
/// <summary>
/// 需有特殊的方法的 可定义子接口
/// </summary>
public interface IUserDAL:IBaseDAL<Model.UserModel>
{
/// <summary>
/// 判断用户名是否存在
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
bool Exists(string userName);
/// <summary>
/// 登录
/// </summary>
/// <param name="name"></param>
/// <param name="pwd"></param>
/// <returns></returns>
Model.UserModel Login(string name, string pwd);
}
}
IUserDAL
Wolfy.DAL层,处理数据库的操作。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.SqlClient;
namespace Wolfy.DAL
{
public class UserDAL : Wolfy.IDAL.IUserDAL
{
public bool Exists(string userName)
{
string sql = "select count(*) from Users where UserName=@UserName";
SqlParameter[] sp = {
new SqlParameter("@UserName",userName)
}; return (int)SqlHelper.ExecuteScalar(CommandType.Text, sql, sp) > ;
} public bool Add(Model.UserModel model)
{
string sql = "insert into Users values(@UserName,@UserPwd)";
SqlParameter[] sp = {
new SqlParameter("@UserName",model.UserName),
new SqlParameter("@UserName",model.Password)
};
return SqlHelper.ExecuteNonQuery(CommandType.Text, sql, sp) > ;
} public bool Detele(int ID)
{
string sql = "delete from Users where id=" + ID;
return SqlHelper.ExecuteNonQuery(CommandType.Text, sql) > ;
} public bool Update(Model.UserModel model)
{
string sql = string.Format("update Users set UserName={0},UserPwd={1} where id={2}", model.UserName, model.Password, model.Id);
return SqlHelper.ExecuteNonQuery(CommandType.Text, sql) > ;
} public Model.UserModel GetModel(int ID)
{
string sql = "select * from Users where id=" + ID;
DataTable dt = SqlHelper.ExecuteDataTable(sql);
if (dt != null && dt.Rows.Count > )
{
return new Model.UserModel() { UserName = dt.Rows[]["UserName"].ToString(), Password = dt.Rows[]["UserPwd"].ToString() };
}
else
{
return null;
}
} public Model.UserModel Login(string name, string pwd)
{
Model.UserModel model = null;
string sql = "select * from Users where UserName=@UserName and UserPwd=@UserPwd";
SqlParameter[] sp = {
new SqlParameter("@UserName",name),
new SqlParameter("@UserPwd",pwd)
};
SqlDataReader reader = SqlHelper.ExecuteReader(CommandType.Text, sql, sp);
if (reader != null && !reader.IsClosed && reader.HasRows)
{
model = new Model.UserModel();
while (reader.Read())
{
model.Id = Convert.ToInt32(reader[]);
model.UserName = reader[].ToString();
model.Password = reader[].ToString();
}
}
reader.Dispose();
return model;
}
}
}
UserDAL
using System;
using System.Data;
using System.Xml;
using System.Data.SqlClient;
using System.Collections;
using System.Configuration;
using System.IO;
using System.Web; namespace Wolfy.DAL
{
/// <summary>
/// 数据库的通用访问代码
/// 此类为抽象类,不允许实例化,在应用时直接调用即可
/// </summary>
public abstract class SqlHelper
{
//获取数据库连接字符串,其属于静态变量且只读,项目中所有文档可以直接使用,但不能修改
public static readonly string connectionString = ConfigurationManager.ConnectionStrings["SqlConnect"].ConnectionString; // 哈希表用来存储缓存的参数信息,哈希表可以存储任意类型的参数。
private static Hashtable parmCache = Hashtable.Synchronized(new Hashtable()); /// <summary>
///执行一个不需要返回值的SqlCommand命令,通过指定专用的连接字符串。
/// 使用参数数组形式提供参数列表
/// </summary>
/// <remarks>
/// 使用示例:
/// int result = ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一个有效的数据库连接字符串</param>
/// <param name="commandType">SqlCommand命令类型 (存储过程, T-SQL语句, 等等。)</param>
/// <param name="commandText">存储过程的名字或者 T-SQL 语句</param>
/// <param name="commandParameters">以数组形式提供SqlCommand命令中用到的参数列表</param>
/// <returns>返回一个数值表示此SqlCommand命令执行后影响的行数</returns>
public static int ExecuteNonQuery(CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
using (SqlConnection conn = new SqlConnection(connectionString))
{
//通过PrePareCommand方法将参数逐个加入到SqlCommand的参数集合中
PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
//清空SqlCommand中的参数列表
cmd.Parameters.Clear();
return val;
}
} /// <summary>
///执行一条不返回结果的SqlCommand,通过一个已经存在的数据库连接
/// 使用参数数组提供参数
/// </summary>
/// <remarks>
/// 使用示例:
/// int result = ExecuteNonQuery(conn, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="conn">一个现有的数据库连接</param>
/// <param name="commandType">SqlCommand命令类型 (存储过程, T-SQL语句, 等等。)</param>
/// <param name="commandText">存储过程的名字或者 T-SQL 语句</param>
/// <param name="commandParameters">以数组形式提供SqlCommand命令中用到的参数列表</param>
/// <returns>返回一个数值表示此SqlCommand命令执行后影响的行数</returns>
public static int ExecuteNonQuery(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
} /// <summary>
/// 执行一条不返回结果的SqlCommand,通过一个已经存在的数据库事物处理
/// 使用参数数组提供参数
/// </summary>
/// <remarks>
/// 使用示例:
/// int result = ExecuteNonQuery(trans, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="trans">一个存在的 sql 事物处理</param>
/// <param name="commandType">SqlCommand命令类型 (存储过程, T-SQL语句, 等等。)</param>
/// <param name="commandText">存储过程的名字或者 T-SQL 语句</param>
/// <param name="commandParameters">以数组形式提供SqlCommand命令中用到的参数列表</param>
/// <returns>返回一个数值表示此SqlCommand命令执行后影响的行数</returns>
public static int ExecuteNonQuery(SqlTransaction trans, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
} /// <summary>
/// 执行一条返回结果集的SqlCommand命令,通过专用的连接字符串。
/// 使用参数数组提供参数
/// </summary>
/// <remarks>
/// 使用示例:
/// SqlDataReader r = ExecuteReader(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一个有效的数据库连接字符串</param>
/// <param name="commandType">SqlCommand命令类型 (存储过程, T-SQL语句, 等等。)</param>
/// <param name="commandText">存储过程的名字或者 T-SQL 语句</param>
/// <param name="commandParameters">以数组形式提供SqlCommand命令中用到的参数列表</param>
/// <returns>返回一个包含结果的SqlDataReader</returns>
public static SqlDataReader ExecuteReader(CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
SqlConnection conn = new SqlConnection(connectionString);
// 在这里使用try/catch处理是因为如果方法出现异常,则SqlDataReader就不存在,
//CommandBehavior.CloseConnection的语句就不会执行,触发的异常由catch捕获。
//关闭数据库连接,并通过throw再次引发捕捉到的异常。
try
{
PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
cmd.Parameters.Clear();
return rdr;
}
catch
{
conn.Close();
throw;
}
} /// <summary>
/// 执行一条返回第一条记录第一列的SqlCommand命令,通过专用的连接字符串。
/// 使用参数数组提供参数
/// </summary>
/// <remarks>
/// 使用示例:
/// Object obj = ExecuteScalar(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一个有效的数据库连接字符串</param>
/// <param name="commandType">SqlCommand命令类型 (存储过程, T-SQL语句, 等等。)</param>
/// <param name="commandText">存储过程的名字或者 T-SQL 语句</param>
/// <param name="commandParameters">以数组形式提供SqlCommand命令中用到的参数列表</param>
/// <returns>返回一个object类型的数据,可以通过 Convert.To{Type}方法转换类型</returns>
public static object ExecuteScalar(CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
using (SqlConnection connection = new SqlConnection(connectionString))
{
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
}
} /// <summary>
/// 执行一条返回第一条记录第一列的SqlCommand命令,通过已经存在的数据库连接。
/// 使用参数数组提供参数
/// </summary>
/// <remarks>
/// 使用示例:
/// Object obj = ExecuteScalar(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="conn">一个已经存在的数据库连接</param>
/// <param name="commandType">SqlCommand命令类型 (存储过程, T-SQL语句, 等等。)</param>
/// <param name="commandText">存储过程的名字或者 T-SQL 语句</param>
/// <param name="commandParameters">以数组形式提供SqlCommand命令中用到的参数列表</param>
/// <returns>返回一个object类型的数据,可以通过 Convert.To{Type}方法转换类型</returns>
public static object ExecuteScalar(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
} /// <summary>
/// 缓存参数数组
/// </summary>
/// <param name="cacheKey">参数缓存的键值</param>
/// <param name="cmdParms">被缓存的参数列表</param>
public static void CacheParameters(string cacheKey, params SqlParameter[] commandParameters)
{
parmCache[cacheKey] = commandParameters;
} /// <summary>
/// 获取被缓存的参数
/// </summary>
/// <param name="cacheKey">用于查找参数的KEY值</param>
/// <returns>返回缓存的参数数组</returns>
public static SqlParameter[] GetCachedParameters(string cacheKey)
{
SqlParameter[] cachedParms = (SqlParameter[])parmCache[cacheKey];
if (cachedParms == null)
return null;
//新建一个参数的克隆列表
SqlParameter[] clonedParms = new SqlParameter[cachedParms.Length];
//通过循环为克隆参数列表赋值
for (int i = , j = cachedParms.Length; i < j; i++)
//使用clone方法复制参数列表中的参数
clonedParms[i] = (SqlParameter)((ICloneable)cachedParms[i]).Clone();
return clonedParms;
} /// <summary>
/// 为执行命令准备参数
/// </summary>
/// <param name="cmd">SqlCommand 命令</param>
/// <param name="conn">已经存在的数据库连接</param>
/// <param name="trans">数据库事物处理</param>
/// <param name="cmdType">SqlCommand命令类型 (存储过程, T-SQL语句, 等等。)</param>
/// <param name="cmdText">Command text,T-SQL语句 例如 Select * from Products</param>
/// <param name="cmdParms">返回带参数的命令</param>
private static void PrepareCommand(SqlCommand cmd, SqlConnection conn, SqlTransaction trans, CommandType cmdType, string cmdText, SqlParameter[] cmdParms)
{
//判断数据库连接状态
if (conn.State != ConnectionState.Open)
conn.Open();
cmd.Connection = conn;
cmd.CommandText = cmdText;
//判断是否需要事物处理
if (trans != null)
cmd.Transaction = trans;
cmd.CommandType = cmdType;
if (cmdParms != null)
{
foreach (SqlParameter parm in cmdParms)
cmd.Parameters.Add(parm);
}
} /// <summary>
/// 获取dataset数据
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public static DataTable ExecuteDataTable(string sql)
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlDataAdapter da = new SqlDataAdapter(sql, conn);
DataTable dst = new DataTable();
da.Fill(dst);
return dst;
}
}
}
}
SqlHelper
Wolfy.BLL业务逻辑层中包含了一个用于继承的基类BaseBLL<T>和用户业务逻辑UserBLL类,这层依赖Wolfy.IDAL,Wolfy.Model,Wolfy.FactoryDAL库
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.BLL
{
public class BaseBLL<T> where T : Wolfy.Model.UserModel, new()
{
protected Wolfy.IDAL.IBaseDAL<T> Dal;
public BaseBLL(string type)
{
//通过工厂得到 dal
Dal = Wolfy.FactoryDAL.DataAccess<Wolfy.IDAL.IBaseDAL<T>>.CreateDAL(type);
}
public virtual bool Add(T model)
{
return Dal.Add(model);
}
public virtual bool Delete(int ID)
{ return Dal.Detele(ID); }
public virtual bool Update(T model)
{ return Dal.Update(model); }
public virtual T GetModel(int ID)
{
return Dal.GetModel(ID);
} }
}
BaseBLL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.BLL
{
public class UserBLL : BaseBLL<Wolfy.Model.UserModel>
{
private const string _Type = "UserDAL";
private Wolfy.IDAL.IUserDAL _DAL;
public UserBLL()
: base(_Type)
{
_DAL = base.Dal as Wolfy.IDAL.IUserDAL;
if (_DAL == null)
{
throw new NullReferenceException(_Type);
}
}
public bool Exists(string userName)
{
return _DAL.Exists(userName);
}
public Model.UserModel Login(string name, string pwd)
{ return _DAL.Login(name, pwd); }
}
}
UserBLL
web.config程序集名称,连接字符串配置
<?xml version="1.0" encoding="utf-8"?>
<!--
有关如何配置 ASP.NET 应用程序的详细信息,请访问
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" /> </system.web>
<connectionStrings>
<add name="SqlConnect" connectionString="server=.;database=Test;uid=sa;pwd=sa"/>
</connectionStrings>
<appSettings>
<add key="DAL" value="Wolfy.DAL"/>
</appSettings>
</configuration>
web.config
测试
简单的ajax登录,web项目需引用Wolfy.BLL.dll和Wolfy.Model.dll和Wolfy.DAL.dll
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>wolfy信息系统登录</title>
<script type="text/javascript" src="Scripts/jquery-1.11.0.js"></script>
<script type="text/javascript">
$(function () {
$("#btnLogin").click(function () {
var name = $("#txtUserName").val();
var pwd = $("#txtPwd").val();
$.ajax({
url: "Ashx/Login.ashx",
data: "name=" + name + "&pwd=" + pwd,
type: "Post",
dataType: "text",
success: function (msg) {
if (msg == "1") {
$("#divMsg").html("登录成功");
} else if(msg=="2") {
$("#divMsg").html("用户名或密码为空");
} else if(msg=="3"){
$("#divMsg").html("用户名不存在");
} else {
$("#divMsg").html("密码错误");
}
} });
});
});
</script>
</head>
<body>
<table>
<tr>
<td>用户名:</td>
<td><input type="text" id="txtUserName" name="name" value="admin" /></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" id="txtPwd" name="name" value="admin" /></td>
</tr>
<tr>
<td colspan="2"><input type="button" id="btnLogin" name="name" value="登录" /></td>
</tr>
</table>
<div id="divMsg"></div>
</body>
</html>
Login.html
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace Wolfy.LoginDemo.Ashx
{
/// <summary>
/// Login 的摘要说明
/// </summary>
public class Login : IHttpHandler
{ public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
//接收用户名和密码
string name = context.Request["name"];
string pwd = context.Request["pwd"];
if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pwd))
{
context.Response.Write("");
}
else
{
BLL.UserBLL bll = new BLL.UserBLL();
Model.UserModel model = new Model.UserModel();
if (!bll.Exists(name))
{
context.Response.Write("");
}
else
{
model = bll.Login(name, pwd);
if (model != null)
{
//登录成功记入cookie
context.Response.Cookies["n"].Value = name;
context.Response.Cookies["n"].Expires = DateTime.Now.AddDays();
context.Response.Cookies["p"].Value = pwd;
context.Response.Cookies["p"].Expires = DateTime.Now.AddDays();
context.Response.Write("");
}
} }
} public bool IsReusable
{
get
{
return false;
}
}
}
}
Login.ashx
结果
源码下载:http://pan.baidu.com/s/1hqqSUrU
总结
参考文章:http://www.cnblogs.com/cmsdn/p/3494461.html
参考着这篇文章,敲了一遍,实践了一下。在博客园,虽然有很多提供代码的园友,但是自己动手实践一下,就可以发现其中好与不好的地方,也可以加深理解。