none
DataServiceContextEx_ReadingEntity changes not affecting underlying DataSet. Full code sample included

Answers

  • Hello Chris,

    I downloaded and debugged you project. When the following line in Default.aspx.cs page is called:

    this.messageList.DataSource = context.Messages;

    The actual raw xml data returned is:

    <entry m:etag="W/&quot;datetime'2011-05-13T15%3A41%3A28.847Z'&quot;" xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
      <id>http://127.0.0.1:10002/devstoreaccount1/Messages(PartitionKey='a',RowKey='12520969707112181959_81962fd7-db9c-452e-8a96-d3969fe3aeaf')</id>
      <title type="text"></title>
      <updated>2011-05-13T15:42:11Z</updated>
      <author>
        <name />
      </author>
      <link rel="edit" title="Messages" href="Messages(PartitionKey='a',RowKey='12520969707112181959_81962fd7-db9c-452e-8a96-d3969fe3aeaf')" />
      <category term="devstoreaccount1.Messages" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
      <content type="application/xml">
        <m:properties>
          <d:PartitionKey xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">a</d:PartitionKey>
          <d:RowKey xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">12520969707112181959_81962fd7-db9c-452e-8a96-d3969fe3aeaf</d:RowKey>
          <d:Timestamp m:type="Edm.DateTime" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">2011-05-13T15:41:28.847Z</d:Timestamp>
          <d:Body xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">Pd6x3xTfjBhZWApoYBeXxA==</d:Body>
          <d:Name xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">Anonymous</d:Name>
          <d:BodyCopy xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">hello</d:BodyCopy>
          <d:Seed m:null="true" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" />
        </m:properties>
      </content>
    </entry> 

    You can see that there is not a namespace of prefix "d" that in the root element. So for your case,  if (dNamespacePrefix != null) is not passing.

    You can either revise the code to deal with that case, or how about using reflection instead of directly processing to the raw xml data?

        void DataServiceContextEx_ReadingEntity(object sender, ReadingWritingEntityEventArgs e)
        {
            int KeyVersion = 0;
             
            foreach (PropertyInfo property in e.Entity.GetType().GetProperties())
            {
                object[] transparentEncryptAttributes = property.GetCustomAttributes(typeof(EncryptAttribute), false);
                if (transparentEncryptAttributes.Length > 0 && property.PropertyType == typeof(string))
                {
                    string propertyValue = (string)property.GetValue(e.Entity, null);
                    propertyValue = azCrypto.DecryptString(propertyValue, 0);
                    property.SetValue(e.Entity, propertyValue, null);
                }
            }
        }

    Thanks,


    Wengchao Zeng
    Please mark the replies as answers if they help or unmark if not.
    If you have any feedback about my replies, please contact msdnmg@microsoft.com.
    Microsoft One Code Framework
    • Marked as answer by ChrisLaMont Friday, May 13, 2011 7:22 PM
    Friday, May 13, 2011 4:18 PM

All replies

  • Have you verified with Fiddler and direct observation using, for example, Cloud Storage Studio that the entity was uploaded with the expected properties and values?
    Thursday, May 12, 2011 5:08 PM
  • I can see the encrypted values as expected when deployed to the cloud and checked the contents using https://www.myazurestorage.com 

     

    The only issue is that upon decryption, I don't see the decrypted value. I see gibberish.  However when I set a breakpoint I see that I'm decoding the content correctly.

    Thursday, May 12, 2011 5:41 PM
  • Here is the actual code that is not working.  See the section that says "decrypt this property"?  When I put a breakpoint there, the code does decrypt the date and it replaces the encrypted value with unencrypted value (for display purposes).

     

    However this change doesn't affect the dataset.  Can anyone explain why?

     

     

    public class MessageDataServiceContext  : TableServiceContext
      {
        public MessageDataServiceContext(string baseAddress, StorageCredentials credentials)
          : base(baseAddress, credentials)
        {
          this.WritingEntity += new EventHandler<ReadingWritingEntityEventArgs>(DataServiceContextEx_WritingEntity);
          this.ReadingEntity += new EventHandler<ReadingWritingEntityEventArgs>(DataServiceContextEx_ReadingEntity);
    
           this.azCrypto = new EncryptDecrypt.AzureTableCrypto(baseAddress, credentials);
    
          // Todo: CHANGE THIS: Replace this certificate hash with your certificate. You can use your SSL key for simplicity
           this.azCrypto.LoadCertificateByThumbprint("‎1e c3 26 00 da 34 05 c9 44 c7 75 18 84 c7 8b a7 e4 f4 ed 8b");
        }
    
        AzureTableCrypto azCrypto = null;
    
        void DataServiceContextEx_ReadingEntity(object sender, ReadingWritingEntityEventArgs e)
        {
          // Todo: Next Version: support more than one version of Key
          int KeyVersion = 0;
    
          // e.Data gives you the XElement for the Serialization of the Entity 
          //Using XLinq , you can add/Remove properties to the element Payload 
          XName xnEntityProperties = XName.Get("properties", e.Data.GetNamespaceOfPrefix("m").NamespaceName);
          XElement xePayload = null;
          foreach (PropertyInfo property in e.Entity.GetType().GetProperties())
          {
            object[] transparentEncryptAttributes = property.GetCustomAttributes(typeof(EncryptAttribute), false);
            if (transparentEncryptAttributes.Length > 0)
            {
              if (xePayload == null)
              {
                // Null exception happens here
                xePayload = e.Data.Descendants().Where<XElement>(xe => xe.Name == xnEntityProperties).First<XElement>();
              }
              // Try to get the namespace, may be null if ATOM format
              var dNamespacePrefix = e.Data.GetNamespaceOfPrefix("d");
              if (dNamespacePrefix != null)
              {
                if (!String.IsNullOrEmpty(dNamespacePrefix.NamespaceName))
                {
                  //The XName of the property we are going to encrypt from the payload
                  XName xnProperty = XName.Get(property.Name, dNamespacePrefix.NamespaceName);
                  //Get the Property of the entity you want to encrypt on the server
                  XElement xeDecryptThisProperty = xePayload
                          .Descendants()
                          .Where<XElement>(xe => xe.Name == xnProperty)
                          .First<XElement>();
    
                  //Decrypt this property 
                  xeDecryptThisProperty.Value = azCrypto.DecryptString(xeDecryptThisProperty.Value,0);
                }
              }
              else
    	        {
                // The object didn't have the encrypt property set
    	        }
            }
          }
    
    <span style="font-family:Consolas,'Courier New',Courier,monospace; font-size:x-small">--}</span>
    

     

    Friday, May 13, 2011 3:29 PM
  • Hello Chris,

    I downloaded and debugged you project. When the following line in Default.aspx.cs page is called:

    this.messageList.DataSource = context.Messages;

    The actual raw xml data returned is:

    <entry m:etag="W/&quot;datetime'2011-05-13T15%3A41%3A28.847Z'&quot;" xmlns="http://www.w3.org/2005/Atom" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
      <id>http://127.0.0.1:10002/devstoreaccount1/Messages(PartitionKey='a',RowKey='12520969707112181959_81962fd7-db9c-452e-8a96-d3969fe3aeaf')</id>
      <title type="text"></title>
      <updated>2011-05-13T15:42:11Z</updated>
      <author>
        <name />
      </author>
      <link rel="edit" title="Messages" href="Messages(PartitionKey='a',RowKey='12520969707112181959_81962fd7-db9c-452e-8a96-d3969fe3aeaf')" />
      <category term="devstoreaccount1.Messages" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
      <content type="application/xml">
        <m:properties>
          <d:PartitionKey xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">a</d:PartitionKey>
          <d:RowKey xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">12520969707112181959_81962fd7-db9c-452e-8a96-d3969fe3aeaf</d:RowKey>
          <d:Timestamp m:type="Edm.DateTime" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">2011-05-13T15:41:28.847Z</d:Timestamp>
          <d:Body xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">Pd6x3xTfjBhZWApoYBeXxA==</d:Body>
          <d:Name xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">Anonymous</d:Name>
          <d:BodyCopy xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices">hello</d:BodyCopy>
          <d:Seed m:null="true" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" />
        </m:properties>
      </content>
    </entry> 

    You can see that there is not a namespace of prefix "d" that in the root element. So for your case,  if (dNamespacePrefix != null) is not passing.

    You can either revise the code to deal with that case, or how about using reflection instead of directly processing to the raw xml data?

        void DataServiceContextEx_ReadingEntity(object sender, ReadingWritingEntityEventArgs e)
        {
            int KeyVersion = 0;
             
            foreach (PropertyInfo property in e.Entity.GetType().GetProperties())
            {
                object[] transparentEncryptAttributes = property.GetCustomAttributes(typeof(EncryptAttribute), false);
                if (transparentEncryptAttributes.Length > 0 && property.PropertyType == typeof(string))
                {
                    string propertyValue = (string)property.GetValue(e.Entity, null);
                    propertyValue = azCrypto.DecryptString(propertyValue, 0);
                    property.SetValue(e.Entity, propertyValue, null);
                }
            }
        }

    Thanks,


    Wengchao Zeng
    Please mark the replies as answers if they help or unmark if not.
    If you have any feedback about my replies, please contact msdnmg@microsoft.com.
    Microsoft One Code Framework
    • Marked as answer by ChrisLaMont Friday, May 13, 2011 7:22 PM
    Friday, May 13, 2011 4:18 PM
  • However this change doesn't affect the dataset.  Can anyone explain why?

     

    public class MessageDataServiceContext : TableServiceContext
     {
    
    
     ...
    
      if (!String.IsNullOrEmpty(dNamespacePrefix.NamespaceName))
      {
    
      ...
      //Decrypt this property 
      xeDecryptThisProperty.Value = azCrypto.DecryptString(xeDecryptThisProperty.Value,0);
      }
      }
     }
    


    The ReadingEntity event "occurs after entity data has been completely read into the entity object" (from http://msdn.microsoft.com/en-us/library/system.data.services.client.dataservicecontext.readingentity.aspx).

    In your code, you are processing the XML input and writing the decrypted value back into the XML. You should write the decrypted value to the entity in e.Data instead.

    UPDATED: I meant "You should write the decrypted value to the entity in e.Entity instead".

    • Edited by Fernando Tubio Friday, May 13, 2011 6:07 PM Updated property name
    Friday, May 13, 2011 4:48 PM
  • Looks like you may be on to something with e.Entity.  How should I write the value?  It's an object, and needs to be converted somehow.

    Once I cast the object I probably need to reflect and get the dynamic property to be decrypted.

     

    Any tips would be welcome....

    Friday, May 13, 2011 7:11 PM
  • Chris -

    In the blog post I referred to above I have some sample code where I accessed the hydrated object and modified a property.

    Friday, May 13, 2011 7:19 PM
  • I hadn't seen Wenchao's reply when I wrote mine, but it looks like the code he wrote might do the trick. Did you try it?
    Friday, May 13, 2011 7:23 PM
  • Wenchao Zeng - Thank you so much for your assistance!  I just checked in the working version thanks to you!  (I credited you in code)
    Friday, May 13, 2011 7:23 PM