从 POST 参数中删除导航 属性

Remove navigation property from POST parameters

我开始使用 ASP.NET Core 和 EF Core 6。我正在尝试构建一个简单的网络 api,其中包含两个模型以及模型之间的 1-n 关系。

型号A:

public class ModelA
{
    public Guid Id { get; set; }
    public string StringProperty { get; set; }

    public ICollection<ModelB> ModelBs { get; set; }
}

模型 B:

public class ModelB
{
    public Guid Id { get; set; }
    public string StringProperty { get; set; }

    public Guid ModelAId { get; set; }
    public ModelA ModelA { get; set; }
}

当我尝试使用 ModelB-Controller 的 POST-endpoint 创建 ModelB 时,它希望我也通过 ModelA。如果我确实提供了它,我会收到重复键错误,因为 EF 试图在数据库中创建一个新的 ModelA,这会导致重复键错误。

我只能使用 ModelB 模型作为 post 方法的参数,并且明确不能使用任何类型的中间模型。

我只想使用 ModelA id,而不是整个 ModelA 对象:

//Desired post-request
{
    "stringProperty": "value",
    "modelAId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

使 ModelA 引用可为空允许 post 请求,如上所述:

public class ModelB
{
    public Guid Id { get; set; }
    public string StringProperty { get; set; }

    public Guid ModelAId { get; set; }
    public ModelA? ModelA { get; set; }
}

但这感觉不对,因为如果没有对 ModelA 实例的引用,则 ModelB 的实例不能存在。 有没有办法在不使用 DTO 或使引用可为空的情况下实现这一目标? 我可能在这里遗漏了一些微不足道的东西。

我认为你应该在你的项目中使用 DTO!例如,模型 B 将只有创建项目所需的数据,即 StringProperty 和 ModelAId,并且在您的控制器内部,您将与现有的模型 A 关联。

你可以看看下面link上的entity framework。

https://www.entityframeworktutorial.net/code-first/configure-one-to-many-relationship-in-code-first.aspx

即使您创建一个 DTO(强烈推荐,传递一个完整的另一个对象只是为了显示关系是浪费带宽和不好的做法),您也会接受来自用户的 ModelAId,而不是整个对象。
编辑: 如果你想欺骗产品负责人。只需创建一个没有 ModelA 道具的基础 class,ModelB 中的所有其余道具现在使 ModelB 继承它并显式添加 ModelA。现在创建 ModelBPost 也继承自此并将其用作 POST 数据的参数,这样产品所有者就知道字段完全相同并且您通过了验证错误。
旧答案

你如何从数据库中获取新的模型 A 并将其分配给模型 B 喜欢

public IAction PostModelB(ModelB modelB)
{
   modelB.ModelA = context.ModelAs.First(x => x.Id == modelB.ModelAId);
   //now since efcore is tracking this it know this object already exists
   context.ModelBs.Add(modelB);  
}

然而,有时 EFCore 搞砸了,你会得到一个已经跟踪的错误(这是非常不幸的,经过这么多时间它仍然无法正确完成)。如果发生这种情况(这可能是因为你坚持不使用 DTO 并接受整个对象只是为了关系)你将不得不设置导航 属性 null 并且只使用 ModelAId 属性插入新记录你可以得到efcore持有的实例:

var modelA = context.ModelAs.First(x => x.Id == modelB.ModelAId);
var trackedinstance = context.ChangeTracker.Entry(modelA)?.Entity ?? modelA;
modelB.ModelA = trackedinstance;
modelB.ModelAId = modelA.Id;