I am very much puzzled and hope somebody can shed any light. After upgrading to the latest version of Web API (from Microsoft.AspNet.WebApi.xxx 5.0.0. to Microsoft.AspNet.WebApi.xxx 5.1.2) OData's EntitySetController stopped processing PATCH verbs and returns 406 (Not Acceptable) code instead.
The implementation hasn't changed:
public class ODataAnnouncementController : EntitySetController<TEntity, TKey> where TEntity : class
{
(...)
protected override Announcement PatchEntity(Guid key, Delta<Announcement> patch)
{
var announcement = getEntityByKeyOrThrowException(key);
patch.Patch(announcement);
AnnouncementManager.Instance.Save(announcement, true);
return announcement;
}
}
The request hasn't changed either:
Accept: application/json
Content-Type: application/json; charset=utf-8
PATCH: http://{url}/odata/Announcement(guid'62E34FB3-A37E-413F-9843-642D95BA580A')
{"Description":"Patched description from Unit Test"}
Breakpoint inside the method is not being hit as if "Delta<Announcement>> patch" has never deserialized. The only condition under which it actually does hit the breakpoint inside the method is when a request BODY is empty resulting with patch object being null of course.
All other verbs (GET, POST, PUT, DELETE) work OK.
This used to work before. Is anybody aware of any breaking changes for Web API 2.1 in regards to PATCH? How else could I troubleshoot it?
Thank you so much
Bill
UPDATE
Here is the C# model:
[Serializable]
[DataContract]
public class Announcement
{
private Guid _announcementID;
private string _description;
private bool _isPrivate;
private DateTime _createdTime;
private Nullable<Guid> _membershipID;
[DataMember]
public Guid AnnouncementID
{
get { return this._announcementID; }
set { this._announcementID = value; }
}
[DataMember]
public string Description
{
get { return this._description; }
set { this._description = value; }
}
[DataMember]
public bool IsPrivate
{
get { return this._isPrivate; }
set { this._isPrivate = value; }
}
[DataMember]
public DateTime CreatedTime
{
get { return this._createdTime; }
set { this._createdTime = value; }
}
[DataMember]
public Nullable<Guid> MembershipID
{
get { return this._membershipID; }
set { this._membershipID = value; }
}
}
I use explicit data model:
var routingConventions = ODataRoutingConventions.CreateDefault();
config.Routes.MapODataRoute(
routeName: "ODataRoute",
routePrefix: "odata",
model: BuildExplicitODataModel(),
pathHandler: new DefaultODataPathHandler(),
routingConventions: routingConventions
);
public static IEdmModel BuildExplicitODataModel()
{
ODataModelBuilder modelBuilder = new ODataModelBuilder();
var announcementSet = buildAnnouncementModel(modelBuilder);
return modelBuilder.GetEdmModel();
}
private static EntitySetConfiguration<Announcement> buildAnnouncementModel(ODataModelBuilder modelBuilder)
{
var announcementSet = modelBuilder.EntitySet<Announcement>("Announcement");
announcementSet.HasEditLink(
entityContext => entityContext.Url.ODataLink(
new EntitySetPathSegment(entityContext.EntitySet.Name),
new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(entityContext.EntityInstance.AnnouncementID, ODataVersion.V3))),
followsConventions: true);
announcementSet.HasIdLink(
entityContext => entityContext.Url.ODataLink(
new EntitySetPathSegment(entityContext.EntitySet.Name),
new KeyValuePathSegment(ODataUriUtils.ConvertToUriLiteral(entityContext.EntityInstance.AnnouncementID, ODataVersion.V3))),
followsConventions: true);
var announcement = announcementSet.EntityType;
announcement.HasKey(p => p.AnnouncementID);
announcement.Property(p => p.Description);
announcement.Property(p => p.IsPrivate);
announcement.Property(p => p.CreatedTime);
announcement.Property(p => p.MembershipID);
return announcementSet;
}
Here is request payload:
PATCH http://{url}/odata/Announcement(guid'62E34FB3-A37E-413F-9843-642D95BA580A')
Accept: application/json
Content-Type: application/json; charset=utf-8
BODY:
{"Description":"Patched description from Unit Test"}