服务端返回数据在客户端显示为Null的技术求助
问题描述
我通过SignalR和HttpClient调用API Controller测试,结果一致:服务端调试时可见完整数据,但客户端接收后Offering和Property字段为Null。
已尝试的操作
- 给所有类标记
[Serializable] - 排查循环引用问题
- 在控制器中手动序列化,客户端接收的字符串显示为
[{},{},{},{}]
相关代码
LINQ查询(API Controller方法)
[Authorize] [HttpGet] public List<OfferingWithProperty> Get(string properties = "0", string inventories = "0") { var id = _userManager.GetUserId(User); var context = _dbContext; int[] propertyIds = HttpUtility.UrlDecode(properties).Split(",").ToIntArray(); int[] inventoryIds = HttpUtility.UrlDecode(inventories).Split(",").ToIntArray(); var offerings = (from o in context.Offerings!.Include(o => o.InventoryItem).Include(o => o.InventoryItem.Inventory) join om in context.OfferingManagers! on o.OfferingId equals om.Offering.OfferingId join pom in context.PropertyOfferingMappings! on o.OfferingId equals pom.Offering!.OfferingId join p in context.Properties! on pom.Property!.PropertyId equals p.PropertyId join pm in context.PropertyManagers! on pom.Property!.PropertyId equals pm.Property.PropertyId join i in context.Inventories! on o.InventoryItem.Inventory.InventoryId equals i.InventoryId join ii in context.InventoryItems!.Include("Inventory") on o.InventoryItem.InventoryItemId equals ii.InventoryItemId join im in context.InventoryManagers! on o.InventoryItem.Inventory.InventoryId equals im.Inventory.InventoryId where om.UserId == _userManager.GetUserId(User) && om.Role == OfferingManagerRoles.Administrator && pm.UserId == _userManager.GetUserId(User) && pm.Role == PropertyManagerRoles.Administrator && im.UserId == _userManager.GetUserId(User) && im.Role == InventoryManagerRoles.Administrator && propertyIds.Contains(pom.Property!.PropertyId) && inventoryIds.Contains(o.InventoryItem.Inventory.InventoryId) select new OfferingWithProperty() { Offering = o, Property = p }).ToList(); return offerings; }
实体类代码
public class Property { public Property() { Name = ""; Slug = ""; } public int PropertyId { get; set; } public string Name { get; set; } public string Slug { get; set; } } [Serializable] public class Offering { public Offering() { Name = ""; Slug = ""; Description = ""; InventoryItem = new(); } public int OfferingId { get; set; } public InventoryItem InventoryItem { get; set; } public string Slug { get; set; } public string Name { get; set; } public string Description { get; set; } [Column(TypeName="decimal(18,2)")] public decimal Price { get; set; } }
客户端调用代码
SignalR调用
OfferingsWithProperties = await hubConnection.InvokeAsync<List<OfferingWithProperty>>("GetOfferings", propertyIds.ToIntArray(), inventoryIds.ToIntArray());
HttpClient调用
string propertyIdString = propertyIds.Count() > 0 ? HttpUtility.UrlEncode(String.Join(',', propertyIds)) : "0"; string inventoryIdString = inventoryIds.Count() > 0 ? HttpUtility.UrlEncode(String.Join(',', inventoryIds)) : "0"; var response = await httpClient.GetStringAsync($"api/Offering?properties={propertyIdString}&inventories={inventoryIdString}");
解决思路
检查
OfferingWithProperty类定义
你未提供这个类的代码,这是最可能的问题根源。要确保:- 属性是公共的,且带有get/set访问器
- 属性名称与服务端返回的序列化字段完全匹配(注意大小写,比如服务端是PascalCase,客户端也要对应,或者配置序列化忽略大小写)
- 类有无参公共构造函数
避免直接返回EF实体对象
EF实体通常包含跟踪代理、导航属性,容易引发序列化问题。建议创建专用DTO类,手动投影需要的字段:// 示例DTO定义 public class OfferingWithPropertyDto { public OfferingDto Offering { get; set; } public PropertyDto Property { get; set; } } public class OfferingDto { public int OfferingId { get; set; } public string Name { get; set; } public string Slug { get; set; } public decimal Price { get; set; } // 按需添加其他字段,避免不必要的导航属性 } public class PropertyDto { public int PropertyId { get; set; } public string Name { get; set; } public string Slug { get; set; } } // 查询中投影到DTO select new OfferingWithPropertyDto() { Offering = new OfferingDto { OfferingId = o.OfferingId, Name = o.Name, Slug = o.Slug, Price = o.Price }, Property = new PropertyDto { PropertyId = p.PropertyId, Name = p.Name, Slug = p.Slug } }).ToList();调整EF查询跟踪模式
尝试在查询中添加.AsNoTracking(),避免EF代理对象干扰序列化:var offerings = (from o in context.Offerings!.AsNoTracking() // 其余查询逻辑不变 ).ToList();配置序列化规则
如果使用System.Text.Json,在服务端配置忽略循环引用、支持大小写不敏感匹配:// Program.cs/Startup.cs builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; }); // SignalR也需要同步配置 builder.Services.AddSignalR() .AddJsonProtocol(options => { options.PayloadSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; });验证客户端类结构
确保客户端的OfferingWithProperty、Offering、Property类结构与服务端完全一致,包括属性名称、类型,且都有公共get/set和无参构造函数。
内容的提问来源于stack exchange,提问作者Anthony Brian Mallgren
相关产品推荐
相关产品推荐

