NullReference exception generated when using FilterClause.ApplyTo method with open properties in the filter query
vibatra opened this issue · comments
Assemblies affected
ASP.NET Core OData 8.2.5
Describe the bug
FilterClause.ApplyTo method generates a NullReferenceException if a open/dynamic property is used in the filter query. The exception happens in QueryBinder.BindPropertyAccessExpression method.
Reproduce steps
// Get local copy of graph model metadata for generating the EDM model
var xmlReader = XmlReader.Create(new StringReader(GraphMetadataForValidation.GraphMetadata));
var EdmModel = CsdlReader.Parse(xmlReader);
// Set annotations to bind the User entity in EDM model to the User class in the SDK
EdmModel.SetAnnotationValue<ClrTypeAnnotation>(EdmModel.FindType("microsoft.graph.user"), new ClrTypeAnnotation(typeof(Graph.User)));
// Generate query option for FilterClause
IDictionary<string, string> queryOptions = new Dictionary<string, string> { { "$filter", "extension_123456_attr1 eq 'Test'"} };
ODataQueryContext ctx2 = new ODataQueryContext(EdmModel, typeof(Graph.User), new ODataPath())
{
DefaultQueryConfigurations = { EnableFilter = true },
};
var parser = new ODataQueryOptionParser(
EdmModel,
ctx2.ElementType,
ctx2.NavigationSource,
queryOptions);
parser.Resolver = new ODataUriResolver { EnableCaseInsensitive = true };
var filterQueryOption = new FilterQueryOption("extension_123456_attr1 eq 'Test'", ctx2, parser);
var results = filterQueryOption.ApplyTo(users.AsQueryable(), new ODataQuerySettings());
Data Model
The User class in Graph SDK but it can be any othr
EDM (CSDL) Model
https://[graph.microsoft.com/v1.0/$metadata#users](https://graph.microsoft.com/v1.0/$metadata#users)
Request/Response
No request/response. We are using it as stand alone.
Expected behavior
A filtered data set is returned.
Actual behavior
A NullReference exception is thrown when the ApplyTo method is called as it is not able to bind to the dynamic/additional data container on the CLR type.
Additional context
There is no way for us inject a custom implementation of FilterBinder in the stand alone mode.
We certainly shouldn't be throwing a NullReferenceException
. Let me look more using your repro steps.
We certainly shouldn't be throwing a
NullReferenceException
. Let me look more using your repro steps.
Below is a version of the repro steps without depending on the SDK and Graph metadata. Result is same - NRE thrown when ApplyTo is called.
string edmMetadata = @"
<edmx:Edmx xmlns:edmx=""http://docs.oasis-open.org/odata/ns/edmx"" Version=""4.0"">
<edmx:DataServices>
<Schema xmlns=""http://docs.oasis-open.org/odata/ns/edm"" Namespace=""testSpace.filterClause"">
<EntityType Name=""customer"" OpenType=""true"">
<Key>
<PropertyRef Name=""id""/>
</Key>
<Property Name=""dateTimeOffsetProperty"" Type=""Edm.DateTimeOffset""/>
<Property Name=""id"" Type=""Edm.String"" Nullable=""false""/>
<Property Name=""stringProperty"" Type=""Edm.String""/>
<Property Name=""booleanProperty"" Type=""Edm.Boolean""/>
</EntityType>
</Schema>
</edmx:DataServices>
</edmx:Edmx>";
// Generate model from the metadata
var xmlReader = XmlReader.Create(new StringReader(edmMetadata));
var EdmModel = CsdlReader.Parse(xmlReader);
// Set annotations to bind the User entity in EDM model to the User class in the SDK
EdmModel.SetAnnotationValue(EdmModel.FindType("testSpace.filterClause.customer"), new ClrTypeAnnotation(typeof(Customer)));
// Generate query option for FilterClause
IDictionary<string, string> queryOptions = new Dictionary<string, string> { { "$filter", "extension_123456_attr1 eq 'Test'" } };
ODataQueryContext ctx2 = new ODataQueryContext(EdmModel, typeof(Customer), new ODataPath())
{
DefaultQueryConfigurations = { EnableFilter = true },
};
var parser = new ODataQueryOptionParser(
EdmModel,
ctx2.ElementType,
ctx2.NavigationSource,
queryOptions);
parser.Resolver = new ODataUriResolver { EnableCaseInsensitive = true };
var filterQueryOption = new FilterQueryOption("extension_123456_attr1 eq 'Test'", ctx2, parser);
List<Customer> customers = new List<Customer>() { new Customer() { Id = "test1", StringProperty = "string", BooleanProperty = true, DateTimeOffsetProperty = DateTimeOffset.UtcNow, ExtensionData = new Dictionary<string, object>() } };
var results = filterQueryOption.ApplyTo(customers.AsQueryable(), new ODataQuerySettings());
The CLR model -
public class Customer
{
public string Id { get; set; }
public string StringProperty { get; set; }
public bool BooleanProperty { get; set; }
public DateTimeOffset DateTimeOffsetProperty { get; set; }
public IDictionary<string, object> ExtensionData { get; set; }
}