Search code examples
c#.netentity-frameworkwcf-data-servicesodata

Update on any properties with odata


Do you know how I could update an entity in WCF Data Services with OData syntax without the key property of the entity.

For example, an entity:

public class Product
{
    [Key]
    public int Id { get; set; }

    public string Reference { get; set; }
}

I would like to make this request:

PUT myservice.svc/Product('REFXX') 

with 'REFXXX' corresponding do the Reference property (which is unique).

Any idea?


Solution

  • I wrote a IDispatchMessageInspector, parse the url and replace the match element in the request parameter with a correct syntax and the real key. I know that the key is not the real "Key" with a specific user agent or with the syntax Service.svc/Entity(SecondaryKey=value), which is used normally for multiple pk's.

    so in the method AfterReceiveRequest the process is:

    • parse the url Service.svc/Entity(SecondaryKey=value)
    • get the key value of the entity (by building a dynamic linq expression)
    • change the match element of the request with Service.svc/Entity(PKValue)

             public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
      {
      
          if (request.Properties.ContainsKey("UriTemplateMatchResults") && HttpContext.Current != null)
          {
              //get match for current request
              UriTemplateMatch match = (UriTemplateMatch)request.Properties["UriTemplateMatchResults"];
      
              Utils.ODataBasicUriParser uriParser = new Utils.ODataBasicUriParser(match.RequestUri.PathAndQuery);
      
              //verify if this is a SecondaryKey request
              if (uriParser.IsEntityQuery && uriParser.IsSecondaryKeyQuery)
              {               
                  //TODO this syntax is also used for entities with multiple pk's, test it
      
                  //get a new data context
                  //TODO see if this can be improved, avoid two datacontext for one request 
                  DataContext ctx = new DataContext();
      
                  Type outType;
                  //get entity type name from the service name 
                  string entityName = DataContext.GetEntityNameByServiceName(uriParser.EntityServiceName);
      
                  //get the pk for the entity
                  string id = ctx.GetEntityId(entityName, uriParser.EntityKey, uriParser.EntityId, out outType);
      
                  //verify if the pk has been found or cancel this to continue with standart request process
                  if (string.IsNullOrEmpty(id))
                  {
                      Trace.TraceWarning(string.Format("Key property not found for the the entity:{0}, with secondaryKeyName:{1} and secondaryKeyValue:{2}",
                          entityName, uriParser.EntityKey, uriParser.EntityId));
      
                      return System.Net.HttpStatusCode.NotFound;
                  }
      
                  //in odata syntax quotes are required for string values, nothing for numbers
                  string quote = outType.FullName == typeof(Int32).FullName || outType.FullName == typeof(Int64).FullName ? string.Empty : "'";
      
                  //build the new standart resource uri with the primary key
                  var newUri = new Uri(string.Format("{0}/{1}({2}{3}{2})", match.BaseUri.ToString(), uriParser.EntityServiceName, quote, id));
      
                  //create a new match to replace in the current request, with the new Uri
                  UriTemplateMatch newMatch = NewMatch(match, newUri); 
      
                  //set request values
                  request.Properties["UriTemplateMatchResults"] = newMatch;
                  request.Headers.To = newUri;
                  request.Properties.Via = newUri;
              }
          }
      
          return null;
      }
      
      
      UriTemplateMatch NewMatch(UriTemplateMatch match, Uri newUri)
      {
          UriTemplateMatch newMatch = new UriTemplateMatch();
          newMatch.RequestUri = newUri;
          newMatch.Data = match.Data;
          newMatch.BaseUri = match.BaseUri;
      
          return newMatch;
      }
      

    works for my current needs