You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

服务端返回数据在客户端显示为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}");

解决思路

  1. 检查OfferingWithProperty类定义
    你未提供这个类的代码,这是最可能的问题根源。要确保:

    • 属性是公共的,且带有get/set访问器
    • 属性名称与服务端返回的序列化字段完全匹配(注意大小写,比如服务端是PascalCase,客户端也要对应,或者配置序列化忽略大小写)
    • 类有无参公共构造函数
  2. 避免直接返回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();
    
  3. 调整EF查询跟踪模式
    尝试在查询中添加.AsNoTracking(),避免EF代理对象干扰序列化:

    var offerings = (from o in context.Offerings!.AsNoTracking()
                    // 其余查询逻辑不变
                    ).ToList();
    
  4. 配置序列化规则
    如果使用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;
        });
    
  5. 验证客户端类结构
    确保客户端的OfferingWithProperty、Offering、Property类结构与服务端完全一致,包括属性名称、类型,且都有公共get/set和无参构造函数。


内容的提问来源于stack exchange,提问作者Anthony Brian Mallgren

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:35:16