ບົດຄວາມນີ້ສະແດງໃຫ້ເຫັນເຕັກນິກການຈໍານວນຫນຶ່ງສໍາລັບການນໍາໃຊ້ຫ້ອງ C# ບາງສ່ວນເພື່ອແກ້ໄຂບັນຫາທົ່ວໄປໃນ EF 8/ASP.NET8.
Abstract: ໃນ EF 8 - ຖານຂໍ້ມູນທໍາອິດເຂົ້າຫາຊັ້ນຮຽນ EF ທີ່ສ້າງຂຶ້ນບໍ່ສາມາດຂະຫຍາຍໄດ້ໂດຍກົງກັບຫນ້າທີ່ເພີ່ມເຕີມນັບຕັ້ງແຕ່ພວກມັນຖືກຂຽນທັບເມື່ອຮູບແບບຖືກສ້າງໃຫມ່. ເພື່ອເອົາຊະນະນີ້, ພວກເຮົາສາມາດນໍາໃຊ້ຫ້ອງຮຽນ C # ບາງສ່ວນ. ບົດຄວາມນີ້ນໍາສະເຫນີ tricks ທີ່ເປັນປະໂຫຍດສໍາລັບການຂະຫຍາຍການທໍາງານໃນສະພາບແວດລ້ອມ EF/ASP.NET.
ໃນໂຄງການ C#/ASP.NET 8 MVC ຂອງຂ້ອຍ, ຂ້ອຍກໍາລັງໃຊ້ Entity Framework Core Database ວິທີການທໍາອິດ . ນີ້ແມ່ນສິ່ງຈໍາເປັນເພາະວ່າຄໍາຮ້ອງສະຫມັກຈໍານວນຫນຶ່ງໃນທົ່ວເຕັກໂນໂລຢີທີ່ແຕກຕ່າງກັນແມ່ນອີງໃສ່ຖານຂໍ້ມູນ SQL Server ດຽວກັນ. ເພື່ອສ້າງຕົວແບບຈາກຖານຂໍ້ມູນ, ຂ້ອຍໃຊ້ EFCorePowerTools.
ບັນຫາເກີດຂື້ນໃນເວລາທີ່ຕົວແບບ EF ທີ່ສ້າງຂຶ້ນຕ້ອງໄດ້ຮັບການປັບປຸງເພື່ອສະທ້ອນເຖິງການປ່ຽນແປງຂອງຖານຂໍ້ມູນ. ເນື່ອງຈາກຊັ້ນຮຽນ EF entity ທີ່ຖືກສ້າງຂື້ນຈະຖືກຂຽນທັບ, ການປ່ຽນແປງໃດໆທີ່ເຮັດກັບພວກມັນຈະສູນເສຍໄປ. ນີ້ແມ່ນບັນຫາເນື່ອງຈາກວ່າ, ບາງຄັ້ງ, ພວກເຮົາຈໍາເປັນຕ້ອງໄດ້ຂະຫຍາຍຫນ່ວຍງານ EF ສໍາລັບການນໍາໃຊ້ພາຍໃນຄໍາຮ້ອງສະຫມັກ.
ເພື່ອແກ້ໄຂບັນຫານີ້, ຂ້າພະເຈົ້າອີງໃສ່ tricks ທີ່ກ່ຽວຂ້ອງກັບ ຫ້ອງຮຽນ C# ບາງສ່ວນ , ເຊິ່ງຈະຖືກອະທິບາຍຂ້າງລຸ່ມນີ້.
ໃນຄໍາຮ້ອງສະຫມັກ C#/EF/ASP.NET 8 MVC ປົກກະຕິ, ສະຖານະການອາດຈະຄ້າຍຄືກັບລະຫັດຕໍ່ໄປນີ້:
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical class generated by EF Core Power Tools //in EF-Database-First approach // <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated> [PrimaryKey("Id")] public partial class Customer { //Customer-partial-class-1 [Key] [StringLength(15)] public string? Id { get; set; } [StringLength(15)] public string? NAME { get; set; } public short? Language { get; set; } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical model calls for ASP.NET MVC public class CustomerEdit_ViewModel { //model public string? Id { get; set; } = null; //view model // this is our Customer Entity from EF Core public Customer? Customer1 { get; set; } = null; //this is flag for submit button public bool IsSubmit { get; set; } = false; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical controller for ASP.NET MVC public class CustomersController : Controller { // some Controller code here public async Task<ActionResult> CustomerEdit(CustomerEdit_ViewModel model) { if (model.IsSubmit) { // Model validation is done during model binding // we have to check if model is valid if (ModelState.IsValid) { if (model.Customer1 != null) { //we update existing customer in database //redirect to the list of customers } } else { // we go for presentation of validation errors ModelState.AddModelError("", "PleaseCorrectAllErrors"); } } else { ModelState.Clear(); //go for presentation of original data if (model.Id != null) { //get Customer by Id from database } } return View("CustomerEdit", model); } }
ທີ່ນີ້, Customer-partial-class-1 ແມ່ນຫ້ອງຮຽນທີ່ສ້າງຂຶ້ນໂດຍ EF (ຜ່ານວິສະວະກໍາຍ້ອນກັບຈາກຖານຂໍ້ມູນ). ມັນປະກອບມີຄຸນລັກສະນະການກວດສອບບາງຢ່າງທີ່ສອດຄ່ອງກັບຂໍ້ຈໍາກັດຂອງຖານຂໍ້ມູນ. ຊັ້ນຮຽນນີ້ປົກກະຕິແລ້ວແມ່ນໃຊ້ໃນຫ້ອງຮຽນແບບຈໍາລອງ (ເຊັ່ນ: CustomerEdit_ViewModel ), ບ່ອນທີ່ຄຸນສົມບັດການກວດສອບຖືກປະມວນຜົນໃນລະຫວ່າງການປະຕິບັດ ຫຼືວິທີການ (ເຊັ່ນ: CustomerEdit ).
ຖ້າພວກເຮົາຕ້ອງການເພີ່ມຄຸນສົມບັດໃຫ້ກັບຊັ້ນຮຽນທີ່ສ້າງຂື້ນ, ພວກເຮົາບໍ່ສາມາດດັດແປງ Customer-partial-class-1 ໂດຍກົງໄດ້, ເພາະວ່າການເຮັດດັ່ງນັ້ນຈະນໍາໄປສູ່ການປ່ຽນແປງທີ່ຖືກຂຽນທັບ. ແທນທີ່ຈະ, ພວກເຮົາສາມາດສ້າງ Customer-partial-class-2 ແລະເພີ່ມຄຸນສົມບັດທີ່ກໍາຫນົດເອງຂອງພວກເຮົາຢູ່ທີ່ນັ້ນ. ເພື່ອປ້ອງກັນບໍ່ໃຫ້ EF ຮວມຄຸນສົມບັດເຫຼົ່ານີ້ຢູ່ໃນຕົວແບບ, ພວກເຮົາຕ້ອງໃຊ້ ຄຸນສົມບັດ [NotMapped] .
ນີ້ແມ່ນຕົວຢ່າງ:
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical class generated by EF Core Power Tools //in EF-Database-First approach // <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated> [PrimaryKey("Id")] public partial class Customer { //Customer-partial-class-1 [Key] [StringLength(15)] public string? Id { get; set; } [StringLength(15)] public string? NAME { get; set; } public short? Language { get; set; } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is our partial class that extends our generated class //and is not overwritten by EF Core Power Tools //we use it to add some additional properties public partial class Customer { //Customer-partial-class-2 [NotMapped] public int NumberOfCreditCards { get; set; } = 0; [NotMapped] public string? LanguageString { get { string? result = "Unknown"; if (Language == 1) { result = "English"; } else if (Language == 2) { result = "German"; } return result; } } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical model calls for ASP.NET MVC public class CustomerEdit_ViewModel { //model public string? Id { get; set; } = null; //view model // this is our Customer Entity from EF Core public Customer? Customer1 { get; set; } = null; //this is flag for submit button public bool IsSubmit { get; set; } = false; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical controller for ASP.NET MVC public class CustomersController : Controller { // some Controller code here //............. //this is typical action for editing customer public async Task<ActionResult> CustomerEdit(CustomerEdit_ViewModel model) { if (model.IsSubmit) { // Model validation is done during model binding // we have to check if model is valid if (ModelState.IsValid) { if (model.Customer1 != null) { //we update existing customer in database //redirect to the list of customers } } else { // we go for presentation of validation errors ModelState.AddModelError("", "PleaseCorrectAllErrors"); } } else { ModelState.Clear(); //go for presentation of original data if (model.Id != null) { //get Customer by Id from database } } return View("CustomerEdit", model); } }
ລະຫັດ, ມີຄໍາຄິດເຫັນລາຍລະອຽດ, ຄວນເປັນຄໍາອະທິບາຍດ້ວຍຕົນເອງ.
ບາງຄັ້ງ, ຄຸນສົມບັດການກວດສອບທີ່ສ້າງຂຶ້ນໂດຍອັດຕະໂນມັດໃນ EF ແມ່ນບໍ່ພຽງພໍ, ແລະພວກເຮົາຈໍາເປັນຕ້ອງເພີ່ມກົດລະບຽບການກວດສອບແບບກຳນົດເອງ. ອີກເທື່ອ ໜຶ່ງ, ຫ້ອງຮຽນບາງສ່ວນມາຮອດກູ້ໄພ. ຂ້າງລຸ່ມນີ້ແມ່ນຕົວຢ່າງ:
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical class generated by EF Core Power Tools //in EF-Database-First approach // <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated> [PrimaryKey("Id")] public partial class Customer { //Customer-partial-class-1 [Key] [StringLength(15)] public string? Id { get; set; } [StringLength(15)] public string? NAME { get; set; } public short? Language { get; set; } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is our partial class that extends our generated class //and is not overwritten by EF Core Power Tools //we use it to add some additional properties //and do some additional validation [MetadataType(typeof(Customer_MetaData))] public partial class Customer { //Customer-partial-class-2 public Customer() { TypeDescriptor.AddProviderTransparent( new AssociatedMetadataTypeTypeDescriptionProvider( typeof(Customer), typeof(Customer_MetaData)), typeof(Customer)); } [NotMapped] public int NumberOfCreditCards { get; set; } = 0; [NotMapped] public string? LanguageString { get { string? result = "Unknown"; if (Language == 1) { result = "English"; } else if (Language == 2) { result = "German"; } return result; } } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is our metadata class for our partial class //purpose of this class is to add validation attributes to our partial class //in addition to those that are already in generated class public class Customer_MetaData { //main trick here is that we are adding more validation attributes //in addition to those in generated class [Required] [MinLength(5)] public string? Id { get; set; } = string.Empty; //main trick here is that we are adding more validation attributes //in addition to those in generated class [Required] [MinLength(3)] public string? NAME { get; set; } = string.Empty; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical model calls for ASP.NET MVC public class CustomerEdit_ViewModel { //model public string? Id { get; set; } = null; //view model // this is our Customer Entity from EF Core public Customer? Customer1 { get; set; } = null; //this is flag for submit button public bool IsSubmit { get; set; } = false; } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is utility class for validation public class ValidationUtil { /// <summary> /// Validates the specified model object against custom validation rules. /// </summary> /// <param name="model">The model object to validate.</param> /// <param name="modelState">The ModelStateDictionary to store validation errors.</param> /// <param name="logger">The logger to log errors.</param> /// <param name="prefix">An optional prefix for error keys in the ModelStateDictionary.</param> /// <returns>True if validation is successful; otherwise, false.</returns> /// <exception cref="ArgumentNullException">Thrown when the model is null.</exception> public static bool ValidateModelForCustomRules( object model, ModelStateDictionary modelState, ILogger? logger, string? prefix = null) { bool validationSuccessful = false; try { if (model == null) { throw new ArgumentNullException(nameof(model)); } else { var validationContext = new ValidationContext(model); var validationResults = new List<ValidationResult>(); Validator.TryValidateObject(model, validationContext, validationResults, true); foreach (var result in validationResults) { foreach (var memberName in result.MemberNames) { string key = string.IsNullOrEmpty(prefix) ? memberName : $"{prefix}.{memberName}"; modelState.AddModelError(key, result.ErrorMessage ?? "Error"); } } //Go recursively into depth for all properties of the model object that are objects themselves //we must go manually recursively into depth because API Validator.TryValidateObject does validation //only for class properties on first level foreach (var property in model.GetType().GetProperties()) { if (property.PropertyType.IsClass && property.PropertyType != typeof(string)) { var propertyValue = property.GetValue(model); if (propertyValue != null) { validationSuccessful &= ValidateModelForCustomRules(propertyValue, modelState, logger, property.Name); } } } } } catch (Exception ex) { string methodName = $"Type: {System.Reflection.MethodBase.GetCurrentMethod()?.DeclaringType?.FullName}, " + $"Method: ValidateModel; "; logger?.LogError(ex, methodName); validationSuccessful = false; } return validationSuccessful; } } //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //this is typical controller for ASP.NET MVC public class CustomersController : Controller { // some Controller code here //............. //this is typical action for editing customer public async Task<ActionResult> CustomerEdit(CustomerEdit_ViewModel model) { if (model.IsSubmit) { // Model validation is done during model binding but we need more //validating for custom validation rules ValidationUtil.ValidateModelForCustomRules(model, ModelState, null); // we have to check if model is valid if (ModelState.IsValid) { if (model.Customer1 != null) { //we update existing customer in database //redirect to the list of customers } } else { // we go for presentation of validation errors ModelState.AddModelError("", "PleaseCorrectAllErrors"); } } else { ModelState.Clear(); //go for presentation of original data if (model.Id != null) { //get Customer by Id from database } } return View("CustomerEdit", model); } }
ດັ່ງທີ່ເຈົ້າສາມາດເຫັນໄດ້, ພວກເຮົາໄດ້ດັດແກ້ Customer-partial-class-2 ແລະເພີ່ມຫ້ອງຮຽນ Customer_MetaData . ຈຸດປະສົງໃນທີ່ນີ້ແມ່ນເພື່ອເພີ່ມການກວດສອບເພີ່ມເຕີມສໍາລັບຊັບສິນ NAME, ຮຽກຮ້ອງໃຫ້ມີຢ່າງຫນ້ອຍ 3 ຕົວອັກສອນ.
ໃນ ASP.NET MVC, ພວກເຮົາສາມາດໃຊ້ ModelState ເພື່ອກວດສອບ ແລະດຶງຂໍ້ຄວາມຂໍ້ຜິດພາດ. ໃຫ້ສັງເກດວ່າພວກເຮົາຍັງຕ້ອງໃຊ້ວິທີການ ValidationUtil.ValidateModelForCustomRules ເພື່ອກະຕຸ້ນການກວດສອບແບບກຳນົດເອງດ້ວຍຕົນເອງ.
ລະຫັດແລະຄໍາເຫັນຄວນໃຫ້ລາຍລະອຽດທີ່ຈໍາເປັນທັງຫມົດສໍາລັບການເຂົ້າໃຈເຕັກນິກນີ້.
ໃນຄໍາຮ້ອງສະຫມັກ C# / EF / ASP.NET 8 MVC ຂອງຂ້ອຍ, ຂ້ອຍໃຊ້ເຕັກນິກຂ້າງເທິງກັບຫ້ອງຮຽນ C# ບາງສ່ວນເພື່ອຂະຫຍາຍການເຮັດວຽກຂອງຊັ້ນຮຽນທີ່ຜະລິດ EF. tricks ເຫຼົ່ານີ້ເຮັດໃຫ້ສະຖານະການທົ່ວໄປງ່າຍຂຶ້ນ, ເຮັດໃຫ້ລະຫັດມີຄວາມຊັດເຈນແລະງ່າຍຕໍ່ການຮັກສາ.