ABP Zero框架中应用Many-To-Many关系
ABP Zero最新版本的数据处理层已经升级为EF Core,因为EF 5.0已经支持Many-to-Many关系的定义。因此可以通过对实体类的定义添加“Many-To-Many”关系。
- 定义实体类Service。
public class Service : Entity<Guid>, IMustHaveTenant { public int TenantId { get; set; } [Required] [StringLength(ServiceConsts.MaxServiceNameLength, MinimumLength = ServiceConsts.MinServiceNameLength)] public virtual string ServiceName { get; set; } [Required] [StringLength(ServiceConsts.MaxDescriptionLength, MinimumLength = ServiceConsts.MinDescriptionLength)] public virtual string Description { get; set; } public virtual DateTime EffectiveDate { get; set; } public virtual DateTime EndDate { get; set; } public virtual decimal BaseAmount { get; set; } public virtual bool BaseOnPerUnits { get; set; } public virtual bool CombineBaseRate { get; set; } public virtual bool LadderType { get; set; } [Required] public virtual string CalculationMethod { get; set; } public virtual ICollection<Tax> Taxes { get; set; } public Service() { this.Taxes = new HashSet<Tax>(); } }
- 定义实体类Tax。
public class Tax : FullAuditedEntity<Guid>, IMustHaveTenant { public int TenantId { get; set; } [Required] [StringLength(TaxConsts.MaxCodeLength, MinimumLength = TaxConsts.MinCodeLength)] public virtual string Code { get; set; } [Required] public virtual Guid RevenueCodeId { get; set; } [ForeignKey("RevenueCodeId")] public RevenueCode RevenueCodeFk { get; set; } [Required] [StringLength(TaxConsts.MaxDescriptionLength, MinimumLength = TaxConsts.MinDescriptionLength)] public virtual string Description { get; set; } public virtual decimal Percent { get; set; } public virtual decimal FlatAmount { get; set; } public virtual decimal? LowerLimit { get; set; } public virtual decimal? UpperLimit { get; set; } [Required] [StringLength(TaxConsts.MaxCalcMethodLength, MinimumLength = TaxConsts.MinCalcMethodLength)] public virtual string CalcMethod { get; set; } public virtual bool ApplytoAllCharges { get; set; } public virtual ICollection<Service> Services { get; set; } protected Tax() { this.Services = new HashSet<Service>(); } }
Many-To-Many关系在EF 5.0中实体类定义就是在实体类中定义与其有关系的类集合,即可。
- 修改CreateOrEditServiceDto对象,这个对象的作用主要是在业务层与领域层之间传递实体数据。
public class CreateOrEditServiceDto : EntityDto<Guid?> { [Required] [StringLength(ServiceConsts.MaxServiceNameLength, MinimumLength = ServiceConsts.MinServiceNameLength)] public string ServiceName { get; set; } [Required] [StringLength(ServiceConsts.MaxDescriptionLength, MinimumLength = ServiceConsts.MinDescriptionLength)] public string Description { get; set; } public DateTime EffectiveDate { get; set; } public DateTime EndDate { get; set; } public decimal BaseAmount { get; set; } public bool BaseOnPerUnits { get; set; } public bool CombineBaseRate { get; set; } public bool LadderType { get; set; } [Required] public string CalculationMethod { get; set; } public List<NameValueDto<string>> TaxItems { get; set; } }
增加了用于显示自身对应的Many-To-Many关系实体对象信息集合。在此处只给Service编辑Dto对象的代码,实际上在CreateOrEditTaxDto对象中也需要定义“public List<NameValueDto<string>> ServiceItems { get; set; }”属性。用于传递之前的关系。
- 修改ServicesAppService.cs文件中的数据保存方法,增加对Tax对象关系的保存。
protected virtual async Task Create(CreateOrEditServiceDto input) { var service = ObjectMapper.Map<Service>(input); if (AbpSession.TenantId != null) { service.TenantId = (int)AbpSession.TenantId; } foreach (var item in input.TaxItems) { var tax = await _taxesRepository.FirstOrDefaultAsync(Guid.Parse(item.Value)); if (tax != null) { if (!service.Taxes.Contains(tax)) service.Taxes.Add(tax); } } await _serviceRepository.InsertAsync(service); } protected virtual async Task Update(CreateOrEditServiceDto input) { var service = await _serviceRepository.GetAllIncluding(t => t.Taxes).FirstOrDefaultAsync(s => s.Id == input.Id); ObjectMapper.Map(input, service); service.Taxes.Clear(); foreach (var item in input.TaxItems) { var tax = await _taxesRepository.FirstOrDefaultAsync(Guid.Parse(item.Value)); if (tax != null) { if (!service.Taxes.Contains(tax)) service.Taxes.Add(tax); } } }
Create与Update方法的区别是,Create方法需要主动调用Repository对象的Insert方法,而Update就去并不需要主动调用Repository对象的Update方法。
-
修改GetServiceForEditOutput类,这个类的作用主要是在业务层与表示层之彰传递实体数据。
public class GetServiceForEditOutput { public CreateOrEditServiceDto Service { get; set; } public List<ComboboxItemDto> TaxeItems { get; set; } public GetServiceForEditOutput() { TaxeItems = new List<ComboboxItemDto>(); } }
- 修改CreateOrEditServiceModalViewModel类,增加UI显示需要的实体数据形式。
public class CreateOrEditServiceModalViewModel { public CreateOrEditServiceDto Service { get; set; } public bool IsEditMode => Service.Id.HasValue; public List<ComboboxItemDto> TaxItems { get; set; } }
- 修改ServiceController,增加UI层数据模型数据加载。
public async Task<PartialViewResult> CreateOrEditModal(Guid? id) { GetServiceForEditOutput getServiceForEditOutput; if (id.HasValue) { getServiceForEditOutput = await _servicesAppService.GetServiceForEdit(new EntityDto<Guid> { Id = (Guid)id }); } else { getServiceForEditOutput = new GetServiceForEditOutput { Service = new CreateOrEditServiceDto(), TaxeItems = await _servicesAppService.GetAllTaxes() }; getServiceForEditOutput.Service.EffectiveDate = DateTime.Now; getServiceForEditOutput.Service.EndDate = DateTime.Now; } var viewModel = new CreateOrEditServiceModalViewModel() { Service = getServiceForEditOutput.Service, TaxItems = getServiceForEditOutput.TaxeItems }; return PartialView("_CreateOrEditModal", viewModel); }
- 修改_CreateOrEditModal.cshtml文件增加Taxes数据控件。
<div class="form-group"> <label for="Service_Taxes">@L("Taxes")</label> <select id="Service_Taxes" class="form-control select2" style="width:100%;" multiple> @foreach (var item in Model.TaxItems) { <option value="@item.Value" selected="@item.IsSelected">@item.DisplayText</option> } </select> </div>
- 修改_CreateOrEditModal.js文件,增加“select2”组件的初始中化。
modal.find("#Service_Taxes") .select2({ placeholder: "Select a tax", tags: true });
增加获取选择数据结果的代码。
this.save = function() { if (!_$serviceInformationForm.valid()) { return; } // taxes select - post var $selectElement = $('#Service_Taxes'); var inputTaxes = []; $.each($selectElement.select2('data'), function (index, item) { inputTaxes.push({ name: item.text, value: item.id }); }); var service = _$serviceInformationForm.serializeFormToObject(); service.taxItems = inputTaxes; _modalManager.setBusy(true); _servicesService.createOrEdit( service ).done(function() { abp.notify.info(app.localize('SavedSuccessfully')); _modalManager.close(); abp.event.trigger('app.createOrEditServiceModalSaved'); }).always(function() { _modalManager.setBusy(false); }); };
到这里ABP Zero框架中添加“Many-To-Many”关系的实体关系的方法已经全部分修改完毕。