最近通過WPF開發項目,為了對WPF知識點進行總結,所以利用業餘時間,開發一個學生信息管理系統【Student Information Management System】。前四篇文章進行了框架搭建和模塊劃分,後臺WebApi介面編寫,以及課程管理模塊,班級管理模塊,學生管理模塊的開發,本文在前四篇... ...
最近通過WPF開發項目,為了對WPF知識點進行總結,所以利用業餘時間,開發一個學生信息管理系統【Student Information Management System】。前四篇文章進行了框架搭建和模塊劃分,後臺WebApi介面編寫,以及課程管理模塊,班級管理模塊,學生管理模塊的開發,本文在前四篇基礎之上,繼續深入開發學生信息管理系統的成績管理和系統管理模塊,通過本篇文章,將繼續鞏固之前的知識點,本文僅供學習分享使用,如有不足之處,還請指正。
涉及知識點
學生信息管理系統SIMS屬於一個小型的完整系統開發,涉及的知識點比較,具體如下所示:
- WPF開發中TextBlock,TextBox,DataGrid,Combox,TabControl等控制項的基礎使用以及數據綁定等操作。
- MAH樣式的使用,在本示例中MAH主要用於統一頁面風格,提高用戶體驗。
- HttpClient使用,主要用於訪問服務端提供的介面。
業務邏輯
前面幾篇文章,由淺入深,逐步介紹了課程管理模塊,班級管理模塊,學生管理模塊,今天繼續介紹成績管理模塊,業務邏輯關係如下:
- 學生屬於某一班級之學生,所以學生中包含班級信息。
- 班級中存在班長,同時班長又屬於學生的一個實體。
- 成績是某一學生的成績,且一名學生有各門課程的成績。所以成績和學生有關,且和課程有關。
實體E-R圖
學生表,成績表,班級表,課程表,各個數據表之間的E-R圖,如下所示:
由此可見,成績表與課程和學生表,都有關聯,所以放在最後。
成績管理
成績管理主要用於錄入各個學生各個課程的成績,包含成績表的增刪改查功能。
1. 成績管理後臺服務Service
IScoreAppService介面是對成績管理的抽象,如下所示:
1 namespace SIMS.WebApi.Services.Score 2 { 3 public interface IScoreAppService 4 { 5 public PagedRequest<ScoreEntity> GetScores(string studentName,string courseName,int pageNum,int pageSize); 6 7 /// <summary> 8 /// 通過id查詢成績信息 9 /// </summary> 10 /// <param name="id"></param> 11 /// <returns></returns> 12 public ScoreEntity GetScore(int id); 13 14 /// <summary> 15 /// 新增成績 16 /// </summary> 17 /// <param name="score"></param> 18 /// <returns></returns> 19 public int AddScore(ScoreEntity score); 20 21 /// <summary> 22 /// 修改成績 23 /// </summary> 24 /// <param name="score"></param> 25 /// <returns></returns> 26 public int UpdateScore(ScoreEntity score); 27 28 /// <summary> 29 /// 刪除成績 30 /// </summary> 31 /// <param name="id"></param> 32 public int DeleteScore(int id); 33 } 34 }
服務實現類ScoreAppService,是對介面的實現,具體如下所示:
1 namespace SIMS.WebApi.Services.Score 2 { 3 public class ScoreAppService : IScoreAppService 4 { 5 private DataContext dataContext; 6 7 public ScoreAppService(DataContext dataContext) 8 { 9 this.dataContext = dataContext; 10 } 11 12 public int AddScore(ScoreEntity score) 13 { 14 var entity = this.dataContext.Scores.Add(score); 15 this.dataContext.SaveChanges(); 16 return 0; 17 } 18 19 public int DeleteScore(int id) 20 { 21 var entity = dataContext.Scores.FirstOrDefault(x => x.Id == id); 22 if (entity != null) 23 { 24 dataContext.Scores.Remove(entity); 25 dataContext.SaveChanges(); 26 } 27 return 0; 28 } 29 30 public ScoreEntity GetScore(int id) 31 { 32 var entity = dataContext.Scores.FirstOrDefault(r => r.Id == id); 33 return entity; 34 } 35 36 /// <summary> 37 /// 按條件查詢成績列表 38 /// </summary> 39 /// <param name="studentName"></param> 40 /// <param name="courseName"></param> 41 /// <param name="pageNum"></param> 42 /// <param name="pageSize"></param> 43 /// <returns></returns> 44 public PagedRequest<ScoreEntity> GetScores(string studentName, string courseName, int pageNum, int pageSize) 45 { 46 IQueryable<ScoreEntity> scores = null; 47 if (!string.IsNullOrEmpty(studentName) && !string.IsNullOrEmpty(courseName)) 48 { 49 var students = this.dataContext.Students.Where(r => r.Name.Contains(studentName)); 50 var courses = this.dataContext.Courses.Where(r => r.Name.Contains(courseName)); 51 scores = this.dataContext.Scores.Where(r => students.Select(t => t.Id).Contains(r.StudentId)).Where(r => courses.Select(t => t.Id).Contains(r.CourseId)); 52 } 53 else if (!string.IsNullOrEmpty(studentName)) 54 { 55 var students = this.dataContext.Students.Where(r => r.Name.Contains(studentName)); 56 scores = this.dataContext.Scores.Where(r => students.Select(t => t.Id).Contains(r.StudentId)); 57 } 58 else if (!string.IsNullOrEmpty(courseName)) 59 { 60 var courses = this.dataContext.Courses.Where(r => r.Name.Contains(courseName)); 61 scores = this.dataContext.Scores.Where(r => courses.Select(t => t.Id).Contains(r.CourseId)); 62 } 63 else { 64 scores = dataContext.Scores.Where(r => true).OrderBy(r => r.Id); 65 } 66 int count = scores.Count(); 67 List<ScoreEntity> items; 68 if (pageSize > 0) 69 { 70 items = scores.Skip((pageNum - 1) * pageSize).Take(pageSize).ToList(); 71 } 72 else 73 { 74 items = scores.ToList(); 75 } 76 return new PagedRequest<ScoreEntity>() 77 { 78 count = count, 79 items = items 80 }; 81 } 82 83 public int UpdateScore(ScoreEntity score) 84 { 85 dataContext.Scores.Update(score); 86 dataContext.SaveChanges(); 87 return 0; 88 } 89 } 90 }
2. 成績管理WebApi介面控制器
控制器是對數據服務的公開,每一個控制器的方法表示一個Action,即表示一個客戶端可以訪問的入口。具體如下所示:
1 namespace SIMS.WebApi.Controllers 2 { 3 /// <summary> 4 /// 成績控制器 5 /// </summary> 6 [Route("api/[controller]/[action]")] 7 [ApiController] 8 public class ScoreController : ControllerBase 9 { 10 private readonly ILogger<ScoreController> logger; 11 12 private readonly IScoreAppService scoreAppService; 13 14 public ScoreController(ILogger<ScoreController> logger, IScoreAppService scoreAppService) 15 { 16 this.logger = logger; 17 this.scoreAppService = scoreAppService; 18 } 19 20 /// <summary> 21 /// 獲取成績信息 22 /// </summary> 23 /// <param name="id"></param> 24 /// <returns></returns> 25 [HttpGet] 26 public PagedRequest<ScoreEntity> GetScores(string? studentName, string? courseName, int pageNum, int pageSize) 27 { 28 return scoreAppService.GetScores(studentName, courseName, pageNum, pageSize); 29 } 30 31 /// <summary> 32 /// 獲取成績信息 33 /// </summary> 34 /// <param name="id"></param> 35 /// <returns></returns> 36 [HttpGet] 37 public ScoreEntity GetScore(int id) 38 { 39 return scoreAppService.GetScore(id); 40 } 41 42 /// <summary> 43 /// 新增成績 44 /// </summary> 45 /// <param name="score"></param> 46 /// <returns></returns> 47 [HttpPost] 48 public int AddScore(ScoreEntity score) 49 { 50 return scoreAppService.AddScore(score); 51 } 52 53 /// <summary> 54 /// 修改成績 55 /// </summary> 56 /// <param name="score"></param> 57 /// <returns></returns> 58 [HttpPut] 59 public int UpdateScore(ScoreEntity score) 60 { 61 return scoreAppService.UpdateScore(score); 62 } 63 64 /// <summary> 65 /// 刪除成績 66 /// </summary> 67 /// <param name="id"></param> 68 [HttpDelete] 69 public int DeleteScore(int id) 70 { 71 return scoreAppService.DeleteScore(id); 72 } 73 } 74 }
當服務運行起來後,Swagger還每一個控制器都進行歸類,可以清晰的看到每一個介面對應的網址,成績管理模塊對應的介面如下所示:
3. 成績管理客戶端介面訪問類HttpUtil
在學生信息系統開發的過程中,發現所有的介面訪問都是通用的,所以對介面訪問功能提取成一個HttpUtil基類【包括GET,POST,PUT,DELETE等功能】,其他具體業務再繼承基類,並細化具體業務即可。ScoreHttpUtil代碼如下所示:
1 namespace SIMS.Utils.Http 2 { 3 public class ScoreHttpUtil:HttpUtil 4 { 5 /// <summary> 6 /// 通過id查詢成績信息 7 /// </summary> 8 /// <param name="id"></param> 9 /// <returns></returns> 10 public static ScoreEntity GetScore(int id) 11 { 12 Dictionary<string, object> data = new Dictionary<string, object>(); 13 data["id"] = id; 14 var str = Get(UrlConfig.SCORE_GETSCORE, data); 15 var socre = StrToObject<ScoreEntity>(str); 16 return socre; 17 } 18 19 /// <summary> 20 /// 21 /// </summary> 22 /// <param name="studentName"></param> 23 /// <param name="courseName"></param> 24 /// <param name="pageNum"></param> 25 /// <param name="pageSize"></param> 26 /// <returns></returns> 27 public static PagedRequest<ScoreEntity> GetScores(string? studentName, string? courseName, int pageNum, int pageSize) 28 { 29 Dictionary<string, object> data = new Dictionary<string, object>(); 30 data["courseName"] = courseName; 31 data["studentName"] = studentName; 32 data["pageNum"] = pageNum; 33 data["pageSize"] = pageSize; 34 var str = Get(UrlConfig.SCORE_GETSCORES, data); 35 var socres = StrToObject<PagedRequest<ScoreEntity>>(str); 36 return socres; 37 } 38 39 public static bool AddScore(ScoreEntity socre) 40 { 41 var ret = Post<ScoreEntity>(UrlConfig.SCORE_ADDSCORE, socre); 42 return int.Parse(ret) == 0; 43 } 44 45 public static bool UpdateScore(ScoreEntity socre) 46 { 47 var ret = Put<ScoreEntity>(UrlConfig.SCORE_UPDATESCORE, socre); 48 return int.Parse(ret) == 0; 49 } 50 51 public static bool DeleteScore(int Id) 52 { 53 Dictionary<string, string> data = new Dictionary<string, string>(); 54 data["Id"] = Id.ToString(); 55 var ret = Delete(UrlConfig.SCORE_DELETESCORE, data); 56 return int.Parse(ret) == 0; 57 } 58 } 59 }
4. 成績管理客戶端操作
經過前面四個部分的開發,客戶端就可以與數據介面進行交互,展示數據到客戶端。客戶端所有的開發,均採用MVVM模式進行。
在成績管理模塊中,根據功能區分,主要包含兩個View視圖及對應的ViewModel。如下所示:
- Score視圖,主要用於成績的查詢,以及新增,修改,刪除的鏈接入口。
- AddEditScore視圖,主要用於成績信息的新增和修改,共用一個視圖頁面。
- 成績課程不需要頁面,所以沒有對應視圖。
4.1. Score視圖
Score視圖,主要是成績的查詢和新增,修改,刪除的鏈接入口。涉及知識點如下:
- Score視圖頁面佈局採用Grid方式和StackPanel混合佈局,即整體佈局採用Grid,細微佈局採用StackPanel。
- 成績採用分頁列表的方式展示,需要用到DataGrid,及分頁控制項【WPF預設不提供分頁控制項,可自行編寫分頁控制項】。
- 查詢條件採用按鈕Button和文本框TextBox等組成,關於基礎控制項的使用,不再詳細論述,可參考其他文章。
- 在本系統的所有WPF視圖中,均需要引入Prism和 MAH組件。
- Score視圖中,所有的數據均採用Binding的方式與ViewModel進行交互。
Score視圖具體代碼,如下所示:
1 <UserControl x:Class="SIMS.ScoreModule.Views.Score" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:i="http://schemas.microsoft.com/xaml/behaviors" 7 xmlns:prism="http://prismlibrary.com/" 8 xmlns:local="clr-namespace:SIMS.ScoreModule.Views" 9 mc:Ignorable="d" 10 xmlns:mahApps="http://metro.mahapps.com/winfx/xaml/controls" 11 xmlns:ctrls ="clr-namespace:SIMS.Utils.Controls;assembly=SIMS.Utils" 12 prism:ViewModelLocator.AutoWireViewModel="True" 13 d:DesignHeight="450" d:DesignWidth="800"> 14 15 <UserControl.Resources> 16 <ResourceDictionary> 17 <ResourceDictionary.MergedDictionaries> 18 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" /> 19 <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" /> 20 <ResourceDictionary> 21 <Style x:Key="LinkButton" TargetType="Button"> 22 <Setter Property="Background" Value="White"></Setter> 23 <Setter Property="Cursor" Value="Hand"></Setter> 24 <Setter Property="Margin" Value="3"></Setter> 25 <Setter Property="MinWidth" Value="80"></Setter> 26 <Setter Property="MinHeight" Value="25"></Setter> 27 <Setter Property="BorderThickness" Value="0 0 0 0"></Setter> 28 </Style> 29 </ResourceDictionary> 30 </ResourceDictionary.MergedDictionaries> 31 </ResourceDictionary> 32 </UserControl.Resources> 33 <i:Interaction.Triggers> 34 <i:EventTrigger EventName="Loaded"> 35 <i:InvokeCommandAction Command="{Binding LoadedCommand}"></i:InvokeCommandAction> 36 </i:EventTrigger> 37 </i:Interaction.Triggers> 38 <Grid> 39 <Grid.RowDefinitions> 40 <RowDefinition Height="Auto"></RowDefinition> 41 <RowDefinition Height="Auto"></RowDefinition> 42 <RowDefinition Height="*"></RowDefinition> 43 <RowDefinition Height="Auto"></RowDefinition> 44 </Grid.RowDefinitions> 45 <TextBlock Text="成績信息" FontSize="20" Background="AliceBlue" Margin="2"></TextBlock> 46 <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center">