Blazor WASM中SyncFusion DataManager无法识别实体实例变更
问题描述
我开发了一个包含Invoice实体的应用,该实体包含InvoiceLine实体的集合导航属性。使用SyncFusion的DataGrid结合自定义DataManager执行CRUD操作时,点击DataGrid中的发票会显示详情组件,修改发票行后UI和本地Invoice对象的集合都能体现变更,但点击保存时,变更无法同步到服务器端的控制器。
相关代码
发票实体类(导航属性部分)
// 导航属性 public List<InvoiceLine>? InvoiceLines { get => GetPropertyValue<List<InvoiceLine>?>(); set => SetPropertyValue(value); }
自定义DataManager类
namespace AccountsReceivable.Client.DataAdaptor { public class InvoicesDataManager : SfDataManager { private IServerApiService _serverApiService = default!; [Inject] private IServerApiService ServerApiService { get => _serverApiService; set { if (_serverApiService != value) { _serverApiService = value; OnServerApiServiceChanged(); } } } public InvoicesDataManager() { Adaptor = Syncfusion.Blazor.Adaptors.ODataV4Adaptor; this.EnableCaching = true; } // 设置数据管理器URL值 private void OnServerApiServiceChanged() { // 主URL const string apiUrl = "api/Invoices"; // 实例化HTTP对象 HttpClientInstance = ServerApiService.HttpClientInstance; // 设置基础URL Url = ServerApiService.GetApiUrl(apiUrl); // 设置保存URL UpdateUrl = ServerApiService.GetApiUrl($"{apiUrl}/save"); // 设置删除URL RemoveUrl = ServerApiService.GetApiUrl($"{apiUrl}/delete"); } protected override void OnInitialized() { OnServerApiServiceChanged(); base.OnInitialized(); } } }
服务器控制器代码
// PATCH: api/invoices/save [HttpPatch("save({invoiceId})")] [AuthorizeScope(AuthorizationScope.UpdateInvoice)] public async Task<ActionResult> Patch([FromRoute] int invoiceId, [FromBody] IDictionary<string, JsonDocument>? values, CancellationToken ct) { try { Invoice? invoice = await _invoiceDataProvider.GetItemAsync(invoiceId); if (invoice != null) { values?.Patch(invoice); await _invoiceDataProvider.SaveAsync(invoice); } return Ok(ApiResponse.OkResponse(invoice)); } catch (Exception ex) { _logger.LogError(ex, "Invoice Error"); return BadRequest(new ApiResponse(ApiStatusCodes.UnhandledError, ex.Message)); } }
问题原因
- ODataV4Adaptor默认不会自动检测集合类型导航属性的变更(比如
InvoiceLines的增删改),OData的PATCH协议对复杂集合的变更处理需要明确的序列化规则,默认Adaptor未覆盖此场景。 - 当前使用
List<InvoiceLine>作为集合类型,无法触发集合内部项变更的属性通知,导致DataManager无法感知到集合内容的变化。 - 控制器中使用
IDictionary<string, JsonDocument>接收数据时,默认的Patch方法无法正确解析并应用集合类型的变更。
解决方案
1. 替换集合类型为ObservableCollection
将List<InvoiceLine>改为ObservableCollection<InvoiceLine>,确保集合内部项的增删改能触发属性变更通知,让DataManager感知到变更:
// 修改导航属性类型 public ObservableCollection<InvoiceLine>? InvoiceLines { get => GetPropertyValue<ObservableCollection<InvoiceLine>?>(); set => SetPropertyValue(value); }
2. 自定义Adaptor处理集合序列化
继承ODataV4Adaptor并重写UpdateAsync方法,确保集合变更被正确序列化发送到服务器:
public class CustomInvoiceAdaptor : ODataV4Adaptor { public override async Task<object> UpdateAsync(DataManagerRequest dataManagerRequest, string key, object data, string? updateUrl = null, CancellationToken cancellationToken = default) { var invoice = data as Invoice; if (invoice != null && invoice.InvoiceLines != null) { // 构造包含集合变更的更新数据 var updateData = new Dictionary<string, object> { { "InvoiceLines", invoice.InvoiceLines } // 按需添加其他需要更新的属性 }; return await base.UpdateAsync(dataManagerRequest, key, updateData, updateUrl, cancellationToken); } return await base.UpdateAsync(dataManagerRequest, key, data, updateUrl, cancellationToken); } }
然后在自定义DataManager中替换Adaptor:
public InvoicesDataManager() { Adaptor = new CustomInvoiceAdaptor(); // 使用自定义Adaptor this.EnableCaching = true; }
3. 调整控制器的变更应用逻辑
修改控制器方法,手动解析并应用集合变更:
[HttpPatch("save({invoiceId})")] [AuthorizeScope(AuthorizationScope.UpdateInvoice)] public async Task<ActionResult> Patch([FromRoute] int invoiceId, [FromBody] IDictionary<string, JsonDocument>? values, CancellationToken ct) { try { var invoice = await _invoiceDataProvider.GetItemAsync(invoiceId); if (invoice != null) { // 先应用基础属性变更 values?.Patch(invoice); // 手动处理InvoiceLines的变更 if (values?.ContainsKey("InvoiceLines") == true) { var updatedLines = values["InvoiceLines"].Deserialize<List<InvoiceLine>>(); if (updatedLines != null) { invoice.InvoiceLines?.Clear(); invoice.InvoiceLines?.AddRange(updatedLines); } } await _invoiceDataProvider.SaveAsync(invoice); } return Ok(ApiResponse.OkResponse(invoice)); } catch (Exception ex) { _logger.LogError(ex, "Invoice Error"); return BadRequest(new ApiResponse(ApiStatusCodes.UnhandledError, ex.Message)); } }
4. 临时禁用缓存排查问题
若上述方案无效,可临时禁用DataManager缓存,确认是否是缓存导致变更未发送:
public InvoicesDataManager() { Adaptor = new CustomInvoiceAdaptor(); this.EnableCaching = false; // 临时禁用缓存 }
内容的提问来源于stack exchange,提问作者Clay Hess
相关产品推荐
相关产品推荐

