使用所需的外键在OData中插入实体

时间:2015-02-06 11:44:33

标签: c# .net asp.net-web-api odata

EDIT-2:经过几个小时的研究,几乎所有与谷歌相关的链接都变成了紫色,我发现了“深插入”的概念' (link)存在于OData规范中。毕竟,即使没有链接,我所做的也应该有效。有谁知道如何在Microsoft OData客户端上启用它?还有其他OData客户支持这个概念吗?

编辑:也许这是错误的做法,所以请告诉我,我是否做错了。无法保存真的阻碍了我们的进步!

我对OData v3有疑问。我有一个Associate课程,其中包含必需的Address。当我尝试POST一个新的Associate时,由于Address属性为null(EF6因外键违反而抛出DbUpdateException)而失败。我的Associate课程如下:

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; }
}

我使用Microsoft 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"
}

即使客户端上生成的类具有Address属性,也不会传输地址。

我该如何解决这个问题?

4 个答案:

答案 0 :(得分:1)

我也找不到任何关于此的信息 - 这确实是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);
        }
  }

因此,从那时起发布学生,您将忽略教师字段,只需使用TeacherId发布。

我没有用OData客户端对此进行测试,但我想不出为什么这不起作用。您只需使用Id字段而不是对象。

答案 1 :(得分:0)

基本上在创建对象时

var associate = new Associate { /* ... */ };

它未插入数据库。它是在内存中创建的。当你打电话

context.SaveChanges();

它将保存在数据库中。此时,数据库验证发生并生成密钥。假设您的ID是在数据库中生成的唯一标识符,请注意,为了使其从数据库中获取更新值,您需要将 StoreGeneratedPattern 设置为身份从实体模型视图。

如果不这样做,您的本地上下文和数据库上下文将不再匹配。如果您在哪里使用该对象而引用其他内容则会失败。

我认为这样的事情会奏效:

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();

答案 2 :(得分:0)

没有解决方法。我将使用与web-api一起使用的变更集概念来推广我自己的上下文。当我完成时,我会把它放在github上。

答案 3 :(得分:0)

addlink和setlink工作的唯一方法是,如果外键可以为空,并且你要创建一个postput函数调用create link,请参阅here