使用Rest API在Dynamics 365 Business Central创建Item遇400错误求助
The error you're hitting stems from how Dynamics Business Central's OData API handles related entities like baseUnitOfMeasure and itemCategory—these are "non-open types" that won’t accept full nested objects. Instead, you need to use bound navigation property references to link to existing records in BC. Here’s how to fix this:
Root Cause
When you include the full baseUnitOfMeasure or itemCategory objects in your request body, the API interprets this as an attempt to pass an untyped value (or create a new related entity) rather than linking to an existing one. For pre-existing records, you must use the @odata.bind syntax to reference them directly.
Corrected Request Body
Replace your nested objects with @odata.bind pointers to the existing BC records. Here’s the adjusted request body:
{ "number": "1896-S", "displayName": "ATHENS Desk", "type": "Inventory", "blocked": false, "baseUnitOfMeasure@odata.bind": "/unitOfMeasures('PCS')", "gtin": "", "itemCategory@odata.bind": "/itemCategories('TABLE')", "inventory": 0, "unitPrice": 1000.8, "priceIncludesTax": false, "unitCost": 780.7, "taxGroupCode": "FURNITURE" }
Note: If constructing this in C#, make sure to escape single quotes inside the OData path (use \' or wrap the path in double quotes).
Adjust Your C# Code
Update your itemBodyValues to use these @odata.bind properties instead of nested objects. Here are two common approaches:
1. Using an Anonymous Object
var itemBodyValues = new { number = "1896-S", displayName = "ATHENS Desk", type = "Inventory", blocked = false, baseUnitOfMeasure@odata.bind = "/unitOfMeasures('PCS')", gtin = "", itemCategory@odata.bind = "/itemCategories('TABLE')", inventory = 0, unitPrice = 1000.8, priceIncludesTax = false, unitCost = 780.7, taxGroupCode = "FURNITURE" }; string requestBody = JsonConvert.SerializeObject(itemBodyValues);
2. Using a Strongly-Typed Class
If you prefer a strongly-typed model, use JsonProperty attributes to map the bind properties correctly:
public class ItemRequest { public string number { get; set; } public string displayName { get; set; } public string type { get; set; } public bool blocked { get; set; } public string gtin { get; set; } public int inventory { get; set; } public double unitPrice { get; set; } public bool priceIncludesTax { get; set; } public double unitCost { get; set; } public string taxGroupCode { get; set; } [JsonProperty("baseUnitOfMeasure@odata.bind")] public string BaseUnitOfMeasureBind { get; set; } [JsonProperty("itemCategory@odata.bind")] public string ItemCategoryBind { get; set; } } // Usage var itemBodyValues = new ItemRequest { number = "1896-S", displayName = "ATHENS Desk", type = "Inventory", blocked = false, BaseUnitOfMeasureBind = "/unitOfMeasures('PCS')", ItemCategoryBind = "/itemCategories('TABLE')", inventory = 0, unitPrice = 1000.8, priceIncludesTax = false, unitCost = 780.7, taxGroupCode = "FURNITURE" }; string requestBody = JsonConvert.SerializeObject(itemBodyValues);
Why This Works
The @odata.bind syntax explicitly tells the BC API to link your new item to the existing unit of measure and item category records, rather than trying to process an unstructured object or create duplicate entities. This matches how BC’s OData API handles related entities for non-open types.
内容的提问来源于stack exchange,提问作者Muhammad Murad Haider

