EntityPropertyConverter::ConvertBack<T> intolerant to properties in dictionary not found as properties on T
See original GitHub issueThe EntityPropertyConverter::ConvertBack<T>
method is currently extremely fragile in terms of being able to deserialize a set of properties that does not exactly match the shape of the T
being specified. Consider the sample repro program below where I have a class Test
with some properties Id
and Nested
. If my properties dictionary contains a key other than those exact property names (i.e. SomePropIDontHave
), then the call to convert back will throw a completely nondescript NullReferenceException
. The NullReferenceException
is due to this code that just blindly expects to find the property on the T
and use it:
My initial expectation would be that, at the very least, a more specific exception would be thrown that tells me exactly which property couldn’t be deserialized rather than a NullReferenceException
. For example, given the code above, adding something like this:
if (propertyToGet == null)
{
throw new MissingPropertyException($"No property was found on type {parentProperty.GetType().FullName} that corresponds to {propertyPath}.");
}
My desire for this API though would be giving me a little more control by allowing me to tell it to “relax” and just deserialize what it can. Since ConvertBack
already takes an EntityPropertyConvertOptions
parameter in one of its overloads, this could be added to that options class in the form of a bool IgnoreMissingProperties
property that basically tells it: hey, if you can’t find a property on my T
that matches the key coming out of the dictionary, just go ahead and ignore it. That might then look something like this:
if (propertyToGet == null)
{
if (entityPropertyConverterOptions.IgnoreMissingProperties)
{
continue;
}
throw new MissingPropertyException($"No property was found on type {parentProperty.GetType().FullName} that corresponds to {propertyPath}.");
}
Sample Repro Code
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using System;
using System.Collections.Generic;
namespace EntityPropertyConverterTest
{
class Program
{
static void Main(string[] args)
{
var rawProperties = new Dictionary<string, EntityProperty>
{
["SomePropIDontHave"] = EntityProperty.GeneratePropertyForString("Hmmm"),
["Id"] = EntityProperty.GeneratePropertyForString("MyId123"),
["Nested_NestedId"] = EntityProperty.GeneratePropertyForString("MyNestedId456"),
};
// NOTE: this will throw a NullReferenceException unless you comment out the SomePropIDontHave entry in the dictionary initializer above
var test = TableEntity.ConvertBack<Test>(rawProperties, new OperationContext());
Console.WriteLine($"Id: {test.Id}");
Console.WriteLine($"NestedId: {test.Nested?.NestedId}");
Console.ReadKey();
}
public class Test
{
public string Id { get; set; }
public TestNested Nested { get; set; }
}
public class TestNested
{
public string NestedId { get; set; }
}
}
}
Issue Analytics
- State:
- Created 5 years ago
- Comments:7 (2 by maintainers)
Top GitHub Comments
@erezvani1529 can you explain what you mean? Since CosmosDB Table API is not a replacement for Azure Table Storage, why would development be frozen on new features?
@doguva-msft Is there anywhere else to get this fix? It is a problem for me since your code is great for not inheriting from ITableEntity but I cannot put a BsonId in my model and therefore EntityPropertyConverter crashes when it tries to deserialize it.
Hi, I am not from the azure team but I wrote the EntityPropertyConverter class. Your suggestion of leveraging EntityPropertyConverterOptions is right on target. The main reason I added that options class is to allow customizations in the api without changing any method signature and giving more control on the callers for the behaviour. Your proposed solution above looks good. Minor comments you may want to save parentProperty.GetType() to a variable not to call it second time for logging and in the log statement you may also want to log the specific property (properties[i]) that does not exist in T.
If azure team is happy to take this in you should submit a Pull request with your proposal.
Thanks Dogu