title: Web API + OData - PATCH request 400 error
body: <p>I have a KendoUI DataSource linked up to a WebApi 2 OData controller and am having problems with update operations. I can create and delete just fine. </p>
  
  <p>When I make the call to sync the datasource to the server after making any updates I get a 400 error:</p>
  
  <pre><code>{
    "odata.error":{
      "code":"","message":{
        "lang":"en-US","value":"The request is invalid."
      },"innererror":{
        "message":"patch : Invalid JSON. A token was not recognized in the JSON content.\r\n","type":"","stacktrace":""
      }
    }
  }
  </code></pre>
  
  <p>Debugging in Visual Studio shows that the patch function is being passed the Id but not the Company object. Firebug shows that the PATCH request looks like this:</p>
  
  <pre><code>models=%7B%22Id%22%3A1026%2C%22Title%22%3A%22Test+Company+test%22%7D
  </code></pre>
  
  <p>I have a hunch there is something wonky about this that the server doesn't understand. </p>
  
  <p>The model is simple and I left the controller as whatever VS generated for me:</p>
  
  <p><strong>Model:</strong> </p>
  
  <pre><code>public class Company {
      public Company() { }
  
      public Company(Company company) {
          this.Id = company.Id;
          this.Title = company.Title;
          this.Projects = company.Projects;
      }
  
      public int Id { get; set; }
      public string Title { get; set; }
  
      public virtual ICollection&lt;Project&gt; Projects { get; set; }
  }
  </code></pre>
  
  <p><strong>Controller:</strong></p>
  
  <pre><code>public class CompanyController : ODataController
  {
      private ApplicationDbContext db = new ApplicationDbContext();
  
      // GET odata/Company
      [Queryable]
      public IQueryable&lt;Company&gt; GetCompany()
      {
          return db.Companies;
      }
  
      // GET odata/Company(5)
      [Queryable]
      public SingleResult&lt;Company&gt; GetCompany([FromODataUri] int key)
      {
          return SingleResult.Create(db.Companies.Where(company =&gt; company.Id == key));
      }
  
      // PUT odata/Company(5)
      public async Task&lt;IHttpActionResult&gt; Put([FromODataUri] int key, Company company)
      {
          if (!ModelState.IsValid)
          {
              return BadRequest(ModelState);
          }
  
          if (key != company.Id)
          {
              return BadRequest();
          }
  
          db.Entry(company).State = EntityState.Modified;
  
          try
          {
              await db.SaveChangesAsync();
          }
          catch (DbUpdateConcurrencyException)
          {
              if (!CompanyExists(key))
              {
                  return NotFound();
              }
              else
              {
                  throw;
              }
          }
  
          return Updated(company);
      }
  
      // POST odata/Company
      public async Task&lt;IHttpActionResult&gt; Post(Company company)
      {
          if (!ModelState.IsValid)
          {
              return BadRequest(ModelState);
          }
  
          db.Companies.Add(company);
          await db.SaveChangesAsync();
  
          return Created(company);
      }
  
      // PATCH odata/Company(5)
      [AcceptVerbs("PATCH", "MERGE")]
      public async Task&lt;IHttpActionResult&gt; Patch([FromODataUri] int key, Delta&lt;Company&gt; patch)
      {
          if (!ModelState.IsValid)
          {
              return BadRequest(ModelState);
          }
  
          Company company = await db.Companies.FindAsync(key);
          if (company == null)
          {
              return NotFound();
          }
  
          patch.Patch(company);
  
          try
          {
              await db.SaveChangesAsync();
          }
          catch (DbUpdateConcurrencyException)
          {
              if (!CompanyExists(key))
              {
                  return NotFound();
              }
              else
              {
                  throw;
              }
          }
  
          return Updated(company);
      }
  
      // DELETE odata/Company(5)
      public async Task&lt;IHttpActionResult&gt; Delete([FromODataUri] int key)
      {
          Company company = await db.Companies.FindAsync(key);
          if (company == null)
          {
              return NotFound();
          }
  
          db.Companies.Remove(company);
          await db.SaveChangesAsync();
  
          return StatusCode(HttpStatusCode.NoContent);
      }
  
      // GET odata/Company(5)/Projects
      [Queryable]
      public IQueryable&lt;Project&gt; GetProjects([FromODataUri] int key)
      {
          return db.Companies.Where(m =&gt; m.Id == key).SelectMany(m =&gt; m.Projects);
      }
  
      protected override void Dispose(bool disposing)
      {
          if (disposing)
          {
              db.Dispose();
          }
          base.Dispose(disposing);
      }
  
      private bool CompanyExists(int key)
      {
          return db.Companies.Count(e =&gt; e.Id == key) &gt; 0;
      }
  }
  </code></pre>
  
  <p>Finally, the KendoUI, HTML/Javascript is this:</p>
  
  <pre><code>&lt;h2&gt;Company List&lt;/h2&gt;
  
  &lt;div id="company-data"&gt;
      &lt;div class="col-md-3 col-sm-5 col-xs-5"&gt;
          &lt;div id="company-list" style="padding: 0px; height: 500px; overflow: auto" data-role="listview" data-template="list-template" data-bind="source: companies, events: {change: OnSelect}" data-selectable="true"&gt;&lt;/div&gt;
          &lt;div&gt;
              &lt;button class="btn btn-success btn-sm" id="btn-add-company"&gt;&lt;span class="glyphicon glyphicon-plus"&gt;&lt;/span&gt; Add&lt;/button&gt;
              &lt;button class="btn btn-danger btn-sm" id="btn-delete-company" data-bind="visible: hasSelection, click: deleteSelection"&gt;&lt;span class="glyphicon glyphicon-remove"&gt;&lt;/span&gt; Delete&lt;/button&gt;
              &lt;button class="btn btn-default btn-sm" id="btn-clear-company" data-bind="visible: hasSelection, click: clearSelection"&gt;&lt;span class="glyphicon glyphicon-ban-circle"&gt;&lt;/span&gt; Clear&lt;/button&gt;
              &lt;button class="btn btn-primary btn-sm btn-block" id="btn-save" data-bind="visible: hasChanges, click: saveChanges"&gt;&lt;span class="glyphicon glyphicon-cloud-upload"&gt;&lt;/span&gt; Save All&lt;/button&gt;
          &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="col-md-9 col-sm-7 col-xs-7" data-bind="visible: hasSelection"&gt;
          &lt;label for="company-title"&gt;Title:&lt;/label&gt;&lt;br /&gt;
          &lt;input id="company-title" data-bind="value: selectedItem.Title" &gt;&lt;br /&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  
  &lt;script type="text/x-kendo-template" id="list-template"&gt;
      &lt;div class="company" style="cursor: pointer"&gt;
          &lt;span data-bind="text: Title"&gt;&lt;/span&gt;
      &lt;/div&gt;
  &lt;/script&gt;
  
  &lt;script&gt;
      $(function () {
          var firstSync = true;
          var companyVM = new kendo.observable({
              // Data Source.
              companies: new kendo.data.DataSource({
                  type: 'odata',
                  transport: {
                      create: {
                          url: '/odata/Company',
                          dataType: 'json',
                          type: 'POST'
                      },
                      read: {
                          url: '/odata/Company',
                          dataType: 'json'
                      },
                      update: {
                          url: function (data) {
                              return '/odata/Company(' + data.Id + ')';
                          },
                          dataType: 'json',
                          type: 'PATCH'
                      },
                      destroy: {
                          url: function (data) {
                              return '/odata/Company(' + data.Id + ')';
                          },
                          dataType: 'json',
                          type: 'DELETE'
                      },
                      parameterMap: function (options, operation) {
                          if (operation !== "read" &amp;&amp; options) {
                              console.log(operation + '*: ' + kendo.stringify(options));
                              return {
                                  models: kendo.stringify(options)
                              };
                          }
                          console.log(operation + ': ' + kendo.stringify(options));
                          return options;
                      }
                  },
                  schema: {
                      data: function (data) {
                          return data['value'];
                      },
                      total: function (data) {
                          return data['odata.count'];
                      },
                      model: {
                          id: 'Id',
                          fields: {
                              Title: { type: 'string' }
                          }
                      }
                  },
                  change: function () {
                      // We don't want to fire the first time the data loads because that counts as changed.
                      if (!firstSync)
                          companyVM.set('hasChanges', true);
                      else
                          firstSync = false;
                  }
              }),
  
              // Properties.
              selectedItem: null,
              hasSelection: function () {
                  return this.get('selectedItem') != null;
              },
              hasChanges: false,
  
              // Functions.
              clearSelection: function() {
                  this.set('selectedItem', null);
                  $('#company-list').getKendoListView().clearSelection();
              },
              saveChanges: function() {
                  this.companies.sync();
                  this.set('hasChanges', false);
              },
              deleteSelection: function () {
                  if (confirm('Warning, deletion is permanent! Are you sure you wish to delete this item?')) {
                      this.companies.remove(this.selectedItem);
                      this.set('hasChanges', true);
                      this.clearSelection();
                  }
              },
  
              // Events.
              OnSelect: function (e) {
                  var list = $(e.sender.element).getKendoListView();
                  var row = list.select();
                  var item = list.dataSource.getByUid(row.data('uid'));
  
                  this.set('selectedItem', item);
              }
          });
  
          kendo.bind($('#company-data'), companyVM);
      });
  &lt;/script&gt;
  </code></pre>