EF 和 [JsonIgnore]:为啥我在使用现有类型时得到空字典,但在使用匿名类型时却没有?

Posted

技术标签:

【中文标题】EF 和 [JsonIgnore]:为啥我在使用现有类型时得到空字典,但在使用匿名类型时却没有?【英文标题】:EF and [JsonIgnore]: Why do I get an empty dictionary when using an existing type, but not when using an anonymous type?EF 和 [JsonIgnore]:为什么我在使用现有类型时得到空字典,但在使用匿名类型时却没有? 【发布时间】:2021-12-23 05:46:19 【问题描述】:

问题

当使用匿名类型时它可以工作,但当使用与匿名类型相同的ClientGroupView 类型时则不行。为什么?

我怀疑这与 [JsonIgnore] 属性有关 - 它以某种方式持续存在于 类型化对象,但不存在于 匿名对象

背景

我在 ClientsClientGroups 之间建立了多对多关系,并且控制器返回给定客户端的组。

当我使用类型化对象时,控制器方法返回带有空字典的Json,但如果我使用匿名对象,则返回预期的Json。我使用 .NET 5 和 .Net 6 得到相同的结果。

使用匿名对象时,返回的Json为:

[
    "group": "clientGroupId":1, "groupName":"ClientGroup1" ,
    "permissions": "rClientGroupClientId":1, "clientGroupId":1, "clientId":1 
]

当使用 ClientGroupView 类型的对象(不包含任何 [JsonIgnore] 属性)时,返回的 Json 为:

[]

我这两种情况result是一样的,在转换为Json之前,返回Ok(result)

控制器

返回给定ClientId信息的控制器方法:

[HttpGet]
[Route("id")]
public async Task<IActionResult> GetClientGroups(int id)

    var client = await db.Clients.FindAsync(id);
    if (client == null)
        return NotFound();

    // Works when using an anonymous type - the Json dictionary is not empty.
    var groups = db.Entry(client)
            .Collection(r => r.XClientGroups)
            .Query()
            .Include(r => r.ClientGroup)
            .Select(r => new  r.ClientGroup, r );

    // But fails when not using an anonymous type - the json dictionary is empty.
    //var groups = db.Entry(client)
    //        .Collection(r => r.XClientGroups)
    //        .Query()
    //        .Include(r => r.ClientGroup)
    //        .Select(r => new ClientGroupView(r.ClientGroup, r));
            
    var result = await groups.ToListAsync();
    if (result == null || result.Count <= 0)
        return NotFound();
    return Ok(result);

Client

public class Client

    public int ClientId  get; set; 
    public string ClientName  get; set;  = string.Empty;

    [JsonIgnore]
    public virtual ICollection<X_ClientGroup_Client> XClientGroups  get; set; 

    public Client()
    
        XClientGroups = new HashSet<X_ClientGroup_Client>();
    

ClientGroup

public class ClientGroup

    public int ClientGroupId  get; set; 
    public string GroupName  get; set; 

    [JsonIgnore]
    public virtual ICollection<X_ClientGroup_Client> XClients  get; set; 

    public ClientGroup()
    
        XClients = new HashSet<X_ClientGroup_Client>();
    

X_ClientGroup_Client

ClientClientGroup 之间的多对多关系:

public class X_ClientGroup_Client

    public int RClientGroupClientId  get; set; 
    public int ClientGroupId  get; set; 
    public int ClientId  get; set; 

    [JsonIgnore]
    public ClientGroup ClientGroup  get; set; 
    [JsonIgnore]
    public Client Client  get; set; 

ClientGroupView

这里是对应匿名对象的ClientGroupView。它不包含任何[JsonIgnore] 属性:

public class ClientGroupView

    public ClientGroup Group;
    public X_ClientGroup_Client Permissions;
    public ClientGroupView(ClientGroup group, X_ClientGroup_Client permissions)
    
        this.Group = group;
        this.Permissions = permissions;
    

如果我删除构造函数并像使用匿名对象一样构造对象,结果是一样的。

一个解决方案是更改ClientGroupView 以包含字段而不是类,但我更喜欢它包含类而不是类的字段,因为它更易于维护:

// I do not want to use this version of ClientGroupView
public class ClientGroupView

    public int RClientGroupClientId  get; set; 
    public int ClientId  get; set; 
    public int ClientGroupId  get; set; 
    public string GroupName  get; set; 

    public ClientGroupView(ClientGroup clientgroup, X_ClientGroup_Client permissions)
    
        RClientGroupClientId = permissions.RClientGroupClientId;
        ClientId = permissions.ClientId;
        ClientGroupId = clientgroup.ClientGroupId;
        GroupName = clientgroup.GroupName;
    

我不想使用这个版本的原因是因为它需要更多的维护因为控制器是内部使用的,即我不需要使用 DTO。

【问题讨论】:

如何(1)制作具体的类字段属性,以及(2)使用初始化器(new ClientGroupView Group = ..., ... )而不是构造器? @IvanStoev 我已经尝试过了,它产生了相同的结果。 我试过这个模型,但无法重现 - 无论是在 EFC 5.0 还是 EFC 6.0 中 @IvanStoev 我找到了一个修复程序,我将其添加到问题中,但我对此并不满意。感谢您的帮助。 @IvanStoev 感谢您对其进行测试,这让我意识到这可能与我省略的 [JsonIgnore] 标记有关。我已经修改了问题。 【参考方案1】:

ClientGroupView.GroupClientGroupView.Permissions 是字段,而不是属性。

添加 get; set;

【讨论】:

据我所知,您知道为什么匿名对象没有 getter 和 setter 吗? 因为匿名类型被编译为具有只读属性的类型 (docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/…)。

以上是关于EF 和 [JsonIgnore]:为啥我在使用现有类型时得到空字典,但在使用匿名类型时却没有?的主要内容,如果未能解决你的问题,请参考以下文章

EF4 独立协会 - 为啥要避免它们?

webservice使用EF生成的model序列化问题

EF Core Eager 加载返回 null

强制杰克逊在没有 JsonIgnore 的情况下忽略 isEmpty

为啥 EF Core 2.0 会生成多个重复的 SQL 语句?

EF Core - 为啥显式加载非常慢?