三層架構,也有人叫做三層結構,是一種軟體架構。
三層架構主要包括了表現層(UI)、業務邏輯層(BLL)和資料訪問層(DAL)。這樣的方式將軟體開發進行分層,每層之間的分工是相對的比較明確的,而且很容易去理解。一般客戶端不是直接與資料庫進行互動的,而是與中間層(業務邏輯層)建立的連線。三層架構不只適用於在web當中的開發,在別的一些地方也是很有多用途的。
我這裡將會以我自己寫的一個ASP.NET三層架構為例子來學習理解它。剛開始學習.NET的時候,老師教的就是三層架構,但是三層架構不是萬能的,也不是你寫所有的.NET程式都用三層架構。我覺得最重要的是通過自己去學習別人的專案,自己親自動手去寫它來理解其中的架構思想。每一種架構思想,基本上都可以在不同的語言環境下面實現,而且當你通過自己的理解去實現它的時候你會有不一樣的認識。
我這裡所有寫的都是以我自己的認識理解寫的,我是一個菜鳥,也是一個程式猿,但我深愛著IT。如果有哪裡不正確或者不當的地方,可以隨便提出來或者聯絡我,交流中共同進步!
我所寫的是一個學生作業管理系統,利用ASP.net編寫,整個軟體架構使用了三層架構,前端框架使用了Bootstrap。原始碼直接點選此處下載。如果想對.NET三層架構有深入瞭解的,我推薦動力啟航(http://www.dtcms.net/)大家可以去學習一下。
第一步,在我們建立.NET三層架構之前,我們得首先知道這三層分別的用處。
表現層:可以和使用者直接接觸的一層,如果你寫B/S模式下的三層架構,那麼表現層通俗的理解為就是我們所看到的web頁面。表現層可以顯示資料和接收使用者輸入的資料,為使用者提供一種互動介面。
業務邏輯層:這一層也算是你的業務處理中最重要的,如果你的業務比較的繁瑣,這一層是核心。一般我們進行開發,這一層主要集中在業務規則的制定、業務流程的實現等與業務需求有關的系統設計。
資料訪問層:其實從字面我們就知道這一層用來進行資料庫的訪問,這裡要注意的是,這裡除了資料庫之外,我們的文字檔案、XML文件也算是資料,對其的訪問也可以是叫做資料訪問。比如在我的專案裡面我會將一些系統的配置儲存到xml檔案裡面。
第二步,新建asp.net專案,在新建專案之前,首先要對我們的專案的命名有個統一的規範,包括自己專案當中的引數命名、資料庫命名等都要有一個統一的命名規範,寫一個規範的專案,我個人覺得命名是第一步。對於C#的命名規範這裡不過多的講解。
我的解決方案的專案整個如下圖所示:
其中XGhms.BLL表示業務邏輯層、XGhms.DAL表示資料訪問層、XGhms.Web表示UI表現層。雖然這幾層就可以簡單的組成一個專案,但是往往在實際開發當中,我們還要考慮別的方面,比如如何的節省開發,如果該系統要與別的系統多對接該如何實現等等,所以XGhms.Helper是一個公共的層,裡面包括了公共的一些方法,比如字串的加密、XML文件的操作等。XGhms.Model層是一個模型(實體類),在三層之間傳遞資料的,我這裡Model層裡面的每一個類對應資料庫裡面的每一張表,這樣方面操作。XGhms.Web.Services是為了後面網站如果進行與別的系統做對接或者提供提供相應的介面服務,都可以在裡面實現。
第三步,每個專案開始之前,最重要的還是做需求分析,比如我的學生作業管理系統,你要花大量的時間去了解和實際的去調查,需求做好才能進行下面的工作。這裡就不做大量的解說,如果後面有時間了專門寫博探討需求分析上面的事宜。
這是資料庫的設計,這張關係圖畫的將就一下吧,o(╯□╰)o 資料庫的原始碼都可以直接下載的。
第四步,關於Model層還有BLL層和DAL層前面講了,現在有好多的軟體可以直接生成這些層,不用我們去敲程式碼,比如動軟程式碼生成器,但是我認為第一次去學習三層架構,程式碼還是自己動手去敲的比較好。
下面這是Model層的程式碼示例:
using System;
namespace XGhms.Model
{
public partial class course
{
private int _id;
///
/// 自增ID
///
public int id
{
get { return _id; }
set { _id = value; }
}
private string _course_number;
///
/// 課程號碼(專業課程編碼)
///
public string course_number
{
get { return _course_number; }
set { _course_number = value; }
}
private string _course_name;
///
/// 課程名稱
///
public string course_name
{
get { return _course_name; }
set { _course_name = value; }
}
private int _term_id;
///
/// 學期ID
///
public int term_id
{
get { return _term_id; }
set { _term_id = value; }
}
private string _teacher;
///
/// 課程老師(主教)
///
public string teacher
{
get { return _teacher; }
set { _teacher = value; }
}
private string _other_teacher;
///
/// 助教
///
public string other_teacher
{
get { return _other_teacher; }
set { _other_teacher = value; }
}
private string _student_leader;
///
/// 該課程學生負責人
///
public string student_leader
{
get { return _student_leader; }
set { _student_leader = value; }
}
private string _course_info;
///
/// 該課程的資訊
///
public string course_info
{
get { return _course_info; }
set { _course_info = value; }
}
private int _college_id;
///
/// 學院ID
///
public int college_id
{
get { return _college_id; }
set { _college_id = value; }
}
}
}
要連線運算元據庫,經典的你可以選擇ADO.NET,也可以用Entity Framework,現在正在學習Entity Framework,有時間分享心得。
DAL層程式碼示例:
using System;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using XGhms.Helper;
namespace XGhms.DAL
{
///
/// 資料訪問類:course
///
public partial class course
{
#region 基本方法=============================================
///
/// 是否存在該記錄
///
/// ID
/// true or false
public bool Exists(int id)
{
StringBuilder strSql = new StringBuilder();
strSql.Append("select count(1) from xg_course");
strSql.Append(" where [email protected] ");
SqlParameter[] parameters = {
new SqlParameter("@id", SqlDbType.Int,4)};
parameters[0].Value = id;
return SQLHelper.Exists(strSql.ToString(), parameters);
}
///
/// 根據課程ID和老師ID檢查是否存在該課程
///
/// 課程ID
/// 老師ID
/// 是否存在
public bool Exists(int id, int teacherID)
{
StringBuilder strSql = new StringBuilder();
strSql.Append("select count(1) from xg_course");
strSql.Append(" where id=" id);
strSql.Append(" and teacher='" teacherID "'");
return SQLHelper.Exists(strSql.ToString());
}
///
/// 根據ID來刪除相應的資料
///
/// ID
/// true or false
public bool Delete(int id)
{
StringBuilder strSql = new StringBuilder();
strSql.Append("delete from xg_course ");
strSql.Append(" where [email protected]");
SqlParameter[] parameters = {
new SqlParameter("@id", SqlDbType.Int,4)};
parameters[0].Value = id;
int rows = SQLHelper.ExecuteSql(strSql.ToString(), parameters);
if (rows > 0)
{
return true;
}
else
{
return false;
}
}
///
/// 查詢id得到一個物件實體
///
/// 課程id
/// model物件
public Model.course GetModel(int id)
{
SqlParameter[] parameters = {
new SqlParameter("@id", SqlDbType.Int,4)};
parameters[0].Value = id;
Model.course model = new Model.course();
DataSet ds = SQLHelper.SelectSqlReturnDataSet("course_SelectCourseById", parameters, CommandType.StoredProcedure);
if (ds.Tables[0].Rows.Count > 0)
{
if (ds.Tables[0].Rows[0]["id"] != null && ds.Tables[0].Rows[0]["id"].ToString() != "")
{
model.id = int.Parse(ds.Tables[0].Rows[0]["id"].ToString());
}
if (ds.Tables[0].Rows[0]["course_number"] != null && ds.Tables[0].Rows[0]["course_number"].ToString() != "")
{
model.course_number = ds.Tables[0].Rows[0]["course_number"].ToString();
}
if (ds.Tables[0].Rows[0]["course_name"] != null && ds.Tables[0].Rows[0]["course_name"].ToString() != "")
{
model.course_name = ds.Tables[0].Rows[0]["course_name"].ToString();
}
if (ds.Tables[0].Rows[0]["term_id"] != null && ds.Tables[0].Rows[0]["term_id"].ToString() != "")
{
model.term_id = int.Parse(ds.Tables[0].Rows[0]["term_id"].ToString());
}
if (ds.Tables[0].Rows[0]["teacher"] != null && ds.Tables[0].Rows[0]["teacher"].ToString() != "")
{
model.teacher = ds.Tables[0].Rows[0]["teacher"].ToString();
}
if (ds.Tables[0].Rows[0]["other_teacher"] != null && ds.Tables[0].Rows[0]["other_teacher"].ToString() != "")
{
model.other_teacher = ds.Tables[0].Rows[0]["other_teacher"].ToString();
}
if (ds.Tables[0].Rows[0]["student_leader"] != null && ds.Tables[0].Rows[0]["student_leader"].ToString() != "")
{
model.student_leader = ds.Tables[0].Rows[0]["student_leader"].ToString();
}
if (ds.Tables[0].Rows[0]["course_info"] != null && ds.Tables[0].Rows[0]["course_info"].ToString() != "")
{
model.course_info = ds.Tables[0].Rows[0]["course_info"].ToString();
}
if (ds.Tables[0].Rows[0]["college_id"] != null && ds.Tables[0].Rows[0]["college_id"].ToString() != "")
{
model.college_id = int.Parse(ds.Tables[0].Rows[0]["college_id"].ToString());
}
return model;
}
else
{
return null;
}
}
///
/// 插入新的課程(管理員插入)
///
/// 課程編號
/// 課程名
/// 學期ID
/// 老師ID
/// 學院ID
/// 受影響的行數
public int Insert(string course_number, string course_name,int tremID,int terID,int collegeID)
{
StringBuilder str = new StringBuilder();
str.Append("INSERT INTO [xg_course]([course_number],[course_name],[term_id],[teacher],[college_id])");
str.Append(" VALUES(@course_number,@course_name,");
str.Append(tremID ",");
str.Append("'" terID "',");
str.Append(collegeID ")");
SqlParameter[] parameters = {
new SqlParameter("@course_number", SqlDbType.NVarChar,50),
new SqlParameter("@course_name",SqlDbType.NVarChar,50)};
parameters[0].Value = course_number;
parameters[1].Value = course_name;
return SQLHelper.ExecuteSql(str.ToString(), parameters);
}
///
/// 老師更新課程資訊
///
/// 課程ID
/// 助教ID
/// 學生ID
/// 課程資訊
/// 受影響的行數
public int Update(int courseID, string oTerID, string stuID, string courInfo)
{
StringBuilder str = new StringBuilder();
str.Append("UPDATE [xg_course]");
str.Append(" SET other_teacher [email protected]_teacher,");
str.Append("student_leader [email protected]_leader,");
str.Append("course_info = @course_info");
str.Append(" where id=" courseID);
SqlParameter[] parameters = {
new SqlParameter("@other_teacher", SqlDbType.NVarChar,100),
new SqlParameter("@student_leader",SqlDbType.NVarChar,50),
new SqlParameter("@course_info",SqlDbType.NVarChar,500)};
parameters[0].Value = oTerID;
parameters[1].Value = stuID;
parameters[2].Value = courInfo;
return SQLHelper.ExecuteSql(str.ToString(), parameters);
}
///
/// 根據課程的ID更新相應的課程
///
/// 課程ID
/// 課程編號
/// 課程名
/// 學期ID
/// 老師ID
/// 學院ID
/// 受影響的行數
public int UpdateOldCourse(int courseID, string course_number, string course_name, int tremID, int terID, int collegeID)
{
StringBuilder str = new StringBuilder();
str.Append("UPDATE [xg_course]");
str.Append(" SET [course_number] [email protected]_number,");
str.Append("[course_name] [email protected]_name,");
str.Append("[term_id] = " tremID);
str.Append(",[teacher] = '" terID "'");
str.Append(",[college_id] = " collegeID);
str.Append(" where id=" courseID);
SqlParameter[] parameters = {
new SqlParameter("@course_number", SqlDbType.NVarChar,50),
new SqlParameter("@course_name",SqlDbType.NVarChar,50)};
parameters[0].Value = course_number;
parameters[1].Value = course_name;
return SQLHelper.ExecuteSql(str.ToString(), parameters);
}
///
/// 根據學期ID和學院ID獲取課程數目
///
/// 課程ID
/// 學院ID
/// 課程數目
public int GetNumTotalByTremIdandCollegeID(int tremID,int collegeID)
{
string sql = "select count(id) from xg_course where term_id=" tremID " and college_id=" collegeID;
object ob = SQLHelper.GetSingle(sql);
if (ob == null)
{
return 0;
}
else
{
return Convert.ToInt32(ob);
}
}
///
/// 根據學期和學院來分頁顯示課程
///
/// 學期ID
/// 學院ID
/// 開始的數目
/// 結束的數目
/// DataTable
public DataTable GetSelectByTremandCollegeandPage(int tremID, int collegeID, int PageBeginNum, int PageEndNum)
{
SqlParameter[] parameters = {
new SqlParameter("@term_id", SqlDbType.Int,6),
new SqlParameter("@college_id", SqlDbType.Int,6),
new SqlParameter("@PageBeginNum", SqlDbType.Int,6),
new SqlParameter("@PageEndNum", SqlDbType.Int,6)};
parameters[0].Value = tremID;
parameters[1].Value = collegeID;
parameters[2].Value = PageBeginNum;
parameters[3].Value = PageEndNum;
using (DataSet ds = SQLHelper.SelectSqlReturnDataSet("course_SelectByTremandCollegeandPage", parameters, CommandType.StoredProcedure))
{
return ds.Tables[0];
}
}
///
/// 根據學期和老師來查詢課程
///
/// 老師ID
/// 學期ID
/// 課程列表
public DataTable GetCourseListByTerTrem(int terID, int tremID)
{
StringBuilder str = new StringBuilder();
str.Append("select id from xg_course where");
str.Append(" term_id=" tremID);
str.Append(" and teacher='" terID "'");
using (DataSet ds = SQLHelper.Query(str.ToString()))
{
return ds.Tables[0];
}
}
///
/// 根據學期和學生ID來獲取課程列表
///
/// 學生ID
/// 學期ID
/// 課程列表
public DataTable GetCourseListByStuTrem(int stuID, int tremID)
{
StringBuilder str = new StringBuilder();
str.Append("select xg_course.id from xg_course,xg_course_student ");
str.Append("where xg_course.id=xg_course_student.course_id and xg_course_student.student_id=" stuID);
str.Append(" and term_id=" tremID);
using (DataSet ds = SQLHelper.Query(str.ToString()))
{
return ds.Tables[0];
}
}
#endregion
}
}
BLL業務邏輯層程式碼示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace XGhms.BLL
{
///
/// 課程管理
///
public partial class course
{
DAL.course_student courstuDal = new DAL.course_student();
DAL.course coursDal = new DAL.course();
///
/// 根據課程ID和老師ID檢查是否存在該課程
///
/// 課程ID
/// 老師ID
/// 是否存在
public bool Exists(int id, int teacherID)
{
return coursDal.Exists(id, teacherID);
}
///
/// 查詢id得到一個物件實體
///
/// 課程id
/// model物件
public Model.course GetModel(int id)
{
return coursDal.GetModel(id);
}
public List GetCourListOfstuID(int stuID,int pageBeginNum,int pageEndNum)
{
DataTable courListDT = courstuDal.GetListOfStuID(stuID, pageBeginNum, pageEndNum);
List listModel=new List();
for (int i = 0; i < courListDT.Rows.Count; i )
{
Model.course courModel=coursDal.GetModel(Convert.ToInt32(courListDT.Rows[i][0]));
listModel.Add(courModel);
}
return listModel;
}
///
/// 根據ID來刪除相應的資料
///
/// ID
/// true or false
public bool Delete(int id)
{
return coursDal.Delete(id);
}
public bool Exists(int id)
{
return coursDal.Exists(id);
}
///
/// 插入新的課程(管理員插入)
///
/// 課程編號
/// 課程名
/// 學期ID
/// 老師ID
/// 學院ID
/// 受影響的行數
public int Insert(string course_number, string course_name, int tremID, int terID, int collegeID)
{
return coursDal.Insert(course_number, course_name, tremID, terID, collegeID);
}
///
/// 根據課程的ID更新相應的課程
///
/// 課程ID
/// 課程編號
/// 課程名
/// 學期ID
/// 老師ID
/// 學院ID
/// 受影響的行數
public int UpdateOldCourse(int courseID, string course_number, string course_name, int tremID, int terID, int collegeID)
{
return coursDal.UpdateOldCourse( courseID, course_number, course_name, tremID, terID, collegeID);
}
///
/// 根據學期ID和學院ID獲取課程數目
///
/// 課程ID
/// 學院ID
/// 課程數目
public int GetNumTotalByTremIdandCollegeID(int tremID, int collegeID)
{
return coursDal.GetNumTotalByTremIdandCollegeID(tremID, collegeID);
}
///
/// 根據學期和學院來分頁顯示課程
///
/// 學期ID
/// 學院ID
/// 開始的數目
/// 結束的數目
/// DataTable
public DataTable GetSelectByTremandCollegeandPage(int tremID, int collegeID, int PageBeginNum, int PageEndNum)
{
return coursDal.GetSelectByTremandCollegeandPage( tremID, collegeID, PageBeginNum, PageEndNum);
}
///
/// 根據學期和老師來查詢課程
///
/// 老師ID
/// 學期ID
/// 課程列表
public DataTable GetCourseListByTerTrem(int terID, int tremID)
{
return coursDal.GetCourseListByTerTrem(terID, tremID);
}
///
/// 老師更新課程資訊
///
/// 課程ID
/// 助教ID
/// 學生ID
/// 課程資訊
/// 受影響的行數
public int Update(int courseID, string oTerID, string stuID, string courInfo)
{
return coursDal.Update(courseID, oTerID, stuID, courInfo);
}
///
/// 根據學期和學生ID來獲取課程列表
///
/// 學生ID
/// 學期ID
/// 課程列表
public DataTable GetCourseListByStuTrem(int stuID, int tremID)
{
return coursDal.GetCourseListByStuTrem(stuID, tremID);
}
}
}
前端展示UI層,我使用了Bootstrap框架。有興趣的可以學習一下(http://www.bootcss.com/)使用Ajax,拋棄了一些沒必要的伺服器控制元件,減少頁面載入時間。(不過微軟出那麼多的伺服器控制元件自然有其中的原因,要真的理解每個控制元件的用法不容易,還得努力!)
前端頁面程式碼示例:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="MyCourse.aspx.cs" Inherits="XGhms.Web.Teacher.CourseControls.MyCourse" %> <%@ Register TagPrefix="uctrols" TagName="terTopNav" Src="~/Teacher/MyControls/TerTopNav.ascx" %> <%@ Register TagPrefix="uctrols" TagName="terLeftNav" Src="~/Teacher/MyControls/TerLeftNav.ascx" %> <%@ Register TagPrefix="uctrols" TagName="downFooter" Src="~/MyControls/DownFooter.ascx" %> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>我的課程 - XGhms</title> <link href="../../Style/bootstrap.css" rel="stylesheet" type="text/css" /> <link href="../../Style/site.css" rel="stylesheet" type="text/css" /> <link href="../../Style/bootstrap-responsive.css" rel="stylesheet" type="text/css" /> <!--[if lt IE 9]> <script src="http://cdn.bootcss.com/html5shiv/3.7.0/html5shiv.min.js"></script> <![endif]--> </head> <body> <form id="form1" runat="server"> <uctrols:terTopNav ID="topNav" runat="server" /> <div class="container-fluid"> <div class="row-fluid"> <uctrols:terLeftNav ID="leftNav" runat="server" /> <div class="span9"> <div class="row-fluid"> <div class="page-header"> <h1><span id="title_h1">我的課程</span> <small id="h1small"> </small></h1> </div> <div id="oneDiv" class="form-horizontal"> <fieldset> <div class="control-group"> <label class="control-label" for="collegeName">選擇學期</label> <div class="controls"> <select onchange="SelectTerm()" id="selTerm"><option value="0">請選擇</option></select> </div> </div> </fieldset> </div> <table class="table table-striped table-bordered table-condensed" style="width:95%"> <thead> <tr> <th>ID</th> <th>課程編號</th> <th>課程名</th> <th>課程老師</th> <th>所在學院</th> <th>操作</th> </tr> </thead> <tbody id="defaultAddTr"> </tbody> </table> </div> </div> </div> <uctrols:downFooter ID="AllDownFooter" runat="server" /> </div> </form> </body> </html> <script type="text/javascript"> $(function () { $("#left_nav_ul li:eq(4)").attr('class', 'active'); //獲取相應的li,設定class為active $.ajax({ //新增學期 type: "post", dataType: "json", url: "/Handles/TeacherHandler.ashx", data: "action=GetAllTermForNow", success: function (data) { $.each(data.termList, function (index, item) { if (item.termCheck == 1) { col_addSelect("#selTerm", item.id, item.termName, item.id); $('#h1small').append(item.termName); GetCourselistByTrem(item.id); } else { col_add("#selTerm", item.id, item.termName); } }); } }); }); //當學期選項變化時候 function SelectTerm() { if ($('#selTerm option:selected').val() == 0) { return; } $('#defaultAddTr').empty(); $('#h1small').empty(); $('#h1small').append($('#selTerm option:selected').text()); GetCourselistAll(); } //查詢資料 function GetCourselistByTrem(tremID) { $.ajax({ //獲取當前的查詢條件下面的條數 type: "post", dataType: "json", async: false, url: "/Handles/TeacherHandler.ashx", data: "action=GetTerCourseByTrem&tremID=" tremID, success: function (data) { $.each(data.courseList, function (index, item) { AddTrList(item.id, item.course_number, item.course_name, item.teacher, item.college); }); } }); } //查詢資料 function GetCourselistAll() { var tremID = $('#selTerm option:selected').val(); $.ajax({ //獲取當前的查詢條件下面的條數 type: "post", dataType: "json", async: false, url: "/Handles/TeacherHandler.ashx", data: "action=GetTerCourseByTrem&tremID=" tremID, success: function (data) { $('#defaultAddTr').empty(); $.each(data.courseList, function (index, item) { AddTrList(item.id, item.course_number, item.course_name, item.teacher, item.college); }); } }); } function AddTrList(ID, courseNum, courseName, teacher, collegeName) { var newHW = '<li><a href="CourseManage.aspx?id=' ID '"><i class="icon-pencil"></i> 修改課程</a></li><li><a href="../HomeWorkControls/HomeWorkManage.aspx?cid=' ID '"><i class="icon-plus-sign"></i> 新增作業</a></li><li><a href="CourseStatistics.aspx?id=' ID '"><i class="icon-signal"></i> 作業統計</a></li>'; $('#defaultAddTr').append('<tr class="list-users"><td>' ID '</td><td>' courseNum '</td><td>' courseName '</td><td>' teacher '</td><td>' collegeName '</td><td><div class="btn-group"><a class="btn btn-mini dropdown-toggle" data-toggle="dropdown" href="#">操作 <span class="caret"></span></a><ul class="dropdown-menu">' newHW '</ul></div></td></tr>'); } // 新增下拉選單選項根據已選擇的 function col_addSelect(selectobj, value, text, selectValue) { var selObj = $(selectobj); if (value == selectValue) { selObj.append("<option selected='selected' value='" value "'>" text "</option>"); } else { selObj.append("<option value='" value "'>" text "</option>"); } } // 新增下拉選單選項 function col_add(selectobj, value, text) { var selObj = $(selectobj); selObj.append("<option value='" value "'>" text "</option>"); } // 刪除 function col_delete(selectobj) { var selOpt = $(selectobj " option:selected"); selOpt.remove(); } // 清空下拉選單選項並且加一個全選的按鈕 function col_clear(colobject) { var selOpt = $(colobject " option"); selOpt.remove(); col_add(colobject, 0, "請選擇"); //預設新增有全部請選擇的選項 } </script>
這差不多就是一個簡單.NET三層架構的例子。至於學生作業管理系統的整個專案的流程還有模組的實現,以後有時間我會寫好world文件上傳到原始碼文件裡面。所有程式碼我已經開源,大家可以訪問下載參考,(例子僅供參考,引用說明出處,專案用於商業請聯絡作者,謝謝!)
写评论
很抱歉,必須登入網站才能發佈留言。