Inserting Entities in OData with required Foreign Keys

烈酒焚心 提交于 2019-12-12 08:48:27

问题


EDIT-2: After hours of research and almost every odata related link on google turning purple, I found out that the concept of 'deep-inserts' (link) exists in the OData specification. So after all, what I'm doing should work, even without the links. Does anyone know how to enable this on the Microsoft OData client? Are there any other OData clients out there that support that concept?

EDIT: Maybe this is the wrong approach, so please tell me if I'm doing it totally wrong. Not being able to save is really blocking our progress!

I have an issue with OData v3. I have a class Associate that has a required Address. When I try to POST a new Associate, it fails due to the Address property being null (EF6 throws DbUpdateException with foreign key violation). My Associate class looks like this:

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

I use the Microsoft OData client, and try to add the associate in the following way:

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

On the server, in the controller, the Associate arrives without the foreign key, however. When I inspect the POST request with Fiddler, I see why:

{
    "odata.type" : "xxx.Data.Entities.Associate",
    "AddressId" : 0,
    "Id" : 0,
    "IsLegalRepresentative" : false,
    "IsMailReceiver" : false,
    "Name" : "John Doe",
    "Role" : "Father"
}

The address is not transmitted, even though the generated class on the client has an Address property.

How can i solve this problem?


回答1:


I too could not find any information about this - it really feels like an issue in OData. Here is how I managed to get it to work.

Define the foreign key explicitly

class Student {
        public int TeacherId { get; set; }

        [Required, ForeignKey("TeacherId")]
        public virtual Teacher Teacher { get; set; }
}

When performing the insert, fetch the related record and fix the model state:

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

So from then on to post a Student, you ignore the Teacher field and just post with TeacherId.

I haven't tested this with the OData client, but I can't think of why this wouldn't work. You will just have to use the Id field rather than the object.




回答2:


Basically when you create the object

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

It is not inserted into the database. It is created in the memory. When you call

context.SaveChanges();

It will be saved in the database. At this point database validation happens and key's are generated. Assuming your Id is unique identifier, that is generated in the datebase, note that in order for it to get updated value back from the database you need to have StoreGeneratedPattern set to Identity from Entity model view.

If this is not done your local context and database context no longer match. If you where to use that object with reference to something else it would fail.

I assume something like this would work:

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



回答3:


There is no solution to this. I will roll my own context with a notion of change sets that works with web-api. I will put it on github, when I'm done.




回答4:


The only way addlink and setlink work is if the foreign key is nullable and you ahvbe to create a postput function call create link see here



来源:https://stackoverflow.com/questions/28365066/inserting-entities-in-odata-with-required-foreign-keys

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!