internal static void ReadAndUpdateTableEntityWithEdmTypeResolver(ITableEntity entity, Dictionary <string, string> entityAttributes, EntityReadFlags flags, Func <string, string, string, string, EdmType> propertyResolver, OperationContext ctx) { Dictionary <string, EntityProperty> entityProperties = (flags & EntityReadFlags.Properties) > 0 ? new Dictionary <string, EntityProperty>() : null; Dictionary <string, EdmType> propertyResolverDictionary = null; // Try to add the dictionary to the cache only if it is not a DynamicTableEntity. If DisablePropertyResolverCache is true, then just use reflection and generate dictionaries for each entity. if (entity.GetType() != typeof(DynamicTableEntity)) { #if WINDOWS_DESKTOP && !WINDOWS_PHONE if (!TableEntity.DisablePropertyResolverCache) { propertyResolverDictionary = TableEntity.PropertyResolverCache.GetOrAdd(entity.GetType(), TableOperationHttpResponseParsers.CreatePropertyResolverDictionary); } else { Logger.LogVerbose(ctx, SR.PropertyResolverCacheDisabled); propertyResolverDictionary = TableOperationHttpResponseParsers.CreatePropertyResolverDictionary(entity.GetType()); } #else propertyResolverDictionary = TableOperationHttpResponseParsers.CreatePropertyResolverDictionary(entity.GetType()); #endif } if (flags > 0) { foreach (KeyValuePair <string, string> prop in entityAttributes) { if (prop.Key == TableConstants.PartitionKey) { entity.PartitionKey = (string)prop.Value; } else if (prop.Key == TableConstants.RowKey) { entity.RowKey = (string)prop.Value; } else if (prop.Key == TableConstants.Timestamp) { if ((flags & EntityReadFlags.Timestamp) == 0) { continue; } entity.Timestamp = DateTime.Parse(prop.Value, CultureInfo.InvariantCulture); } else if ((flags & EntityReadFlags.Properties) > 0) { if (propertyResolver != null) { Logger.LogVerbose(ctx, SR.UsingUserProvidedPropertyResolver); try { EdmType type = propertyResolver(entity.PartitionKey, entity.RowKey, prop.Key, prop.Value); Logger.LogVerbose(ctx, SR.AttemptedEdmTypeForTheProperty, prop.Key, type.GetType().ToString()); try { entityProperties.Add(prop.Key, EntityProperty.CreateEntityPropertyFromObject(prop.Value, type.GetType())); } catch (FormatException ex) { throw new StorageException(string.Format(CultureInfo.InvariantCulture, SR.FailParseProperty, prop.Key, prop.Value, type.ToString()), ex) { IsRetryable = false }; } } catch (StorageException) { throw; } catch (Exception ex) { throw new StorageException(SR.PropertyResolverThrewError, ex) { IsRetryable = false }; } } else if (entity.GetType() != typeof(DynamicTableEntity)) { EdmType edmType; Logger.LogVerbose(ctx, SR.UsingDefaultPropertyResolver); if (propertyResolverDictionary != null) { propertyResolverDictionary.TryGetValue(prop.Key, out edmType); Logger.LogVerbose(ctx, SR.AttemptedEdmTypeForTheProperty, prop.Key, edmType); entityProperties.Add(prop.Key, EntityProperty.CreateEntityPropertyFromObject(prop.Value, edmType)); } } else { Logger.LogVerbose(ctx, SR.NoPropertyResolverAvailable); entityProperties.Add(prop.Key, EntityProperty.CreateEntityPropertyFromObject(prop.Value, typeof(string))); } } } if ((flags & EntityReadFlags.Properties) > 0) { entity.ReadEntity(entityProperties, ctx); } } }
private static T ReadAndResolveWithEdmTypeResolver <T>(Dictionary <string, string> entityAttributes, Func <string, string, DateTimeOffset, IDictionary <string, EntityProperty>, string, T> resolver, Func <string, string, string, string, EdmType> propertyResolver, string etag, Type type, OperationContext ctx, bool disablePropertyResolverCache, TableRequestOptions options) { string pk = null; string rk = null; byte[] cek = null; EncryptionData encryptionData = null; DateTimeOffset ts = new DateTimeOffset(); Dictionary <string, EntityProperty> properties = new Dictionary <string, EntityProperty>(); Dictionary <string, EdmType> propertyResolverDictionary = null; HashSet <string> encryptedPropertyDetailsSet = null; if (type != null) { #if WINDOWS_DESKTOP && !WINDOWS_PHONE if (!disablePropertyResolverCache) { propertyResolverDictionary = TableEntity.PropertyResolverCache.GetOrAdd(type, TableOperationHttpResponseParsers.CreatePropertyResolverDictionary); } else { propertyResolverDictionary = TableOperationHttpResponseParsers.CreatePropertyResolverDictionary(type); } #else propertyResolverDictionary = TableOperationHttpResponseParsers.CreatePropertyResolverDictionary(type); #endif } // Decrypt the metadata property value to get the names of encrypted properties so that they can be parsed correctly below. bool isJavaV1 = false; if (options.EncryptionPolicy != null) { string metadataValue = null; string keyPropertyValue = null; if (entityAttributes.TryGetValue(Constants.EncryptionConstants.TableEncryptionPropertyDetails, out metadataValue) && entityAttributes.TryGetValue(Constants.EncryptionConstants.TableEncryptionKeyDetails, out keyPropertyValue)) { EntityProperty propertyDetailsProperty = EntityProperty.CreateEntityPropertyFromObject(metadataValue, EdmType.Binary); EntityProperty keyProperty = EntityProperty.CreateEntityPropertyFromObject(keyPropertyValue, EdmType.String); entityAttributes.TryGetValue(TableConstants.PartitionKey, out pk); entityAttributes.TryGetValue(TableConstants.RowKey, out rk); cek = options.EncryptionPolicy.DecryptMetadataAndReturnCEK(pk, rk, keyProperty, propertyDetailsProperty, out encryptionData, out isJavaV1); properties.Add(Constants.EncryptionConstants.TableEncryptionPropertyDetails, propertyDetailsProperty); byte[] binaryVal = propertyDetailsProperty.BinaryValue; encryptedPropertyDetailsSet = ParseEncryptedPropertyDetailsSet(isJavaV1, binaryVal); } else { if (options.RequireEncryption.HasValue && options.RequireEncryption.Value) { throw new StorageException(SR.EncryptionDataNotPresentError, null) { IsRetryable = false }; } } } foreach (KeyValuePair <string, string> prop in entityAttributes) { if (prop.Key == TableConstants.PartitionKey) { pk = (string)prop.Value; } else if (prop.Key == TableConstants.RowKey) { rk = (string)prop.Value; } else if (prop.Key == TableConstants.Timestamp) { ts = DateTimeOffset.Parse(prop.Value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); if (etag == null) { etag = GetETagFromTimestamp(prop.Value); } } else if (prop.Key == Constants.EncryptionConstants.TableEncryptionKeyDetails) { // This and the following check are required because in JSON no-metadata, the type information for the properties are not returned and users are // not expected to provide a type for them. So based on how the user defined property resolvers treat unknown properties, we might get unexpected results. properties.Add(prop.Key, EntityProperty.CreateEntityPropertyFromObject(prop.Value, EdmType.String)); } else if (prop.Key == Constants.EncryptionConstants.TableEncryptionPropertyDetails) { if (!properties.ContainsKey(Constants.EncryptionConstants.TableEncryptionPropertyDetails)) { // If encryption policy is not set, then add the value as-is to the dictionary. properties.Add(prop.Key, EntityProperty.CreateEntityPropertyFromObject(prop.Value, EdmType.Binary)); } else { // Do nothing. Already handled above. } } else { if (propertyResolver != null) { Logger.LogVerbose(ctx, SR.UsingUserProvidedPropertyResolver); try { EdmType edmType = propertyResolver(pk, rk, prop.Key, prop.Value); Logger.LogVerbose(ctx, SR.AttemptedEdmTypeForTheProperty, prop.Key, edmType); try { CreateEntityPropertyFromObject(properties, encryptedPropertyDetailsSet, prop, edmType); } catch (FormatException ex) { throw new StorageException(string.Format(CultureInfo.InvariantCulture, SR.FailParseProperty, prop.Key, prop.Value, edmType), ex) { IsRetryable = false }; } } catch (StorageException) { throw; } catch (Exception ex) { throw new StorageException(SR.PropertyResolverThrewError, ex) { IsRetryable = false }; } } else if (type != null) { Logger.LogVerbose(ctx, SR.UsingDefaultPropertyResolver); EdmType edmType; if (propertyResolverDictionary != null) { propertyResolverDictionary.TryGetValue(prop.Key, out edmType); Logger.LogVerbose(ctx, SR.AttemptedEdmTypeForTheProperty, prop.Key, edmType); CreateEntityPropertyFromObject(properties, encryptedPropertyDetailsSet, prop, edmType); } } else { Logger.LogVerbose(ctx, SR.NoPropertyResolverAvailable); CreateEntityPropertyFromObject(properties, encryptedPropertyDetailsSet, prop, EdmType.String); } } } // If encryption policy is set on options, try to decrypt the entity. if (options.EncryptionPolicy != null && encryptionData != null) { properties = options.EncryptionPolicy.DecryptEntity(properties, encryptedPropertyDetailsSet, pk, rk, cek, encryptionData, isJavaV1); } return(resolver(pk, rk, ts, properties, etag)); }