I have 2 classes: Item and ItemClass and Item is referenced as a Collection in ItemClass:
public class ItemClass : DbEntityInteger, IDbInternalcodeEntity, IDbNameEntity
{
public ItemClass()
{
Items = new List<Item>();
}
public virtual string InternalCode { get; set; }
//[System.Text.Json.Serialization.JsonIgnore]
public virtual IList<Item> Items { get; set; }
public virtual string? Name { get; set; }
}
I've developed a webapi including OData Query Language so i'd like to have this behavior: Items Property must be serialized only when i use the $expand command otherwise a request to the api that return Item Classes must have this property empty.
I tried to decore Items Property with System.Text.Json.Serialization.JsonIgnore attributre but obviously the property is no longer serialized, but, if i remove the attribute the Items Collection is alwayse serialized, even without the using of $expand.
2022-04-12 edit:
According to this example and this video I only added this code in my project:
Startup:
builder.Services.AddControllers().AddOData(options => options.Select().Filter().OrderBy().Expand().Count().SetMaxTop(50).SkipToken());
Controller:
Add [EnableQuery]
on method Get of my ItemClassController.
But this is not enough.
According to this other example, Controller must inherits from ODataController or implements [ODataAttributeRouting] and in Startup the EdmModel must be defined and declared in AddControllers().
Startup:
static IEdmModel GetEdmModel()
{
ODataConventionModelBuilder builder = new();
builder.EntitySet<ItemClass>("ItemClasses");
return builder.GetEdmModel();
}
[...]
builder.Services.AddControllers().AddOData(opt => opt.AddRouteComponents("odata", GetEdmModel())
.Select()
.Filter()
.OrderBy()
.Expand()
.Count()
.SetMaxTop(50)
.SkipToken()
);
ItemClassController:
public class ItemClassesController : ODataController
{
private readonly ILogger<ItemsController> _logger;
private readonly IItemManager _itemManager;
public ItemClassesController(ILogger<ItemsController> logger, IItemManager itemManager)
{
_logger = logger;
_itemManager = itemManager;
}
[EnableQuery(PageSize = 15)]
public ActionResult<IEnumerable<Test.Core.ItemClass>> Get()
{
IEnumerable<Test.Core.ItemClass> result;
result = _itemManager.ItemClassSessionMapper.Elements;
return new ActionResult<IEnumerable<Test.Core.ItemClass>>(result);
}
}
Now it works, subclasses are not expanded by default and response is encapsulated in odata context:
"@odata.context": "http://localhost:5253/odata/$metadata#ItemClasses",
"value": [
{
"InternalCode": "TST",
"Name": "Test Class",
"Id": 3,
"Version": 0,
"UniqueKey": "4b01b1a6-8042-44d2-9eb2-a1c9ef7f86cf",
"DateTimeCreated": "2022-04-12T08:48:33.8578697+02:00",
"DateTimeModified": null,
"UserCreated": null,
"UserModified": null,
"Note": null,
"DateTimeLoadedInSession": "2022-04-12T17:23:43.8772784+02:00"
}
]
}
Unfortunately expand works with single reference property but not with collections, I suppose could depend on this version of OData which is still under development.
This is part of the exception message I get when I try to expand Items Collection:
---> System.ArgumentException: Argument types do not match
at System.Linq.Expressions.Expression.Condition(Expression test, Expression ifTrue, Expression ifFalse)
at NHibernate.Linq.ReWriters.SubQueryConditionalExpander.SubQueryFromClauseExpander.VisitConditional(ConditionalExpression node)
2022-04-14 edit:
Solved!
The issue with $Expand "Argument types do not match" was due to this reason:
in ItemClassesController I returned ItemClass Elements as IEnumerable<Test.Core.ItemClass>
but, in ItemClass object the property Items was public virtual IList<Item> Items { get; set; }
and it seems that OData service is unable to cast form IList to IEnumerable.
Changing Get method resolves the issue:
[HttpGet]
[EnableQuery(PageSize = 100)]
public ActionResult<IList<Test.Core.ItemClass>> Get()
{
IList<Test.Core.ItemClass> result;
result = _itemManager.ItemClassSessionMapper.Elements.ToList();
return new ActionResult<IList<Test.Core.ItemClass>>(result);
}
This is not Nhibernate Layer fault because if I deactivate OData and return an IEnumerable<Test.Core.ItemClass>
, Items collection is correctly serialized.