locked
Returning validation result RRS feed

  • Question

  • Hi,

     

    I am working on a MVC3 application that is exposing some data to a WPF Client using WCF DataServices.

    In order to reuse the DataAnnotation validation attributes, I am validating the entities using ChangeInterceptors on the server side.

    The problem I have found is that I couldn’t find an easy way to return the validation result to the client.

    [ChangeInterceptor("SomeEntitySet")]
    public void ValidateClinicalTrial(SomeEntity someEntity, UpdateOperations operation)
    {
      var result = Validate(someEntity); // using DataAnnotations.Validator
    
      if(result.Any())
        throw new DataServiceException("Data is invalid"); // <= here I need to return the validation result
    }
    

    I tried the following:

    -          Using fault contracts

    -          Using the Data property of the exception base (it is not serialized).

    -          Returning data as part of the response’s header

     

    Any thoughts?

     

    Thanks a lot

     

     

     


    Regards,

    Jorge Fioranelli
    http://blog.jorgef.net
    Wednesday, December 15, 2010 10:31 PM

Answers

  • This is fine: the client will capture this and rethrow a dataServiceClientException (or query/request exception, depending how you construct the request). If you unwrap it, and get to the innermost exception, you should see:

     

    <internalexception>
      <message>Data is invalid</message>
      <type>MyException</type>
      <stacktrace></stacktrace>
      </internalexception>

    Regards,
    PQ


    Peter Q. http://blogs.msdn.com/peter_qian
    Thursday, December 16, 2010 7:28 AM
    Answerer

All replies

  • Unfortunately you cannot return a message via the change interceptor, however, you can throw a better exception and the client will catch that exception message. I believe this is the right thing to do here because technically this is in the error-case scenario.

    Regards,

    PQ


    Peter Q. http://blogs.msdn.com/peter_qian
    Wednesday, December 15, 2010 11:15 PM
    Answerer
  • Hi Peter,

    Thanks for your answer!

    I tried throwing a custom exception but the only members that are serialized are the Message, Type and StackTrace. If I throws the following exception:

    [Serializable]
    public class MyException : InvalidOperationException
    {
      public MyException(string message, string additionalInformation)
        : base(message)
      {
        AdditionalInformation = additionalInformation;
      }
    
      protected MyException(SerializationInfo serializationInfo, StreamingContext streamingContext)
        : base(serializationInfo, streamingContext)
      {
        if (serializationInfo != null)
        {
          AdditionalInformation = serializationInfo.GetString("additionalInformation");
        }
      }
    
      public string AdditionalInformation { get; set; }
    
      public override void GetObjectData(SerializationInfo info, StreamingContext context)
      {
        base.GetObjectData(info, context);
    
        info.AddValue("additionalInformation", AdditionalInformation);
      }
    }
    

    In this way:

    [ChangeInterceptor("SomeEntitySet")]
    public void ValidateClinicalTrial(SomeEntity someEntity, UpdateOperations operation)
    {
     var result = Validate(someEntity);
    
     if(result.Any())
      throw new MyException("Data is invalid", "Additional information");
    }
    
    

    This is the message that is sent (without the "Additional information" value):

    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
     <code></code>
     <message xml:lang="en-AU">An error occurred while processing this request.</message>
     <innererror>
      <message>Exception has been thrown by the target of an invocation.</message>
      <type>System.Reflection.TargetInvocationException</type>
      <stacktrace>  at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct&amp; sig, MethodAttributes methodAttributes, RuntimeType typeOwner)&#xD;
      at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)&#xD;
      at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)&#xD;
      at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)&#xD;
      at System.Data.Services.UpdateTracker.FireNotifications()&#xD;
      at System.Data.Services.Serializers.Deserializer.HandlePutRequest(RequestDescription description, IDataService dataService, Stream stream)&#xD;
      at System.Data.Services.DataService`1.HandlePutOperation(RequestDescription description, IDataService dataService)&#xD;
      at System.Data.Services.DataService`1.ProcessIncomingRequest(RequestDescription description, IDataService dataService)&#xD;
      at System.Data.Services.DataService`1.HandleNonBatchRequest(RequestDescription description)&#xD;
      at System.Data.Services.DataService`1.HandleRequest()</stacktrace>
      <internalexception>
       <message>Data is invalid</message>
       <type>MyException</type>
       <stacktrace>[stacktrace]</stacktrace>
      </internalexception>
     </innererror>
    </error>
    

     I have also tried as innerexception of the DataServiceException:

    [ChangeInterceptor("SomeEntitySet")]
    public void ValidateClinicalTrial(SomeEntity someEntity, UpdateOperations operation)
    {
     var result = Validate(someEntity);
    
     if(result.Any())
      throw new DataServiceException("Data is invalid", new MyException("Data is invalid", "Additional information"));
    }
    

    And this is the message:

    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
     <code></code>
     <message xml:lang="en-AU">invalid data</message>
     <innererror>
      <message>Exception has been thrown by the target of an invocation.</message>
      <type>System.Reflection.TargetInvocationException</type>
      <stacktrace>  at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct&amp; sig, MethodAttributes methodAttributes, RuntimeType typeOwner)&#xD;
      at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner)&#xD;
      at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)&#xD;
      at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)&#xD;
      at System.Data.Services.UpdateTracker.FireNotifications()</stacktrace>
      <internalexception>
       <message>Data is invalid</message>
       <type>System.Data.Services.DataServiceException</type>
       <stacktrace>[stack trace]</stacktrace>
       <internalexception>
        <message>Data is invalid</message>
        <type>MyException</type>
        <stacktrace></stacktrace>
       </internalexception>
      </internalexception>
     </innererror>
    </error>
    

    Any idea?

    Thanks! 


    Regards,

    Jorge Fioranelli
    http://blog.jorgef.net
    Thursday, December 16, 2010 6:09 AM
  • This is fine: the client will capture this and rethrow a dataServiceClientException (or query/request exception, depending how you construct the request). If you unwrap it, and get to the innermost exception, you should see:

     

    <internalexception>
      <message>Data is invalid</message>
      <type>MyException</type>
      <stacktrace></stacktrace>
      </internalexception>

    Regards,
    PQ


    Peter Q. http://blogs.msdn.com/peter_qian
    Thursday, December 16, 2010 7:28 AM
    Answerer
  • Hi Peter,

    Does it mean that the only property I could use is the Message?

    If I would like to return a collection of "property name" / "error", should I serialize it inside the exception message?

     

    Thanks a lot


    Regards,

    Jorge Fioranelli
    http://blog.jorgef.net
    Thursday, December 16, 2010 8:35 PM
  • Hi All

     

    I  have excatly this issue, I would like to return my own extra fields, has anyone got an answer to this ?

     

    Regards

    Paul

    Thursday, May 12, 2011 12:57 PM
  • Hi,

    Currently only the Message field (along with the Type and StackTrace) are serialized. WCF Data Services doesn't support serializing more data into the error payload.

    Thanks,


    Vitek Karas [MSFT]
    Thursday, May 12, 2011 1:58 PM
    Moderator