使用所需的外键在 OData 中插入实体
Inserting Entities in OData with required Foreign Keys
EDIT-2: 经过数小时的研究和几乎所有与 link 有关 google 变紫的 odata,我发现 google 的概念 'deep-inserts' (link) 存在于 OData 规范中。所以毕竟,即使没有 links,我所做的也应该有效。有谁知道如何在 Microsoft OData 客户端上启用它?是否有其他支持该概念的 OData 客户端?
编辑: 也许这是错误的方法,如果我做的完全错了,请告诉我。无法保存真的阻碍了我们的进步!
我对 OData v3 有疑问。我有一个 class Associate
,它有一个必需的 Address
。当我尝试 POST 一个新的 Associate 时,由于 Address
属性 为空(EF6 抛出外键违规的 DbUpdateException)而失败。我的 Associate
class 看起来像这样:
public class Associate
{
public int Id { get; set; }
[Required, StringLength(100)]
public string Name { get; set; }
[Required, StringLength(50)]
public string Role { get; set; }
public bool IsMailReceiver { get; set; }
public bool IsLegalRepresentative { get; set; }
[ForeignKey("AddressId")]
public virtual Address Address { get; set; }
public int AddressId { get; set; }
}
我使用微软OData客户端,尝试通过以下方式添加关联:
var associate = new Associate { /* ... */ };
context.AddObject("Associates", associate);
context.AddObject("Addresses", associate.Address);
/* UI fills associate data */
context.SetLink(associate, "Address", associate.Address);
context.UpdateObject(associate);
context.UpdateObject(associate.Address);
/* at this point the associate has the address set! */
context.SaveChanges(); // << Exception
然而,在服务器上,在控制器中,Associate 到达时没有外键。当我使用 Fiddler 检查 POST 请求时,我明白了原因:
{
"odata.type" : "xxx.Data.Entities.Associate",
"AddressId" : 0,
"Id" : 0,
"IsLegalRepresentative" : false,
"IsMailReceiver" : false,
"Name" : "John Doe",
"Role" : "Father"
}
即使在客户端生成的 class 有一个 Address
属性.
,地址也不会传输
我该如何解决这个问题?
基本上当你创建对象时
var associate = new Associate { /* ... */ };
没有插入数据库。它是在内存中创建的。当你打电话
context.SaveChanges();
它将保存在数据库中。此时发生数据库验证并生成密钥。假设您的 Id 是在数据库中生成的唯一标识符,请注意,为了使其从数据库中获取更新的值,您需要将 StoreGeneratedPattern 设置为 实体模型视图中的 Identity。
如果不这样做,您的本地上下文和数据库上下文将不再匹配。如果你在哪里使用那个对象来引用其他东西,它会失败。
我假设这样的事情会起作用:
Address address = new Address{ City = "Tallinn" /*etc*/};
context.SaveChanges();
//At this point Address will be in database context and has Id
associate = new Associate {
name = "Margus",
role = "Admin",
receiver = true,
representative = true,
AddressId = address.id
};
context.SaveChanges();
没有解决办法。我将使用适用于 web-api 的更改集概念来滚动我自己的上下文。完成后我会把它放在 github 上。
我也找不到任何关于此的信息 - 这确实是 OData 中的一个问题。这是我设法让它工作的方法。
显式定义外键
class Student {
public int TeacherId { get; set; }
[Required, ForeignKey("TeacherId")]
public virtual Teacher Teacher { get; set; }
}
执行插入时,获取相关记录并修复模型状态:
public IHttpActionResult Post(Student student)
{
student.Teacher = this.db.Teacher.FirstOrDefault(i => i.TeacherId == student.TeacherId);
if (student.Teacher != null)
{
this.ModelState.Remove("student.Teacher");
}
if (!this.ModelState.IsValid)
{
return this.BadRequest(this.ModelState);
}
}
所以从那时起 post 一个学生,你忽略教师字段,只 post 和 TeacherId。
我还没有用 OData 客户端测试过这个,但我想不出为什么这不起作用。您只需要使用 Id 字段而不是对象。
addlink 和 setlink 工作的唯一方法是如果外键可以为 null 并且您可能会创建一个 postput 函数调用 create link 请参阅 here
EDIT-2: 经过数小时的研究和几乎所有与 link 有关 google 变紫的 odata,我发现 google 的概念 'deep-inserts' (link) 存在于 OData 规范中。所以毕竟,即使没有 links,我所做的也应该有效。有谁知道如何在 Microsoft OData 客户端上启用它?是否有其他支持该概念的 OData 客户端?
编辑: 也许这是错误的方法,如果我做的完全错了,请告诉我。无法保存真的阻碍了我们的进步!
我对 OData v3 有疑问。我有一个 class Associate
,它有一个必需的 Address
。当我尝试 POST 一个新的 Associate 时,由于 Address
属性 为空(EF6 抛出外键违规的 DbUpdateException)而失败。我的 Associate
class 看起来像这样:
public class Associate
{
public int Id { get; set; }
[Required, StringLength(100)]
public string Name { get; set; }
[Required, StringLength(50)]
public string Role { get; set; }
public bool IsMailReceiver { get; set; }
public bool IsLegalRepresentative { get; set; }
[ForeignKey("AddressId")]
public virtual Address Address { get; set; }
public int AddressId { get; set; }
}
我使用微软OData客户端,尝试通过以下方式添加关联:
var associate = new Associate { /* ... */ };
context.AddObject("Associates", associate);
context.AddObject("Addresses", associate.Address);
/* UI fills associate data */
context.SetLink(associate, "Address", associate.Address);
context.UpdateObject(associate);
context.UpdateObject(associate.Address);
/* at this point the associate has the address set! */
context.SaveChanges(); // << Exception
然而,在服务器上,在控制器中,Associate 到达时没有外键。当我使用 Fiddler 检查 POST 请求时,我明白了原因:
{
"odata.type" : "xxx.Data.Entities.Associate",
"AddressId" : 0,
"Id" : 0,
"IsLegalRepresentative" : false,
"IsMailReceiver" : false,
"Name" : "John Doe",
"Role" : "Father"
}
即使在客户端生成的 class 有一个 Address
属性.
我该如何解决这个问题?
基本上当你创建对象时
var associate = new Associate { /* ... */ };
没有插入数据库。它是在内存中创建的。当你打电话
context.SaveChanges();
它将保存在数据库中。此时发生数据库验证并生成密钥。假设您的 Id 是在数据库中生成的唯一标识符,请注意,为了使其从数据库中获取更新的值,您需要将 StoreGeneratedPattern 设置为 实体模型视图中的 Identity。
如果不这样做,您的本地上下文和数据库上下文将不再匹配。如果你在哪里使用那个对象来引用其他东西,它会失败。
我假设这样的事情会起作用:
Address address = new Address{ City = "Tallinn" /*etc*/};
context.SaveChanges();
//At this point Address will be in database context and has Id
associate = new Associate {
name = "Margus",
role = "Admin",
receiver = true,
representative = true,
AddressId = address.id
};
context.SaveChanges();
没有解决办法。我将使用适用于 web-api 的更改集概念来滚动我自己的上下文。完成后我会把它放在 github 上。
我也找不到任何关于此的信息 - 这确实是 OData 中的一个问题。这是我设法让它工作的方法。
显式定义外键
class Student {
public int TeacherId { get; set; }
[Required, ForeignKey("TeacherId")]
public virtual Teacher Teacher { get; set; }
}
执行插入时,获取相关记录并修复模型状态:
public IHttpActionResult Post(Student student)
{
student.Teacher = this.db.Teacher.FirstOrDefault(i => i.TeacherId == student.TeacherId);
if (student.Teacher != null)
{
this.ModelState.Remove("student.Teacher");
}
if (!this.ModelState.IsValid)
{
return this.BadRequest(this.ModelState);
}
}
所以从那时起 post 一个学生,你忽略教师字段,只 post 和 TeacherId。
我还没有用 OData 客户端测试过这个,但我想不出为什么这不起作用。您只需要使用 Id 字段而不是对象。
addlink 和 setlink 工作的唯一方法是如果外键可以为 null 并且您可能会创建一个 postput 函数调用 create link 请参阅 here