花了两天,整理了一下三层架构,顺便练了一个小型三层架构——工程报价系统。
功能很简单,完成基本的增删改查
-
搭建项目三层结构
-
界面的设计以及美化
-
分层代码的设计与实现
-
模型层:由于表设计时存在主外键关系,故把表映射成对象时添加一个外键对象来保存外键那张表的相关数据。
// 外键实体
private Product _product;
private Project _project;
public Project _Project
{
get { return _project; }
set { _project = value; }
}
public Product _Product
{
get { return _product; }
set { _product = value; }
}
2.数据访问层:把数据封装成Model对象的各层之间传递。
我个人不喜欢三层之间传递DataSet数据集,我的理解是尽量不在BLL层出现DataSet,所以我在DAL层把所有获取的数据封装成IEnumerable集合,然后返回。
protected IEnumerable<ProjectItem> ToModelsByFK(SqlDataReader reader)
{
var list = new List<ProjectItem>();
while (reader.Read())
{
list.Add(ToModelByFK(reader));
}
return list;
}
public ProjectItem ToModelByFK(SqlDataReader reader)
{
ProjectItem projectItem = new ProjectItem();
projectItem._Product = new Product();
projectItem._Project = new Project();
projectItem.ID = (int)ToModelValue(reader, "ID");
projectItem._Product.ID = (int)ToModelValue(reader, "ProductID");
projectItem._Project.ID = (int)ToModelValue(reader,"projectID");
projectItem._Product.Manufacturer = ToModelValue(reader, "Manufacturer").ToString();
projectItem._Product.Parameters = ToModelValue(reader, "Parameters").ToString();
projectItem._Product.Price = decimal.Parse( ToModelValue(reader, "Price").ToString());
projectItem._Product.ProductName = ToModelValue(reader, "ProductName").ToString();
projectItem._Product.Specification = ToModelValue(reader, "Specification").ToString();
projectItem._Product.Unit = ToModelValue(reader, "Unit").ToString();
projectItem.Count = (int)ToModelValue(reader, "Count");
projectItem.TotalMoney = (decimal)ToModelValue(reader, "TotalMoney");
projectItem.UnitPrice = (decimal)ToModelValue(reader, "UnitPrice");
return projectItem;
}
protected object ToModelValue(SqlDataReader reader,string columnName)
{
if(reader.IsDBNull(reader.GetOrdinal(columnName)))
{
return null;
}
else
{
return reader[columnName];
}
}
使用GetProjectByCondition方法返回一个封装成对象的 IEnumerable集合
public IEnumerable<Model.Project> GetProjectByCondition(string projectName, string customerName, string contract, string tel)
{
StringBuilder sqlWhere = new StringBuilder("select * from Project where 1=1");
List<SqlParameter> listParameters = new List<SqlParameter>();
if (!string.IsNullOrWhiteSpace(projectName))
{
sqlWhere.AppendLine(" and projectName like @projectName");
listParameters.Add(new SqlParameter("projectName", "%" + projectName + "%"));
}
if (!string.IsNullOrWhiteSpace(contract))
{
sqlWhere.AppendLine(" and Contact like @Contact");
listParameters.Add(new SqlParameter("Contact", "%" + contract + "%"));
}
if (!string.IsNullOrWhiteSpace(customerName))
{
sqlWhere.AppendLine(" and customer like @customer");
listParameters.Add(new SqlParameter("customer", "%" + customerName + "%"));
}
if (!string.IsNullOrWhiteSpace(tel))
{
sqlWhere.AppendLine(" and tel like @tel");
listParameters.Add(new SqlParameter("tel", "%" + tel + "%"));
}
using (SqlDataReader reader = SqlHelper.ExecuteDataReader(sqlWhere.ToString(), listParameters.ToArray()))
{
return ToModels(reader);
}
}
UpdatePassChecked方法接受一个对象,通过对象的属性来获取数据
public int UpdatePassChecked(ProjectItem projectItem)
{
string sql =
"UPDATE Project " +
"SET " +
" ProjectName = @ProjectName"
+ ", Customer = @Customer"
+ ", Contact = @Contact"
+ ", Tel = @Tel"
+ ", DeliveryPlace = @DeliveryPlace"
+ ", DeliveryTime = @DeliveryTime"
+ ", TransportCosts = @TransportCosts"
+ ", PaymentTerm = @PaymentTerm"
+ ", Bak = @Bak"
+ " WHERE ID = @ID";
SqlParameter[] para = new SqlParameter[]
{
new SqlParameter("@ID", projectItem._Project.ID)
,new SqlParameter("@ProjectName", ToDBValue(projectItem._Project.ProjectName))
,new SqlParameter("@Customer", ToDBValue(projectItem._Project.Customer))
,new SqlParameter("@Contact", ToDBValue(projectItem._Project.Contact))
,new SqlParameter("@Tel", ToDBValue(projectItem._Project.Tel))
,new SqlParameter("@DeliveryPlace", ToDBValue(projectItem._Project.DeliveryPlace))
,new SqlParameter("@DeliveryTime", ToDBValue(projectItem._Project.DeliveryTime))
,new SqlParameter("@TransportCosts", ToDBValue(projectItem._Project.TransportCosts))
,new SqlParameter("@PaymentTerm", ToDBValue(projectItem._Project.PaymentTerm))
,new SqlParameter("@Bak", ToDBValue(projectItem._Project.Bak))
};
return SqlHelper.ExecuteNonQuery(sql, para);
}
Add方法传入一个已经把数据封装好的对象,然后返回一个新的对象(包含新创建生成的ID)
public Product Add(Product product)
{
string sql ="INSERT INTO Product (ProductName, Specification, Manufacturer, Parameters, Price, Unit) output inserted.ID VALUES (@ProductName, @Specification, @Manufacturer, @Parameters, @Price, @Unit)";
SqlParameter[] para = new SqlParameter[]
{
new SqlParameter("@ProductName", ToDBValue(product.ProductName)),
new SqlParameter("@Specification", ToDBValue(product.Specification)),
new SqlParameter("@Manufacturer", ToDBValue(product.Manufacturer)),
new SqlParameter("@Parameters", ToDBValue(product.Parameters)),
new SqlParameter("@Price", ToDBValue(product.Price)),
new SqlParameter("@Unit", ToDBValue(product.Unit)),
};
int newId = (int)SqlHelper.ExecuteScalar(sql, para);
return GetByID(newId);
}
3.业务逻辑层:业务逻辑层是项目的核心,业务逻辑上的代码通常在这儿实现(一言难尽)。
简单数据验证:
public bool UpdateThroughChecked(ProjectItem projectItem)
{
if (string.IsNullOrEmpty(projectItem._Project.ProjectName))
{
throw new Exception("项目名称不能为空");
}
//顾客姓名可以为空,但联系人不能为空,所以数据库里就应该设计好
if (string.IsNullOrEmpty(projectItem._Project.Contact))
{
throw new Exception("联系人姓名不能为空");
}
if (string.IsNullOrEmpty(projectItem._Project.Tel))
{
throw new Exception("联系电话不能为空");
}
if (string.IsNullOrEmpty(projectItem._Project.DeliveryPlace))
{
throw new Exception("交货地点不能为空");
}
if (string.IsNullOrEmpty(projectItem._Project.DeliveryTime))
{
throw new Exception("交货时间不能为空");
}
if (projectItem._Project.TransportCosts<00)
{
throw new Exception("运输费用不能为负数");
}
return new DAL.ProjectItemService().UpdatePassChecked(projectItem) > 0;
}
计算总金额:
public decimal GetProductTotalMoney(int projectID)
{
var list = new DAL.ProjectItemService().GetProductTotalMoney(projectID);
decimal totalMoney = 0.00M;
foreach (var model in list)
{
totalMoney+=model.TotalMoney.Value;
}
return totalMoney;
}
根据查询条件获取数据集合:
/// <summary>
/// 根据查询条件获取数据集合
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
public IEnumerable<Model.Product> GetProductsByCondition(string condition)
{
return new DAL.ProductService().GetProductsByCondition(condition);
}
根据ProjectItemID获取一条记录(封装成对象)
public Model.ProjectItem GetOneProjectItemByID(int ProjectItemID)
{
return new DAL.ProjectItemService().GetOneProjectItemByID(ProjectItemID);
}
增加一条记录:
/// <summary>
/// 新增一条记录 根据条件
/// </summary>
/// <param name="modelProject"></param>
/// <returns></returns>
public Model.Project AddPassCheckd(Model.Project modelProject)
{
if (string.IsNullOrEmpty(modelProject.ProjectName))
{
throw new Exception("项目名称不能为空");
}
if (string.IsNullOrEmpty(modelProject.Customer))
{
throw new Exception("顾客名称不能为空");
}
if (string.IsNullOrEmpty(modelProject.Tel))
{
throw new Exception("联系电话不能为空");
}
if (modelProject.TransportCosts <= 0)
{
throw new Exception("运输费用不能为负数:(");
}
return new DAL.ProjectService().Add(modelProject);
}
删除一个项目:
/// <summary>
/// 删除一个项目
/// </summary>
/// <param name="projectID"></param>
/// <returns></returns>
public bool DeleteProject(int projectID)
{
bool isSucceed= new BLL.ProjectItemManager().DeleteProjectItemsByProjectID(projectID);
isSucceed = new DAL.ProjectService().DeleteByID(projectID) > 0 && isSucceed;
return isSucceed;
}
4.表现层(UI)
简单的窗体里嵌套窗体:
public void LoadProjectListForm()
{
projectListForm = null;
if (projectListForm==null)
{
projectListForm = new ProjectListForm();
}
//去掉边框
projectListForm.FormBorderStyle = FormBorderStyle.None;
projectListForm.TopLevel = false;//窗体是否是顶级窗体
projectListForm.Dock = DockStyle.Fill;//填充
projectListForm.Show();//不写看不到
panelContainer.Controls.Clear();//清空
panelContainer.Controls.Add(projectListForm);
}
绑定数据:
我原先思路是直接在DataGridView里绑定对象的属性,和GridView一样使用,但一直无法绑上去,哪儿错了?
所以只能曲线救国了:
void LoadData() { var list = new BLL.ProjectItemManager().GetAllProducts(projectID); //dataGridViewProjectItems.DataSource = list;//当控件被绑定时无法向其添加Row dataGridViewProjectItems.Rows.Clear(); foreach (var model in list) { int i = dataGridViewProjectItems.Rows.Add(); dataGridViewProjectItems.Rows[i].Cells["idColumn"].Value = model.ID; dataGridViewProjectItems.Rows[i].Cells["specificationColumn"].Value = model._Product.Specification; dataGridViewProjectItems.Rows[i].Cells["productNameColumn"].Value = model._Product.ProductName; dataGridViewProjectItems.Rows[i].Cells["manufacturerColumn"].Value = model._Product.Manufacturer; dataGridViewProjectItems.Rows[i].Cells["parametersColumn"].Value = model._Product.Parameters; dataGridViewProjectItems.Rows[i].Cells["productIDColumn"].Value = model._Product.ID; dataGridViewProjectItems.Rows[i].Cells["totalMoneyColumn"].Value = model.TotalMoney; dataGridViewProjectItems.Rows[i].Cells["countColumn"].Value = model.Count; dataGridViewProjectItems.Rows[i].Cells["unitPriceColumn"].Value = model.UnitPrice; dataGridViewProjectItems.Rows[i].Cells["projectIDColumn"].Value = model._Project.ID; }
5.项目总结
花了一个周末修修补补完成了这个工程报价系统,算是复习了一把三层架构。
如果有需要这个项目的朋友可以留下邮箱,我发给你,不算很专业,凑合着参考下~~
没想到这么多朋友要我的源码,我很感动。附件我就不传了,我也怕误导新人。有需要的朋友可以留下邮箱,大家一起探讨,我的代码纯当入门阶段,是我个人对三层架构的理解,不足之处,也请多多包涵,提个意见,希望与君互勉。想要更深刻探索我推介《ASP.NET 设计模式》
源代码下载: