基于PetShop的数据可视化网站(一):基本结构

项目需要制作一个数据可视化网站,本人从零基础开始进行学习以及实现,这篇BLOG对所学的内容进行简单的总结。

具体的项目文件可以参考我的github仓库

简介

PetShop的来源一说是微软为展现.NET对于大型网站开发能力而编写的一个分层购物网站,由于C#这门语言天然的简洁性,因此作为新手入门是一种不错的选择。

其中PetShop具有三个具体层:数据访问层(Data Access Layer),业务逻辑层(Bussiness Logic Layer)以及表现层(Presentation Layer)。具体如下:

基于PetShop的数据可视化网站(一):基本结构_第1张图片

这张图片是使用其他博客现有的图片,来源是这里。

作为一个数据可视化网站,重点只是将数据从数据库中提取出来,因此图片中大部分的功能没什么卵用。

这里我通过自己的项目,简单的介绍下各个层之间的数据传递关系。

数据访问层(DAL)

数据访问层的大体结构如下:

基于PetShop的数据可视化网站(一):基本结构_第2张图片

这张图片也是使用其他博客现有的图片,来源是这里。

现在我们通过数据库中的一个表对上述结构进行讲解,表的结构和数据如下。

结构
基于PetShop的数据可视化网站(一):基本结构_第3张图片

数据
基于PetShop的数据可视化网站(一):基本结构_第4张图片

这个表存储设备信息,包括:设备ID,设备坐标和设备描述。

Model

Model这个类库存储了数据对象,当然不止DAL,其他的层中也会用到Model中的数据对象。如果对C语言有所了解的话,不妨把这个对象理解为一个结构体,这个结构体即是数据库表的结构。每当我们从数据库中提取数据时,我们就可以将数据放入这个结构体中,构成结构体数组。

针对上述设备表,我们在Model类库中构建了DeviceInfo.cs的类,其代码如下:

using System;
using System.Collections.Generic;
using System.Text;

namespace FWSync.Model
{
    [Serializable]
    public class DeviceInfo
    {
        //字段
        private int deviceID;
        private double deviceX;
        private double deviceY;
        private string deviceDesc;
        //构造函数,空的构造函数用于返回一个null值
        public DeviceInfo()
        { }
        public DeviceInfo(int deviceID, double deviceX, double deviceY, string deviceDesc)
        {
            this.deviceID = deviceID;
            this.deviceX = deviceX;
            this.deviceY = deviceY;
            this.deviceDesc = deviceDesc;
        }
        public int DeviceID
        {
            get
            {
                return deviceID;
            }
            set
            {
                deviceID = value;
            }
        }
        public double DeviceX
        {
            get
            {
                return deviceX;
            }
            set
            {
                deviceX = value;
            }
        }
        public double DeviceY
        {
            get
            {
                return deviceY;
            }
            set
            {
                deviceY = value;
            }
        }
        public string DeviceDesc
        {
            get
            {
                return deviceDesc;
            }
            set
            {
                deviceDesc = value;
            }
        }
    }
}

IDAL

IDAL是数据访问层接口,规定了数据访问层需要用到的方法。项目中建立了IDevice.cs。

代码如下:

using System;
using System.Collections.Generic;
using System.Text;
using FWSync.Model;

namespace FWSync.IDAL
{
    /// 
    /// 设备数据访问层接口
    /// 
    public interface IDevice
    {
        /// 
        /// 获取所有设备的方法
        /// 
        /// 返回DeviceInfo类型的列表
        IList GetDevices();

        /// 
        /// 通过deviceid获取设备的方法
        /// 
        /// 
        /// 返回DeviceInfo
        DeviceInfo GetDeviceByID(int deviceid);
    }
}

DBUtility

这个类库是一个工具类,是将一些打开数据库连接,关闭数据库连接的操作提取到一个助手类里,在使用的时候较为方便而已。项目中直接将其导入即可。

PetShop中仅包含针对SQL SERVER和ORACLE两种数据库的助手类,如果使用MySql等数据库,则需要自己写一下助手类,并且引用相应的dll。

SQLServerDal

SQLServerDal是数据访问层,其中每个类均继承了相应的接口,并实现接口中的方法,下面是Device.cs的代码:

using System;
using System.Collections.Generic;
using System.Text;
using FWSync.IDAL;
using FWSync.Model;
using System.Data.SqlClient;
using FWSync.DBUtility;
using System.Data;

namespace FWSync.SQLServerDAL
{
    public class Device : IDevice
    {
        //静态常量
        private const string SQL_SELECT_DEVICES = "SELECT * FROM Device";
        private const string SQL_SELECT_DEVICE_BY_ID = "SELECT * FROM Device where DeviceID = @DeviceID";
        private const string PARAM_DEVICE_ID = "@DeviceID";

        /// 
        /// 获取所有设备信息的方法
        /// 
        /// 
        public IList GetDevices()
        {
            //新建一个以DeviceInfo为结构的列表
            IList devices = new List();

            //通过助手类的方法在数据库中执行相应的SQL语句
            using (SqlDataReader rdr = SqlHelper.ExecuteReader(SqlHelper.ConnectionStringLocalTransaction, System.Data.CommandType.Text, SQL_SELECT_DEVICES, null))
            {
                //这里rdr像是SQL数据库中的游标指针cursor,首次读到数据时,指针指向表头,每执行一次rdr.Read,cursor向下读取一行,直到结束
                while (rdr.Read())
                {
                    DeviceInfo dev = new DeviceInfo(rdr.GetInt32(0), rdr.GetDouble(1), rdr.GetDouble(2), rdr.GetString(3));
                    devices.Add(dev);
                }
            }

            return devices;
        }

        /// 
        /// 通过设备id获取单个设备表的方法
        /// 
        /// 
        /// 
        public DeviceInfo GetDeviceByID(int deviceid)
        {
            //初始化返回变量
            DeviceInfo device = null;
            //新建参数列表
            SqlParameter parm = new SqlParameter(PARAM_DEVICE_ID, System.Data.SqlDbType.Int);
            //绑定参数
            parm.Value = deviceid;
            //执行query
            using (SqlDataReader rdr = SqlHelper.ExecuteReader(SqlHelper.ConnectionStringLocalTransaction, System.Data.CommandType.Text, SQL_SELECT_DEVICE_BY_ID, parm))
            {
                if (rdr.Read())
                {
                    device = new DeviceInfo(rdr.GetInt32(0), rdr.GetDouble(1), rdr.GetDouble(2), rdr.GetString(3));
                }
                else
                {
                    device = new DeviceInfo();
                }
            }
            return device;
        }
    }
}

DALFactory

DALFactory指数据访问工厂,这里用到了工厂模式的概念,通俗上来讲就是数据工厂中直接取出你需要的对象实例。那么你工厂如何知道你需要什么对象实例呢,这就通过config文件设置就好了,下面是DataAssess.cs的全部内容和web.config的部分内容:

DataAssess.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Configuration;

namespace FWSync.DALFactory
{
    public sealed class DataAccess
    {
        private static readonly string path = ConfigurationManager.AppSettings["WebDAL"];

        private DataAccess() { }

        public static FWSync.IDAL.IDevice CreateDevice()
        {
            string className = path + ".Device";
            return (FWSync.IDAL.IDevice)Assembly.Load(path).CreateInstance(className);
        }
    }
}

web.config


<configuration>
    <appSettings>
        
        <add key="WebDAL" value="FWSync.SQLServerDAL"/>
    appSettings>
configuration>

业务逻辑层

在图片上,整个业务逻辑层所占的比例非常小,只有一个BLL的类库和订单处理的类,但是实际上一个网站最为灵活的地方就是业务逻辑层。因为数据访问层的作用就是将一张张的数据库表(包括联表)传递到业务逻辑层,而狭义上展现层的作用就是将处理好的数据和前台的控件进行绑定。因此业务逻辑层的作用可以理解为将表现层得到的请求使用数据访问层的方法进行加工,并给表现层一个反馈。

举个简单的例子:创建用户。表现层获取用户信息,直接调用业务逻辑层的方法AddUser(UserInfo),业务逻辑层则首先需要对用户信息进行第二次筛查(第一次是在浏览器端),接下来验证用户名或邮箱等登录信息是否存在,最后将密码进行MD5加密后调用数据访问层方法插入到数据库中。

以下是BLL中Device.cs的代码:

using System;
using System.Collections.Generic;
using System.Text;
using FWSync.Model;
using FWSync.IDAL;

namespace FWSync.BLL
{
    public class Device
    {
        //从工厂中获得Device数据访问类的实例
        //这里使用static的原因是在初始化后依然能够保存这个对象实例,以方便每个方法中都能够引用
        private static readonly IDevice dal = FWSync.DALFactory.DataAccess.CreateDevice();

        //下述各个方法只是将实例对应的方法返回了,这是因为针对获取设备信息等方法并没有什么太多的业务逻辑
        public IList GetDevices()
        {
            return dal.GetDevices();
        }

        public DeviceInfo GetDeviceByID(int deviceid)
        {
            return dal.GetDeviceByID(deviceid);
        }
    }
}

表现层

在图片上,表现层中不只是我们见到的网页,还包括了成员管理,缓存依赖等等功能,在这里先简单的介绍下aspx页面。

表现层的对于数据可视化,最基本的方法就是在界面上通过表格展示出数据,那么我们可以新建一个aspx页面,在aspx.cs中调用业务逻辑层得到的结果和页面上repeater等asp.net控件进行数据绑定,从而达到展现数据的效果。其中代码如下。

DeviceDemo.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="DeviceDemo.aspx.cs" Inherits="DeviceDemo" %>



<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>title>
head>
<body>
    <form id="form1" runat="server">
    <asp:Repeater ID="rpt" runat="server" >
            <HeaderTemplate>
                <table>
                    <tr>
                        <td>
                            设备X坐标
                        td>
                        <td>
                            设备Y坐标
                        td>
                        <td>
                            设备描述
                        td>
                    tr>
            HeaderTemplate>

            <ItemTemplate>
                <tr>
                    <td >
                        <asp:Label ID = "lbldx" runat ="server" Text='<%# DataBinder.Eval(Container.DataItem, "deviceX") %>'>asp:Label>
                    td>
                    <td >
                        <asp:Label ID = "lbly" runat ="server" Text='<%# DataBinder.Eval(Container.DataItem, "deviceY") %>'>asp:Label>
                    td>
                     <td >
                        <asp:Label ID = "lbldesc" runat ="server" Text='<%# DataBinder.Eval(Container.DataItem, "deviceDesc") %>'>asp:Label>
                    td>
                tr>
            ItemTemplate>

            <FooterTemplate>
                table>
            FooterTemplate>

        asp:Repeater>
    form>
body>
html>

DeviceDemo.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using FWSync.BLL;
using FWSync.Model;
using FWSync.Web;

public partial class DeviceDemo : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        //创建一个DeviceInfo类型的列表
        IList dinfo = new List();
        //创建Device对象
        Device dev = new Device();
        //调用Device对象中的GetDevices()方法,并将返回值放入列表中
        dinfo = dev.GetDevices();

        //设置repeater的数据源为列表
        rpt.DataSource = dinfo;
        //数据绑定
        rpt.DataBind();
    }
}

效果如下所示:

基于PetShop的数据可视化网站(一):基本结构_第5张图片

这里我在啰嗦下repeater控件,这个控件可以看做是一个重复器,中的内容仅重复一次,而中的内容则随着数据内容而变化,<%# DataBinder.Eval(Container.DataItem, “deviceX”) %>则是绑定表达式,可以将绑定的数据源里面包含的内容绑定到相应控件的Text属性中。

你可能感兴趣的:(asp-net)