-
Notifications
You must be signed in to change notification settings - Fork 178
Description
Assemblies affected
Microsoft.AspNetCore.OData 9.4.0
Describe the bug
Slightly related to #1325
We are using OData, together with CosmosDB to retrieve documents. These documents are free-form, so we use an Open Type.
We do this by compiling the OData query with this package (this repo) and then passing the generated Expression to the .Where()
method of the CosmosDB FeedIterator.
The query we are using is:
unknownPropert/subProperty eq 'Some text'
We expect this to translate in some expression like x["unknownProperty"]["subProperty"] == "Some text"
however it generates an increadibly complex query:
Generated expression tree
.Call Queryable.Where(
.Constant<EnumerableQuery`1[MyModel]>(List`1[MyModel]),
'(.Lambda #Lambda1<Func`2[MyModel,Boolean]>))
.Lambda #Lambda1<Func`2[MyModel,Boolean]>(MyModel $$it) {
(String).If (.Call (.Call (.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
}).GetType()).GetProperty("jobTitle") != null) {
.Call (.Call (.Call (.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
}).GetType()).GetProperty("jobTitle")).GetValue(.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
})
} .Else {
.If (
.Call ((IDictionary`2[String,Object]).Call (.Call QueryBinder.GetDynamicPropertyContainer(
.If (.Call Edm.EdmClrTypeMapExtensions.GetEdmTypeReference(
.Constant<Microsoft.OData.Edm.IEdmModel>(Microsoft.OData.Edm.EdmModel),
.Call (.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
}).GetType()) == null) {
.Throw .New ODataException(.Call String.Format(
"Cannot find the resource type '{0}' in the model.",
(.Call (.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
}).GetType()).FullName))
} .Else {
.Call Microsoft.AspNetCore.OData.Edm.EdmClrTypeMapExtensions.GetEdmTypeReference(
.Constant<Microsoft.OData.Edm.IEdmModel>(Microsoft.OData.Edm.EdmModel),
.Call (.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
}).GetType())
},
.Constant<Microsoft.OData.UriParser.QueryNodeKind>(SingleValueOpenPropertyAccess),
.Constant<Microsoft.OData.Edm.IEdmModel>(Microsoft.OData.Edm.EdmModel))).GetValue(.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
})).ContainsKey("jobTitle")
) {
((IDictionary`2[String,Object]).Call (.Call QueryBinder.GetDynamicPropertyContainer(
.If (.Call Microsoft.AspNetCore.OData.Edm.EdmClrTypeMapExtensions.GetEdmTypeReference(
.Constant<Microsoft.OData.Edm.IEdmModel>(Microsoft.OData.Edm.EdmModel),
.Call (.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
}).GetType()) == null) {
.Throw .New ODataException(.Call String.Format(
"Cannot find the resource type '{0}' in the model.",
(.Call (.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
}).GetType()).FullName))
} .Else {
.Call Edm.EdmClrTypeMapExtensions.GetEdmTypeReference(
.Constant<IEdmModel>(Microsoft.OData.Edm.EdmModel),
.Call (.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
}).GetType())
},
.Constant<Microsoft.OData.UriParser.QueryNodeKind>(SingleValueOpenPropertyAccess),
.Constant<Microsoft.OData.Edm.IEdmModel>(Microsoft.OData.Edm.EdmModel))).GetValue(.If (
.Call ($$it.OpenProperties).ContainsKey("en")
) {
($$it.OpenProperties).Item["en"]
} .Else {
null
})).Item["jobTitle"]
} .Else {
null
}
} == .Constant<LinqParameterContainer+TypedLinqParameterContainer`1[String]>(LinqParameterContainer+TypedLinqParameterContainer`1[String]).TypedProperty
}
Please let me know if this is expected / unexpected / or if it's the scope of this project.
If there is a better way of using odata with CosmosDB, I would also like to try it and find out what works or doesn't.
Reproduce steps
I created a repository: https://github.com/Tvde1/ODataOpenTypeRepro/tree/complex-query
Check out the branch complex-query. It has a Program.cs which can be ran without side effects.
Data Model
[DataContract]
public class MyModel : Dictionary<string, object>
{
[Key]
[DataMember(Name = "id")]
[JsonPropertyName("id")]
public int Id { get; set; }
[DataMember(Name = "knownString")]
[JsonPropertyName("knownString")]
public string KnownString { get; set; } = null!;
[Required]
[DataMember(Name = "knownInt")]
[JsonPropertyName("knownInt")]
public int KnownInt { get; set; }
[DataMember(Name = "knownComplexTypeArray")]
[JsonPropertyName("knownComplexTypeArray")]
public KnownComplexType[] KnownComplexTypeArray { get; set; } = null!;
[JsonExtensionData]
public Dictionary<string, object> OpenProperties => this;
}
[DataContract]
public class KnownComplexType
{
[DataMember(Name = "fileName")]
public string FileName { get; set; } = null!;
}
EDM (CSDL) Model
It's auto generated, but can supply if needed
Request/Response
See above
Expected behavior
I expect a query which is relatively simple and can be fed to CosmosDB along the lines of x["unknownProperty"]["subProperty"] == "Some text"
without too many checks and exceptions