Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inserting Entities in OData with required Foreign Keys

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?

like image 952
LueTm Avatar asked Feb 06 '15 11:02

LueTm


1 Answers

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.

like image 112
Luke Avatar answered Nov 01 '22 17:11

Luke