Open AndriiLesiuk opened 1 year ago
@AndriiLesiuk Can it work if you change 'Patch([FromBody] Delta< Dictionary > delta)' to 'Patch([FromBody] DeltaSet< Dictionary > delta)'?
Patch with Delta< T > should contain the key to update a certain entity, and the payload is an updated entity value and the request Uri should contain the key segment.
@xuzhg Thanks! It really worked. Are there any informative articles on how to implement this functionality according to the OData documentation? Do I need to do any additional configuration when working with delta to get this kind of response, or is it already out of the box?
{
"@context":"http://host/service/$metadata#Customers/$delta",
"@count":5,
"value":
[
{
"@id":"Customers('BOTTM')",
"ContactName":"Susan Halvenstern"
},
{
"@context":"#Customers/$deletedLink",
"source":"Customers('ALFKI')",
"relationship":"Orders",
"target":"Orders(10643)"
},
{
"@context":"#Customers/$link",
"source":"Customers('BOTTM')",
"relationship":"Orders",
"target":"Orders(10645)"
},
{
"@context":"#Orders/$entity",
"@id":"Orders(10643)",
"ShippingAddress":{
"Street":"23 Tsawassen Blvd.",
"City":"Tsawassen",
"Region":"BC",
"PostalCode":"T2F 8M4"
},
},
{
"@context":"#Customers/$deletedEntity",
"@removed": {
"reason":"deleted"
},
"@id":"Customers('ANTON')"
}
],
"@deltaLink": "Customers?$expand=Orders&$deltatoken=8015"
}
@AndriiLesiuk It seems you are looking for this fix: https://github.com/OData/AspNetCoreOData/pull/915.
If yes, It's included in 8.2.0.
I looked at the information you shared for me above. Therefore, I will try to describe in more detail. 1. I try to repeat the logic written in your unit tests, but for some reason I get an error: link
"A resource of type 'Edm.Untyped' was found in a resource set that otherwise has entries of type 'PostgresCRUDRelational.Controllers.Employee'. In OData, all entries in a resource set must have a common base type."
Request PATCH url: https://localhost:44394/odata/Employees Request body:
{
"@context": "http://host/service/$metadata#Employees/$delta",
"value": [
{
"ID": 1,
"Name": "Employee1",
"Friends@delta": [
{
"Id": 1,
"Name": "Friend1",
"Orders@delta": [
{
"Id": 1,
"Price": 10
},
{
"Id": 2,
"Price": 20
}
]
},
{
"Id": 2,
"Name": "Friend2"
}
]
},
{
"ID": 2,
"Name": "Employee2",
"Friends@delta": [
{
"Id": 3,
"Name": "Friend3",
"Orders@delta": [
{
"Id": 3,
"Price": 30
},
{
"Id": 4,
"Price": 40
}
]
},
{
"Id": 4,
"Name": "Friend4"
}
]
}
]
}
Class:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Deltas;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using System.ComponentModel.DataAnnotations;
namespace PostgresCRUDRelational.Controllers
{
public class Employee
{
[Key]
public int ID { get; set; }
public String Name { get; set; }
public List<Friend> Friends { get; set; }
}
public class Friend
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
[Key]
public int Id { get; set; }
public int Price { get; set; }
}
public class EmployeesController : ODataController
{
public EmployeesController()
{
if (null == Employees)
{
InitEmployees();
}
}
/// <summary>
/// static so that the data is shared among requests.
/// </summary>
public static IList<Employee> Employees = null;
private List<Friend> Friends = null;
private void InitEmployees()
{
Friends = new List<Friend>
{
new Friend
{
Id = 1,
Name = "Test0"
},
new Friend
{
Id = 2,
Name = "Test1",
Orders = new List<Order>()
{
new Order
{
Id = 1,
Price = 2
}
}
},
new Friend
{
Id = 3,
Name = "Test3"
},
new Friend
{
Id = 4,
Name = "Test4"
}
};
Employees = new List<Employee>
{
new Employee()
{
ID=1,
Name="Name1",
Friends = this.Friends.Where(x=>x.Id ==1 || x.Id==2).ToList()
},
new Employee()
{
ID=2,Name="Name2",
Friends = this.Friends.Where(x=>x.Id ==3 || x.Id==4).ToList()
},
new Employee()
{
ID=3,
Name="Name3"
},
};
}
[HttpPatch]
public IActionResult PatchEmployees([FromBody] DeltaSet<Employee> coll)
{
return Ok(coll);
}
}
}
Edm part:
var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Employee>("Employees");
modelBuilder.EntitySet<Friend>("Friends");
modelBuilder.EntitySet<Order>("Orders");
modelBuilder.Namespace = typeof(Employee).Namespace;
modelBuilder.MaxDataServiceVersion = EdmConstants.EdmVersion401;
modelBuilder.DataServiceVersion = EdmConstants.EdmVersion401;
var batchHandler = new DefaultODataBatchHandler();
batchHandler.MessageQuotas.MaxNestingDepth = 2;
batchHandler.MessageQuotas.MaxOperationsPerChangeset = 10;
batchHandler.MessageQuotas.MaxReceivedMessageSize = 100;
Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration).CreateLogger();
builder.WebHost.UseSerilog();
builder.Services.AddControllers().AddOData(opt =>
{
opt.EnableQueryFeatures(3000);
opt.AddRouteComponents(routePrefix: "odata", model: modelBuilder.GetEdmModel(), services => services.AddSingleton<ISearchBinder, SearchBinder>());
opt.RouteOptions.EnableNonParenthesisForEmptyParameterFunction = true;
opt.EnableAttributeRouting = true;
});
I am interested in whether I need to write some mechanism myself that will look inside this object, and depending on what is there, execute a command to the database:
[HttpPatch]
public IActionResult PatchEmployees([FromBody] DeltaSet<Employee> coll)
{
//for exmpl:
foreach (var op in coll)
{
service.PartialUpdate(op);
}
return Ok(coll);
}
, or is this process somehow automated? Thanks.
For more information, check out the docs at: https://learn.microsoft.com/en-us/odata/webapi-8/fundamentals/entityset-routing?tabs=net60%2Cvisual-studio#patching-a-collection-of-entities
1) @xuzhg -- the initial issue was that the controller method had the wrong signature; a patch to a collection should take a deltaset
For more information, check out the docs at: https://learn.microsoft.com/en-us/odata/webapi-8/fundamentals/entityset-routing?tabs=net60%2Cvisual-studio#patching-a-collection-of-entities
Okay, I realized that I need to implement this myself, but it's good that there is an example of how to do it better. Thanks. The second question seems to me to be closed for now. But I still don't understand why the error occurs.
@xuzhg , @habbes Sorry to bother you, but could you clarify why this kind of error might occur?
"A resource of type 'Edm.Untyped' was found in a resource set that otherwise has entries of type 'PostgresCRUDRelational.Controllers.Employee'. In OData, all entries in a resource set must have a common base type."
https://github.com/OData/AspNetCoreOData/issues/942#issuecomment-1568043328
Thanks
@AndriiLesiuk I think the changes in this PR: https://github.com/OData/AspNetCoreOData/pull/915 were not included in the 8.2.0 release. Are you in a position to try out with the changes in the main branch as we plan on another release.
@ElizabethOkerio, ok, thanks, will try.
@AndriiLesiuk The other question on whether you have to write your own logic for applying the DeltaSet
to the collection; In 8.x you have to write your own logic but in v7.x we added the ODataApiHandlers that apply the DeltaSet
for you. We haven't ported this to 8.x as we need to get customers' feedback on this approach and whether it is something that will be beneficial to them. You can also look at this doc on what these ODataApiHandlers are and how they can be used. https://devblogs.microsoft.com/odata/bulk-operations-support-in-odata-web-api/. We will appreciate any feedback on this. Thanks.
@AndriiLesiuk The other question on whether you have to write your own logic for traversing the
DeltaSet
, In 8.x you have to write your own logic but in v7.x we added the ODataApiHandlers that traverses theDeltaSet
for you. We haven't ported this to 8.x as we need to get customers' feedback on this approach and whether it is something that will be beneficial to them. You can also look at this doc on what these ODataApiHandlers are and how they can be used. https://devblogs.microsoft.com/odata/bulk-operations-support-in-odata-web-api/. We will appreciate any feedback on this. Thanks.
@ElizabethOkerio Thanks, but I already implemented my own logic for this functionality.
@AndriiLesiuk The fix for this issue was released in v8.2.1. Could you try out and let's know if it works for you.
Hi Microsoft.AspNetCore.OData 8.2.0 .NET 7.0
I'm trying to configure $delta functionality so that I can handle queries like this: PATCH: https://localhost:44394/odata/Dictionary Body:
My controller:
Program class OData configuration part:
Model:
When I send the request, the delta object in the controller returns null to me. But when I comment out this line of code in the Program file:
//modelBuilder.EntitySet<Dictionary>("Dictionary").HasCountRestrictions().IsCountable(true);
delta starts to contain the value I sent. Please tell me what I'm doing wrong? And are there any examples of how to correctly implement the $delta functionality as described in the official OData documentation? Any advice would be greatly appreciated.