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));
        }