Investigating OData's internals
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 505
- Forks
- 186
- PR merge metrics
- No merged PRs in 30d
Description
Hey guys,
I'm often in a situation where I need things resolved quickly.
To this end I'm trying to learn more about the inner workings of the OData framework in the hopes that one day I might feel confident to submit the odd pull request.
I've hit a bit of a block in my understanding of the process involved here and was wondering if I could get a hint or two to help me resolve the block.
My original problem started here (https://github.com/OData/AspNetCoreOData/issues/248).
I have since pulled down the OData code from here and added it to my solution in order to investigate the problem by stepping through it in my solution.
To cut a long story short here I post the App objects without the Roles as a single array post to a custom action with an OData query asking ?$expand=Item($expand=Roles) so i'm hoping to get all this back and that's not happening.
So my response object is an IQueryable<Result<App>> that looks like this (I've omitted other properties not relevant to the problem) ...
var result = new []
{
new Result<App> {
Success = "True",
Message = "Created Successfully",
Item = new App {
Id = 1,
DefaultCultureId = "",
Name = "TestApp1",
Domain = "Domain1",
DefaultTheme = "Default",
Roles: [
new Role { Id = Guid.NewGuid(), Name = "Administrators" },
new Role { Id = Guid.NewGuid(), Name = "Users" },
new Role { Id = Guid.NewGuid(), Name = "Guests" }
]
}
},
new Result<App> {
Success = "True",
Message = "Created Successfully",
Item = new App {
Id = 2,
DefaultCultureId = "",
Name = "TestApp2",
Domain = "Domain2",
DefaultTheme = "Default",
Roles: [
new Role { Id = Guid.NewGuid(), Name = "Administrators" },
new Role { Id = Guid.NewGuid(), Name = "Users" },
new Role { Id = Guid.NewGuid(), Name = "Guests" }
]
}
}
};
Digging through the execution as it's running reveals that the EnableAttribute when applied to my Iqueryable returns an expression tree that looks like this ...
System.Collections.Generic.List`1[Core.Objects.Result`1[Core.Objects.Entities.CMS.App]]
.Select($it =>
new SelectAllAndExpand`1() {
Model = value(Microsoft.AspNetCore.OData.Query.Container.LinqParameterContainer+TypedLinqParameterContainer`1[Microsoft.OData.Edm.IEdmModel]).TypedProperty,
Instance = $it,
UseInstanceForProperties = True,
Container = new SingleExpandedProperty`1()
{
Name = "Item",
Value = new SelectAllAndExpand`1()
{
Model = value(Microsoft.AspNetCore.OData.Query.Container.LinqParameterContainer+TypedLinqParameterContainer`1[Microsoft.OData.Edm.IEdmModel]).TypedProperty,
Instance = IIF(($it == null), null, $it.Item),
UseInstanceForProperties = True,
Container = new NamedProperty`1()
{
Name = "Roles",
Value = IIF(
(IIF((IIF(($it == null), null, $it.Item) == null), null, IIF(($it == null), null, $it.Item).Roles) == null), null, IIF((IIF(($it == null), null, $it.Item) == null), null, IIF(($it == null), null, $it.Item).Roles)
.Select($it => new SelectAll`1()
{
Model = value(Microsoft.AspNetCore.OData.Query.Container.LinqParameterContainer+TypedLinqParameterContainer`1[Microsoft.OData.Edm.IEdmModel]).TypedProperty,
Instance = $it,
UseInstanceForProperties = True
})
)
}
},
IsNull = (IIF(($it == null), null, $it.Item) == null)
}
}
)
My confusion here is why did this not get translated in to something like this ...
result.Select(i => new {
// all scalar props
// odata props if any like etags or meta links
Roles = i.Roles?.Select(j => {
// all scalar props
// odata props if any like etags or meta links
})
})
Instead it seems that these wrappers have been introduced.
Would a direct LINQ expression tree not make the whole process easier to maintain and then from a serialization point of view we could simply do ...
await repsonse.WriteAsync(result.ToJson(serializationOptions));
I have most likely missed something here that the framework is doing but I figured I would ask as this seems like the next logical improvement to simplify the codebase in my understanding right now.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with issue #248, then trace the EnableAttribute path that produces the SelectAllAndExpand and NamedProperty expression wrappers. Compare that behavior with the proposed direct LINQ shape and review the surrounding OData serialization flow; done would require a decided, documented scope for any internal simplification.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 15/100