Magicodes.IE之導入學生數據教程

  • 2019 年 11 月 30 日
  • 筆記

基礎教程之導入學生數據

說明

本教程主要說明如果使用Magicodes.IE.Excel完成學生數據的Excel導入。

要點

  • 本教程使用Magicodes.IE.Excel來完成Excel數據導入
  • 需要通過創建Dto來完成導入
  • Magicodes.IE.Excel可以根據Dto以及特性設置來自動生成導入的Excel模板,數據驗證(包括重複驗證),模板驗證,讀取設置,值約束和映射,輸出Excel驗證標註

主要步驟

1.安裝包Magicodes.IE.Excel

在本篇教程中,我們僅演示使用Excel來完成學生數據的導入。我們需要在已準備好的工程中安裝以下包,參考命令如下所示:

Install-Package Magicodes.IE.Excel

2.創建導入Dto

主要程式碼如下所示:

  • 學生數據Dto 如上述程式碼所示,我們定義了以上學生數據Dto,主要注意事項如下:
    1. ExcelImporter特性可以設置一些導入的全局設置,比如是否標註錯誤、導入Sheet名稱(如不設置則自動獲取第一個)、截止讀取的列數、表頭位置。
    2. 支援常用的數據驗證設置,比如必填、最大長度。
    3. 支援數據重複校驗,比如身份證號碼。見ImporterHeader特性的IsAllowRepeat設置。
    4. 支援列頭設置,將ImporterHeader的Name屬性。除此之外,ImporterHeader還支援自動過濾空格(默認啟用)、處理掉所有的空格、列索引等。
    5. 對數據列啟用了忽略設置,見SchoolId的"[ImporterHeader(IsIgnore = true)]"。
    6. 使用了值映射,見「Gender」屬性。啟用值映射之後,將不會從枚舉定義中獲取值映射。
    7. 支援枚舉,支援從枚舉的Display、Description特性中獲取值映射。枚舉定義見下文。
/// <summary>      /// 導入學生數據Dto      /// IsLabelingError:是否標註數據錯誤      /// </summary>      [ExcelImporter(IsLabelingError = true)]      public class ImportStudentDto      {          /// <summary>          ///     序號          /// </summary>          [ImporterHeader(Name = "序號")]          public long SerialNumber { get; set; }            /// <summary>          ///     學籍號          /// </summary>          [ImporterHeader(Name = "學籍號")]          [MaxLength(30, ErrorMessage = "學籍號字數超出最大限制,請修改!")]          public string StudentCode { get; set; }            /// <summary>          ///     姓名          /// </summary>          [ImporterHeader(Name = "姓名")]          [Required(ErrorMessage = "學生姓名不能為空")]          [MaxLength(50, ErrorMessage = "名稱字數超出最大限制,請修改!")]          public string Name { get; set; }            /// <summary>          ///     身份證號碼          /// </summary>          [ImporterHeader(Name = "身份證號", IsAllowRepeat = false)]          [Required(ErrorMessage = "身份證號不能為空")]          [MaxLength(18, ErrorMessage = "身份證字數超出最大限制,請修改!")]          public string IdCard { get; set; }            /// <summary>          ///     性別          /// </summary>          [ImporterHeader(Name = "性別")]          [Required(ErrorMessage = "性別不能為空")]          [ValueMapping("男", 0)]          [ValueMapping("女", 1)]          public Genders Gender { get; set; }            /// <summary>          ///     家庭地址          /// </summary>          [ImporterHeader(Name = "家庭住址")]          [Required(ErrorMessage = "家庭地址不能為空")]          [MaxLength(200, ErrorMessage = "家庭地址字數超出最大限制,請修改!")]          public string Address { get; set; }            /// <summary>          ///     家長姓名          /// </summary>          [ImporterHeader(Name = "家長姓名")]          [Required(ErrorMessage = "家長姓名不能為空")]          [MaxLength(50, ErrorMessage = "家長姓名數超出最大限制,請修改!")]          public string Guardian { get; set; }            /// <summary>          ///     家長聯繫電話          /// </summary>          [ImporterHeader(Name = "家長聯繫電話")]          [MaxLength(20, ErrorMessage = "家長聯繫電話字數超出最大限制,請修改!")]          public string GuardianPhone { get; set; }            /// <summary>          ///     學號          /// </summary>          [ImporterHeader(Name = "學號")]          [MaxLength(30, ErrorMessage = "學號字數超出最大限制,請修改!")]          public string StudentNub { get; set; }            /// <summary>          ///     宿舍號          /// </summary>          [ImporterHeader(Name = "宿舍號")]          [MaxLength(20, ErrorMessage = "宿舍號字數超出最大限制,請修改!")]          public string DormitoryNo { get; set; }            /// <summary>          ///     QQ          /// </summary>          [ImporterHeader(Name = "QQ號")]          [MaxLength(30, ErrorMessage = "QQ號字數超出最大限制,請修改!")]          public string QQ { get; set; }            /// <summary>          ///     民族          /// </summary>          [ImporterHeader(Name = "民族")]          [MaxLength(2, ErrorMessage = "民族字數超出最大限制,請修改!")]          public string Nation { get; set; }            /// <summary>          ///     戶口性質          /// </summary>          [ImporterHeader(Name = "戶口性質")]          [MaxLength(10, ErrorMessage = "戶口性質字數超出最大限制,請修改!")]          public string HouseholdType { get; set; }            /// <summary>          ///     聯繫電話          /// </summary>          [ImporterHeader(Name = "學生聯繫電話")]          [MaxLength(20, ErrorMessage = "手機號碼字數超出最大限制,請修改!")]          public string Phone { get; set; }            /// <summary>          ///     狀態          ///     測試可為空的枚舉類型          /// </summary>          [ImporterHeader(Name = "狀態")]          public StudentStatus? Status { get; set; }            /// <summary>          ///     備註          /// </summary>          [ImporterHeader(Name = "備註")]          [MaxLength(200, ErrorMessage = "備註字數超出最大限制,請修改!")]          public string Remark { get; set; }            /// <summary>          ///     是否住校(宿舍)          /// </summary>          [ImporterHeader(IsIgnore = true)]          public bool? IsBoarding { get; set; }            /// <summary>          ///     所屬班級id          /// </summary>          [ImporterHeader(IsIgnore = true)]          public Guid ClassId { get; set; }            /// <summary>          ///     學校Id          /// </summary>          [ImporterHeader(IsIgnore = true)]          public Guid? SchoolId { get; set; }            /// <summary>          ///     校區Id          /// </summary>          [ImporterHeader(IsIgnore = true)]          public Guid? CampusId { get; set; }            /// <summary>          ///     專業Id          /// </summary>          [ImporterHeader(IsIgnore = true)]          public Guid? MajorsId { get; set; }            /// <summary>          ///     年級Id          /// </summary>          [ImporterHeader(IsIgnore = true)]          public Guid? GradeId { get; set; }      }
  • 性別枚舉 定義如下所示: 注意上文中的第7點。
/// <summary>  ///     性別  /// </summary>  public enum Genders  {      /// <summary>      ///     男      /// </summary>      Man = 0,        /// <summary>      ///     女      /// </summary>      Female = 1  }
  • 學生狀態枚舉
/// <summary>  ///     學生狀態 正常、流失、休學、勤工儉學、頂崗實習、畢業、參軍  /// </summary>  public enum StudentStatus  {      /// <summary>      ///     正常      /// </summary>      [Display(Name = "正常")] Normal = 0,        /// <summary>      ///     流失      /// </summary>      [Description("流水")] PupilsAway = 1,        /// <summary>      ///     休學      /// </summary>      [Display(Name = "休學")] Suspension = 2,        /// <summary>      ///     勤工儉學      /// </summary>      [Display(Name = "勤工儉學")] WorkStudy = 3,        /// <summary>      ///     頂崗實習      /// </summary>      [Display(Name = "頂崗實習")] PostPractice = 4,        /// <summary>      ///     畢業      /// </summary>      [Display(Name = "畢業")] Graduation = 5,        /// <summary>      ///     參軍      /// </summary>      [Display(Name = "參軍")] JoinTheArmy = 6  }

注意上文中的第7點。

3.生成導入模板並填充數據

導入之前是不是得準備一份模板?對於我們,手寫模板?這是不存在的。Magicodes.IE.Excel封裝了根據DTO自動生成Excel導入模板的方法,我們可以直接調用。這裡我們來看下導入的相關方法:

/// <summary>  ///     導入  /// </summary>  public interface IImporter  {      /// <summary>      ///     生成Excel導入模板      /// </summary>      /// <typeparam name="T"></typeparam>      /// <returns></returns>      Task<TemplateFileInfo> GenerateTemplate<T>(string fileName) where T : class, new();        /// <summary>      ///     生成Excel導入模板      /// </summary>      /// <typeparam name="T"></typeparam>      /// <returns>二進位位元組</returns>      Task<byte[]> GenerateTemplateBytes<T>() where T : class, new();        /// <summary>      ///     導入模型驗證數據      /// </summary>      /// <typeparam name="T"></typeparam>      /// <param name="filePath"></param>      /// <returns></returns>      Task<ImportResult<T>> Import<T>(string filePath) where T : class, new();  }

通過以上方法中的GenerateTemplate,我們可以得到需要的導入模板。具體使用可以參考以下單元測試:

public IImporter Importer = new ExcelImporter();        [Fact(DisplayName = "生成學生數據導入模板(測試枚舉生成模板)")]      public async Task GenerateStudentImportTemplate_Test()      {          var filePath = Path.Combine(Directory.GetCurrentDirectory(),              nameof(GenerateStudentImportTemplate_Test) + ".xlsx");          if (File.Exists(filePath)) File.Delete(filePath);            var result = await Importer.GenerateTemplate<ImportStudentDto>(filePath);          result.ShouldNotBeNull();          File.Exists(filePath).ShouldBeTrue();            //TODO:讀取Excel檢查表頭和格式      }

以上DTO獲取模板並填充數據後如下圖所示:

注意:枚舉會自動生成下拉選擇,必填項列頭會標紅。

4.獲取學生導入驗證錯誤和數據

根據模板填充數據後,我們就可以進行數據導入了。通常情況下,我們有以下步驟:

  • 驗證導入數據 通過Magicodes.IE.Excel導入數據會自動進行驗證,並且輸出驗證結果,以便於前台顯示。具體我們可以通過其導入的結果類來了解: 其中,
    • Data為數據結果
    • RowErrors為驗證錯誤,比如必填、重複驗證、文本長度等等。會給出行號、欄位以及欄位錯誤集合
    • TemplateErrors為模板錯誤,比如必填列缺失等錯誤資訊。支援錯誤等級(警告、錯誤)
    • Exception為導入異常資訊
    • HasError為是否存在錯誤(不包含警告)

    通過ImportResult,我們就可以很方便的拿到導入驗證錯誤而無須額外編寫程式碼。通常在導入時我們需要判斷HasError屬性並給前台返回具體的錯誤結果。 數據導入參考程式碼如下所示: [Fact(DisplayName = "學生基礎數據導入")] public async Task StudentInfoImporter_Test() { var filePath = Path.Combine(Directory.GetCurrentDirectory(), "TestFiles", "Import", "學生基礎數據導入.xlsx"); var import = await Importer.Import<ImportStudentDto>(filePath); import.ShouldNotBeNull(); if (import.Exception != null) _testOutputHelper.WriteLine(import.Exception.ToString()); if (import.RowErrors.Count > 0) _testOutputHelper.WriteLine(JsonConvert.SerializeObject(import.RowErrors)); import.HasError.ShouldBeFalse(); import.Data.ShouldNotBeNull(); import.Data.Count.ShouldBe(16); }

/// <summary>  ///     導入結果  /// </summary>  public class ImportResult<T> where T : class  {      /// <summary>      /// </summary>      public ImportResult()      {          RowErrors = new List<DataRowErrorInfo>();      }        /// <summary>      ///     導入數據      /// </summary>      public virtual ICollection<T> Data { get; set; }        /// <summary>      ///     驗證錯誤      /// </summary>      public virtual IList<DataRowErrorInfo> RowErrors { get; set; }        /// <summary>      ///     模板錯誤      /// </summary>      public virtual IList<TemplateErrorInfo> TemplateErrors { get; set; }        /// <summary>      ///     導入異常資訊      /// </summary>      public virtual Exception Exception { get; set; }        /// <summary>      ///     是否存在導入錯誤      /// </summary>      public virtual bool HasError => Exception != null ||                                      (TemplateErrors?.Count(p => p.ErrorLevel == ErrorLevels.Error) ?? 0) > 0 ||                                      (RowErrors?.Count ?? 0) > 0;  }
  • 獲取驗證標註

客戶說雖然你提示了,但是我還是不知道哪裡錯了!!怎麼辦?!!

我們貼心的為你準備了導入數據的Excel文件的標註:

如何開啟這個【史詩劇情】呢?僅需:

[ExcelImporter(IsLabelingError = true)]

開啟後,我們將自動保存「{目標文件名稱}_.xlsx」的標註文件到目標位置。

  • 獲取導入數據

沒有錯誤了?也就是HasError為false,那麼我們就可以直接拿到Data為所欲為了!

最後

整個學生數據的導入教程就此結束了。相關庫會一直更新,在功能體驗上有可能會和本文教程有細微的出入,請以相關具體程式碼、版本日誌、單元測試示例為準。