public async Task<IHttpActionResult> Put(int key, Customer customer, ODataQueryOptions<Customer> options) { if (!ModelState.IsValid) { return BadRequest(ModelState); } if (!(key == customer.Id)) { return BadRequest("The customer Id must match the key in the URI"); } if (options.IfMatch != null) { if (!_context.Customers.Where(c => c.Id == key).Any()) { // The entity doesn't exist on the database and as the request contains an If-Match header we don't // insert the entity instead (No UPSERT behavior if the If-Match header is present). return NotFound(); } else if (!((IQueryable<Customer>)options.IfMatch.ApplyTo(_context.Customers.Where(c => c.Id == key))).Any()) { // The ETag of the entity doesn't match the value sent on the If-Match header, so the entity has // been modified by a third party between the entity retrieval and update.. return StatusCode(HttpStatusCode.PreconditionFailed); } else { // The entity exists in the database and the ETag of the entity matches the value on the If-Match // header, so we update the entity. _context.Entry(customer).State = EntityState.Modified; await _context.SaveChangesAsync(); return Ok(customer); } } else { if (!_context.Customers.Where(c => c.Id == key).Any()) { // The request didn't contain any If-Match header and the entity doesn't exist on the database, so // we create a new one. For more details see the section 11.4.4 of the OData v4.0 specification. _context.Customers.Add(customer); await _context.SaveChangesAsync(); return base.Created(customer); } else { // the request didn't contain any If-Match header and the entity exists on the database, so we // update it's value. _context.Entry(customer).State = EntityState.Modified; await _context.SaveChangesAsync(); return Ok(customer); } } }
private static void RunQueries() { HttpClient client = new HttpClient(); HttpRequestMessage request; HttpResponseMessage response; // Retrieving an entity for the first time. Observe that the ETag is in the response headers and // the returned payload contains the annotation @odata.etag indicating the ETag associated with that customer. Console.WriteLine("Retrieving a single customer at {0}/odata/Customers(1)", serverUrl); Console.WriteLine(); request = new HttpRequestMessage(HttpMethod.Get, serverUrl + "/odata/Customers(1)"); response = client.SendAsync(request).Result; response.EnsureSuccessStatusCode(); Console.WriteLine(response.ToString()); dynamic customer = JObject.Parse(response.Content.ReadAsStringAsync().Result); Console.WriteLine(customer); Console.WriteLine(); Console.WriteLine("-----------------------------------------------------"); string etag = customer["@odata.etag"]; // Retrieving the same customer as in the previous request but only if the ETag doesn't match the one // specified in the If-None-Match header. We are sending the ETag value that we obtained from the previous // request, so we expect to see a 304 (Not Modified) response. Console.WriteLine("Retrieving the customer at {0}/odata/Customers(1) when the Etag value sent matches"); request = new HttpRequestMessage(HttpMethod.Get, serverUrl + "/odata/Customers(1)"); request.Headers.IfNoneMatch.Add(EntityTagHeaderValue.Parse(etag)); response = client.SendAsync(request).Result; Console.WriteLine("The response status code is: {0}", response.StatusCode); Console.WriteLine(); Console.WriteLine("-----------------------------------------------------"); // Retrieving the same customer as in the previous request but only if the ETag doesn't match the one // specified in the If-None-Match header. We are sending a different ETag value, so we expect to see a 200 // (OK) response. Console.WriteLine("Retrieving the customer at {0}/odata/Customers(1) when the Etag value sent matches"); request = new HttpRequestMessage(HttpMethod.Get, serverUrl + "/odata/Customers(1)"); request.Headers.IfNoneMatch.Add(EntityTagHeaderValue.Parse("W/\"MQ==\"")); response = client.SendAsync(request).Result; Console.WriteLine("The response status code is {0}", response.StatusCode); Console.WriteLine(JObject.Parse(response.Content.ReadAsStringAsync().Result)); Console.WriteLine(); Console.WriteLine("-----------------------------------------------------"); // Removing the annotations from the customer object as they are not required on the following requests and // changing the age value. customer.Age = 99; customer.Remove("@odata.etag"); customer.Remove("@odata.context"); // Trying to update the customer using a different ETag value than the ETag on the previous request. The // server will return a 412 (Precondition Failed) response. request = new HttpRequestMessage(HttpMethod.Put, serverUrl + "/odata/Customers(1)"); // Setting up a different ETag value. request.Headers.IfMatch.Add(EntityTagHeaderValue.Parse("W/\"MQ==\"")); request.Content = new ObjectContent<JObject>(customer, new JsonMediaTypeFormatter()); Console.WriteLine("Trying to update the Customer using a different ETag value on the If-Match header and failing"); response = client.SendAsync(request).Result; Console.WriteLine("The response status code is {0}", response.StatusCode); Console.WriteLine(); Console.WriteLine("-----------------------------------------------------"); // Trying to update the customer using the ETag value retrieved from the first request. The server will // process the request and return a 200 (OK) response. request = new HttpRequestMessage(HttpMethod.Put, serverUrl + "/odata/Customers(1)"); request.Headers.IfMatch.Add(EntityTagHeaderValue.Parse(etag)); request.Content = new ObjectContent<JObject>(customer, new JsonMediaTypeFormatter()); Console.WriteLine("Trying to update a Customer using the same ETag value on the If-Match header and succeeding"); Console.WriteLine(); response = client.SendAsync(request).Result; Console.Write(JObject.Parse(response.Content.ReadAsStringAsync().Result)); Console.WriteLine(); Console.WriteLine("-----------------------------------------------------"); // Trying to update the customer using the If-Match header and sending an entity that doesn't exist on the // database. The behavior in this case is "strict update", meaning that the server shouldn't try to insert // the entity instead, so the answer we receive is 404 (Not Found). request = new HttpRequestMessage(HttpMethod.Put, serverUrl + "/odata/Customers(30)"); request.Headers.IfMatch.Add(EntityTagHeaderValue.Parse("W/\"MQ==\"")); Customer newCustomer = new Customer { Id = 30, Name = "New customer", Age = 30, Version = 0 }; request.Content = new ObjectContent<Customer>(newCustomer, new JsonMediaTypeFormatter()); Console.WriteLine("Trying to update a non existing customer with the If-Match header present"); Console.WriteLine(); response = client.SendAsync(request).Result; Console.WriteLine("The response status code is {0}", response.StatusCode); Console.WriteLine(); Console.WriteLine("-----------------------------------------------------"); // Trying the same request but without the If-Match header. The behavior in this case is "update or insert", // meaning that the server should try to insert the entity if it doesn't exist, so the answer we receive is // 201 (Created). request = new HttpRequestMessage(HttpMethod.Put, serverUrl + "/odata/Customers(30)"); request.Content = new ObjectContent<Customer>(newCustomer, new JsonMediaTypeFormatter()); Console.WriteLine("Trying to update a non existing customer without the If-Match header"); Console.WriteLine(); response = client.SendAsync(request).Result; Console.WriteLine("The response status code is {0}", response.StatusCode); Console.WriteLine(JObject.Parse(response.Content.ReadAsStringAsync().Result)); Console.WriteLine(); Console.WriteLine("-----------------------------------------------------"); }