模型原型:服务器的配置和运行状态信息。
设计要求:Json格式数据解析后,判断配置信息是否是新数据或者是否更新。如是新数据,则直接添加到数据库;若是数据更新,则更新数据库配置信息并更新运行状态信息;都不是则仅将运行状态添加到数据库。最后从数据库取数据并展示。
模型难点:每个服务器会搭载多个网卡和最多44个硬盘。
(1)View层如何同时展示所有硬盘和其他设备属性的信息。
(2)单服务器配多网卡多硬盘必定会设计多个表(即服务器配置表、运行状态表、网卡配置表、硬盘配置表),MVC框架下如何同时将其Model传到View层。
(3)本文程序使用PageList进行分页,通常为了程序运行速度,会在查询时进行分页,及var servers = db.Servers.OrderByDescending(e => e.ID).ToPagedList(pageNumber, 7),但本设计考虑到多Model传到View层,此方法不适用。如何使程序适应上万条数据的数据库。
Model层:
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.ComponentModel.DataAnnotations;
5 using System.Linq;
6 using System.Web;
7
8 namespace monitoring.Models
9 {
10 public class Servers
11 {
12 public int ID { get; set; }
13 [Required]
14 [DisplayName("主板序列号")]
15 public string AMDCDkey { get; set; }
16 [Required]
17 [DisplayName("服务器名")]
18 public string HostName { get; set; }
19 [Required]
20 [DisplayName("CPU型号")]
21 public string CPUType { get; set; }
22 [Required]
23 [DisplayName("CPU数量")]
24 public int CPUNum { get; set; }
25 [Required]
26 [DisplayName("内存大小")]
27 public string RAMSize { get; set; }
28 [DisplayName("Raid卡型号")]
29 public string RaidType { get; set; }
30 [Required]
31 [DisplayName("硬盘个数")]
32 public int HDDNum { get; set; }
33 [Required]
34 [DisplayName("机箱型号")]
35 public string CaseType { get; set; }
36 }
37 }
Servers
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.ComponentModel.DataAnnotations;
5 using System.Linq;
6 using System.Web;
7
8 namespace monitoring.Models
9 {
10 public class Servers
11 {
12 public int ID { get; set; }
13 [Required]
14 [DisplayName("主板序列号")]
15 public string AMDCDkey { get; set; }
16 [Required]
17 [DisplayName("服务器名")]
18 public string HostName { get; set; }
19 [Required]
20 [DisplayName("CPU型号")]
21 public string CPUType { get; set; }
22 [Required]
23 [DisplayName("CPU数量")]
24 public int CPUNum { get; set; }
25 [Required]
26 [DisplayName("内存大小")]
27 public string RAMSize { get; set; }
28 [DisplayName("Raid卡型号")]
29 public string RaidType { get; set; }
30 [Required]
31 [DisplayName("硬盘个数")]
32 public int HDDNum { get; set; }
33 [Required]
34 [DisplayName("机箱型号")]
35 public string CaseType { get; set; }
36 }
37 }
ServersUsing
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.ComponentModel.DataAnnotations;
5 using System.Linq;
6 using System.Web;
7
8 namespace monitoring.Models
9 {
10 public class NIC
11 {
12 public int ID { get; set; }
13 public string AMDCDkey { get; set; }
14 [Required]
15 [DisplayName("网卡型号")]
16 public string NICType { get; set; }
17 [Required]
18 [DisplayName("网卡数量")]
19 public int NICNum { get; set; }
20 }
21 }
NIC
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.ComponentModel.DataAnnotations;
5 using System.Linq;
6 using System.Web;
7
8 namespace monitoring.Models
9 {
10 public class HDD
11 {
12 public int ID { get; set; }
13 public string AMDCDkey { get; set; }
14 public int RootHDD { get; set; }
15 [Required]
16 [DisplayName("硬盘标识")]
17 public string HDDID { get; set; }
18 [Required]
19 [DisplayName("硬盘型号")]
20 public string HDDType { get; set; }
21 [Required]
22 [DisplayName("硬盘容量")]
23 public string HDDCap { get; set; }
24 }
25 }
HDD
为了将多Model传到View层,单独建立一个List,将多个model的信息添加到List中。
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Web;
5
6 namespace monitoring.Models
7 {
8 public class ViewModel
9 {
10 public int ID { get; set; }
11 public Servers SView { get; set; }
12 public ServersUsing SUView { get; set; }
13 public List<NIC> NICView { get; set; }
14 public List<HDD> HDDView { get; set; }
15 }
16 }
ViewModel
如需实现,还要有上下文:
1 using System.Data.Entity;
2
3 namespace monitoring.Models
4 {
5 public class ServersContext : DbContext
6 {
7 // 您可以向此文件中添加自定义代码。更改不会被覆盖。
8 //
9 // 如果您希望只要更改模型架构,Entity Framework
10 // 就会自动删除并重新生成数据库,则将以下
11 // 代码添加到 Global.asax 文件中的 Application_Start 方法。
12 // 注意: 这将在每次更改模型时销毁并重新创建数据库。
13 //
14 // System.Data.Entity.Database.SetInitializer(new System.Data.Entity.DropCreateDatabaseIfModelChanges<monitoring.Models.ServersContext>());
15
16 public ServersContext() : base("name=ServersContext")
17 {
18
19 }
20
21 public DbSet<Servers> Servers { get; set; }
22 public DbSet<ServersUsing> ServersUsings { get; set; }
23 public DbSet<NIC> NICs { get; set; }
24 public DbSet<HDD> HDDs { get; set; }
25 }
26 }
ServerContext
Controller层:
实现了解析Json并添加数据库的方法,实现了查询和分页功能,完成了将多Model传到View层的方法。
1 using System;
2 using System.Collections.Generic;
3 using System.Data;
4 using System.Data.Entity;
5 using System.Linq;
6 using System.Web;
7 using System.Web.Mvc;
8 using monitoring.Models;
9 using Newtonsoft.Json.Linq;
10 using Newtonsoft.Json;
11 using System.IO;
12 using monitoring.Controllers;
13 using System.Web.Script.Serialization;
14 using PagedList;
15
16 namespace monitoring.Controllers
17 {
18 public class ServersController : Controller
19 {
20 private ServersContext db = new ServersContext();
21 public void ParseJson(StreamReader reader)
22 {
23 // 配置信息
24 JToken jobject = JToken.ReadFrom(new JsonTextReader(reader));
25 string jsonText = jobject.ToString();
26 JObject roots = (JObject)JsonConvert.DeserializeObject(jsonText);
27 JArray servers = (JArray)roots["Servers"];
28 for (int i = 0; i < servers.Count; i++)
29 {
30 JObject root = (JObject)servers[i];
31 Servers newdate = new Servers();
32 newdate.AMDCDkey = (string)root["AMDCDkey"];
33 newdate.HostName = (string)root["HostName"];
34 newdate.CPUType = (string)root["CPUType"];
35 newdate.CPUNum = (int)root["CPUNum"];
36 newdate.RAMSize = (string)root["RAMSize"];
37 newdate.RaidType = (string)root["RaidType"];
38 newdate.HDDNum = (int)root["HDDNum"];
39 newdate.CaseType = (string)root["CaseType"];
40 bool NewDate = false;
41 try
42 {
43 var server = db.Servers.Single(x => x.AMDCDkey == newdate.AMDCDkey);
44 if (server != newdate)
45 {
46 server = newdate;
47 UpdateModel<Servers>(newdate);
48 db.SaveChanges();
49 }
50 }
51 catch
52 {
53 NewDate = true;
54 db.Servers.Add(newdate);
55 db.SaveChanges();
56 }
57
58 // 运行状态信息
59 ServersUsing newdateusi = new ServersUsing();
60 newdateusi.AMDCDkey = (string)root["AMDCDkey"];
61 newdateusi.CPUUsRate = (string)root["CPUUsRate"];
62 newdateusi.RAMUsRate = (string)root["RAMUsRate"];
63 newdateusi.HDDUsRate = (string)root["HDDUsRate"];
64 newdateusi.HDDIO = (string)root["HDDIO"];
65 newdateusi.HostcomputerLoad = (string)root["HostcomputerLoad"];
66 newdateusi.TheRootPartitionUsageRate = (string)root["TheRootPartitionUsageRate"];
67 newdateusi.Time = DateTime.Now;
68 db.ServersUsings.Add(newdateusi);
69 db.SaveChanges();
70
71 // 网卡信息
72 JArray NICs = (JArray)root["NIC"];
73 if (NewDate == true)
74 {
75 for (int b = 0; b < NICs.Count; b++)
76 {
77 JObject nics = (JObject)NICs[b];
78 NIC newdatenic = new NIC();
79 newdatenic.AMDCDkey = (string)root["AMDCDkey"];
80 newdatenic.NICType = (string)nics["NICType"];
81 newdatenic.NICNum = (int)nics["NICNum"];
82 db.NICs.Add(newdatenic);
83 db.SaveChanges();
84 }
85 }
86 else
87 {
88 JavaScriptSerializer Serializer = new JavaScriptSerializer();
89 List<NIC> objs = Serializer.Deserialize<List<NIC>>(NICs.ToString());
90 List<NIC> nic = db.NICs.Where(x => x.AMDCDkey == newdate.AMDCDkey).ToList();
91 bool b = false;
92 foreach (var a in nic) { if (!objs.Contains(a)) b = true; }
93 if (b && nic.Count != objs.Count)
94 {
95 var remove = db.NICs.Where(x => x.AMDCDkey == newdate.AMDCDkey);
96 foreach (var a in remove)
97 {
98 db.NICs.Remove(a);
99 }
100 for (int d = 0; d < NICs.Count; d++)
101 {
102 JObject nics = (JObject)NICs[d];
103 NIC newdatenic = new NIC();
104 newdatenic.AMDCDkey = (string)root["AMDCDkey"];
105 newdatenic.NICType = (string)nics["NICType"];
106 newdatenic.NICNum = (int)nics["NICNum"];
107 db.NICs.Add(newdatenic);
108 }
109 db.SaveChanges();
110 }
111 }
112
113 // 硬盘信息
114 JArray HDDs = (JArray)root["HDD"];
115
116 if (NewDate == true)
117 {
118 for (int f = 0; f < HDDs.Count; f++)
119 {
120 JObject hdds = (JObject)HDDs[f];
121 HDD newdatehdd = new HDD();
122 newdatehdd.HDDID = (string)hdds["HDDID"];
123 newdatehdd.RootHDD = (int)hdds["RootHDD"];
124 newdatehdd.AMDCDkey = (string)root["AMDCDkey"];
125 newdatehdd.HDDType = (string)hdds["HDDType"];
126 newdatehdd.HDDCap = (string)hdds["HDDCap"];
127 db.HDDs.Add(newdatehdd);
128 db.SaveChanges();
129 }
130 }
131 else
132 {
133 JavaScriptSerializer Serializer = new JavaScriptSerializer();
134 List<HDD> objs = Serializer.Deserialize<List<HDD>>(HDDs.ToString());
135 List<HDD> hdd = db.HDDs.Where(x => x.AMDCDkey == newdate.AMDCDkey).ToList();
136 bool b = false;
137 foreach (var a in hdd) { if (!objs.Contains(a)) b = true; }
138 if (b && hdd.Count != objs.Count)
139 {
140 var remove = db.HDDs.Where(x => x.AMDCDkey == newdate.AMDCDkey);
141 foreach (var a in remove)
142 {
143 db.HDDs.Remove(a);
144 }
145 for (int f = 0; f < HDDs.Count; f++)
146 {
147 JObject hdds = (JObject)HDDs[f];
148 HDD newdatehdd = new HDD();
149 newdatehdd.HDDID = (string)hdds["HDDID"];
150 newdatehdd.RootHDD = (int)hdds["RootHDD"];
151 newdatehdd.AMDCDkey = (string)root["AMDCDkey"];
152 newdatehdd.HDDType = (string)hdds["HDDType"];
153 newdatehdd.HDDCap = (string)hdds["HDDCap"];
154 db.HDDs.Add(newdatehdd);
155 }
156 db.SaveChanges();
157 }
158 }
159 }
160 }
161 public ActionResult Index(string searchString, int? page)
162 {
163 // 遍历文件夹中的文件
164 //string path = "C:\\Users\\edong\\Desktop\\json";
165 //DirectoryInfo dir = new DirectoryInfo(path);
166 //FileInfo[] fil = dir.GetFiles();
167 //foreach (FileInfo f in fil)
168 //{
169 // using (StreamReader reader = System.IO.File.OpenText("" + f.FullName + ""))
170 // {
171 // ParseJson(reader);
172 // }
173 // // 存入数据库后删除json文件
174 // // System.IO.File.Delete(@""+ f.FullName +"");
175 //}
176 int pageNumber = page ?? 1;
177
178 var servers = db.Servers.OrderByDescending(e => e.ID);
179
180 var listallview = new List<ViewModel>();
181 foreach (var s in servers)
182 {
183 listallview.Add(new ViewModel()
184 {
185 SView = s,
186 SUView = db.ServersUsings.Where(o => o.AMDCDkey == s.AMDCDkey).First(),
187 NICView = db.NICs.Where(o => o.AMDCDkey == s.AMDCDkey).ToList(),
188 HDDView = db.HDDs.Where(o => o.AMDCDkey == s.AMDCDkey).ToList()
189 });
190 }
191 var pagelist = listallview.OrderByDescending(e => e.ID).ToPagedList(pageNumber, 7);
192 // 查询
193 if (!string.IsNullOrEmpty(searchString))
194 {
195 pagelist = listallview.Where(s => s.SView.HostName.Contains(searchString)).ToPagedList(pageNumber, 7);
196 }
197 // 服务器网卡/硬盘数量最大值
198 int NICNum = 0;
199 int HDDNum = 0;
200 foreach (var item in pagelist)
201 {
202 if (NICNum < item.NICView.Count)
203 NICNum = item.NICView.Count;
204 if (HDDNum < item.HDDView.Count)
205 HDDNum = item.HDDView.Count;
206 }
207 ViewBag.NICNum = NICNum;
208 ViewBag.HDDNum = HDDNum;
209
210 return View(pagelist);
211 }
212
213 protected override void Dispose(bool disposing)
214 {
215 db.Dispose();
216 base.Dispose(disposing);
217 }
218 }
219 }
ServersController
View层:
为了方便查看View和Controller中的传值等问题,先贴代码再贴图。(View中引用了Bootstrap,UI同事教了我一点,仅作排版使用。)
1 @using monitoring.Models;
2 @using PagedList.Mvc;
3
4 @model PagedList.PagedList<ViewModel>
5
6 @{
7 ViewBag.Title = "Index";
8 }
9 <!DOCTYPE html>
10 <head>
11 <title>服务器设备配置信息</title>
12 @*自动刷新,间隔10s*@
13 @*<meta http-equiv="REFRESH" content="10">*@
14 <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
15 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
16
17 <!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
18 <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
19 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
20
21 <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
22 <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
23 </head>
24 <style type="text/css">
25 .Menu {
26 float: left;
27 background: #ffffff;
28 }
29
30 .Menu li {
31 margin: 30px 40px 30px 0px;
32 list-style: none;
33 text-align: center;
34 }
35
36 .Table {
37 background: #4ba54b;
38 color: #ffffff;
39 border-radius: 50px;
40 }
41
42 .Date {
43 background: #ffffff;
44 width: 1150px;
45 float: left;
46 margin: 0px 0px 10px 20px;
47 }
48
49 .Date ul {
50 width: 160px;
51 float: left;
52 }
53
54 .Date ul li {
55 margin: 30px 0px 30px 0px;
56 text-align: center;
57 list-style: none;
58 }
59
60 .Text {
61 width: 378px;
62 height: 40px;
63 float: left;
64 margin: 12px 0px 0px 5px;
65 background: #efefef;
66 padding-left: 15px;
67 border-radius: 42px;
68 border: 2px solid #efefef;
69 outline: none;
70 position: absolute;
71 }
72
73 .search {
74 position: absolute;
75 top: 0;
76 right: 0;
77 width: 42px;
78 height: 42px;
79 background: none;
80 border: none;
81 right: 0;
82 margin-top: 12px;
83 }
84 </style>
85 <body style="background:#efefef">
86 <div style="width:1500px">
87 <div style="margin: 30px 0px 0px 60px; box-shadow: #ff0000 0px 0px 10px; ">
88 <ul class="Menu">
89 <li class="Table">配置</li>
90 <li>主机名</li>
91 <li>CPU型号</li>
92 <li>CPU数量</li>
93 <li>内存大小</li>
94 <li>Raid卡型号</li>
95 <li>硬盘个数</li>
96 <li>机箱型号</li>
97 <li class="Table">运行状态</li>
98 <li>CPU使用率</li>
99 <li>内存使用率</li>
100 <li>硬盘使用率</li>
101 <li>硬盘I/O</li>
102 <li>主机负载</li>
103 <li>根分区使用率</li>
104 <li>时间</li>
105 <li class="Table">网卡信息</li>
106 @for (int i = 0; i < ViewBag.NICNum; i++)
107 {
108 <li>网卡型号</li>
109 <li>网卡数量</li>
110 }
111 <li class="Table">硬盘信息</li>
112 <li>根硬盘型号</li>
113 <li>根硬盘容量</li>
114 @for (int i = 1; i < ViewBag.HDDNum; i++)
115 {
116 <li>硬盘型号</li>
117 <li>硬盘容量</li>
118 }
119 </ul>
120 </div>
121 <div class="Date clearfix" style="height:65px">
122 @using (Html.BeginForm("Index", "Servers", FormMethod.Get))
123 {
124 <div class="row">
125 <div class="col-sm-4" style="float:left">
126 <form class="form">
127 <div class="form-group">
128 @Html.TextBox("SearchString", null, new { @class = "form-control Text", placeholder = "主机名" })
129 <button type="submit" class="search"><span class="glyphicon glyphicon-search"></span></button>
130 </div>
131 </form>
132 </div>
133 <div class="col-sm-8" style="float:right">@Html.PagedListPager(Model, page => Url.Action("Index", new { page }))</div>
134 </div>
135 }
136 </div>
137 <div class="Date">
138 @foreach (var item in Model)
139 {
140 <ul>
141 <li style="margin: 5px 0px 30px 0px">@item.SView.HostName</li>
142 <li>@item.SView.CPUType</li>
143 <li>@item.SView.CPUNum</li>
144 <li>@item.SView.RAMSize</li>
145 <li>@item.SView.RaidType</li>
146 <li>@item.SView.HDDNum</li>
147 <li style="margin: 30px 0px 5px 0px">@item.SView.CaseType</li>
148 </ul>
149 }
150 </div>
151 <div class="Date" style="margin:30px 0px 10px 20px;">
152 @foreach (var item in Model)
153 {
154 <ul>
155 <li style="margin: 25px 0px 30px 0px">@item.SUView.CPUUsRate</li>
156 <li>@item.SUView.RAMUsRate</li>
157 <li>@item.SUView.HDDUsRate</li>
158 <li>@item.SUView.HDDIO</li>
159 <li>@item.SUView.HostcomputerLoad</li>
160 <li>@item.SUView.TheRootPartitionUsageRate</li>
161 <li style="margin: 30px 0px 5px 0px">@item.SUView.Time</li>
162 </ul>
163 }
164 </div>
165 <div class="Date" style="margin: 25px 0px 10px 20px;">
166 @foreach (var item in Model)
167 {
168 <ul>
169 @foreach (var item1 in @item.NICView)
170 {
171 <li>@item1.NICType</li>
172 <li style="margin: 30px 0px 10px 0px">@item1.NICNum</li>
173 }
174 </ul>
175 }
176 </div>
177 <div class="Date" style="margin:15px 0px 10px 20px;">
178 @foreach (var item in Model)
179 {
180 <ul>
181 @foreach (var item2 in @item.HDDView)
182 {
183 if (item2.RootHDD == 1)
184 {
185 <li>@item2.HDDType</li>
186 <li>@item2.HDDCap</li>
187 }
188 }
189 @foreach (var item2 in @item.HDDView)
190 {
191 if (item2.RootHDD != 1)
192 {
193 <li>@item2.HDDType</li>
194 <li>@item2.HDDCap</li>
195 }
196 }
197 </ul>
198 }
199 </div>
200 </div>
201 </body>
View
再将设计老王给的图贴一下。
评价:竖版排版很好的解决了服务器属性过多,需要显示多条数据的问题,也解决了每个服务器需要展示多个网卡数据和硬盘数据的问题。但是并没有解决根本的问题,硬盘上限44,某个服务器可能搭载了44个硬盘,如果全部展示出来则页面太长。
View层完成图:
经理看后提了好多改进的方式,包括Model的命名不规范,数据库中使用无意义的主键的原则,Model中主键外键的设置。Controller中Json数据可以直接转成model中的类型。以及View界面过长改进思路的问题。
此为毕业后的第一个程序,把第一版的代码保留下来,接下来进行代码的重构。