Announcements
Hi Guys,
I have an Odata entity on which I have exposed a method. I want this method to take more than a single input and in particular, I want to be able to pass a collection/list of records as one of the inputs. Eventually I want to pass VendInvoiceInfoTable as one input and another input as a list of records of VendInvoiceInfoLine table. The output from this method will just be a string.
But before doing that I wanted to get the below code to work. It takes just one input ; list of Records of resource Fiscal Calendar. My understanding is that, 'Record' can refer to a resource that can be any table or data entity. I assume this also helps in parsing the incoming json into the respective object without the need of a contract class as in the case of a custom json not adhering to any table or entity structure.
I was successful in getting 'Types::String' to work but when I can make the Type a 'Record' with a resource name it fails regardless of the resource name and the respective input in postman.
[
SysOdataAction("Hello",false),
SysODataCollectionAttribute('name', Types::Record, "FiscalCalendar")
]
Public Static str Hello(List name)
{
return "good";
}
PostMan Request: Passing just 1 record with the field names of the 'FiscalCalendar' table which are 'CalendarId' and 'Description' . Both are of String types
{
"name": [
{
"CalendarId": "test",
"Description": "test"
}
]
}
Response:
{
"Message": "No HTTP resource was found that matches the request URI 'usnconeboxax1aos.cloud.onebox.dynamics.com/.../Microsoft.Dynamics.DataEntities.Hello'. No route data was found for this request."
}
I have already looked at these 2 forum discussions but unable to get it to work for me.
Hi Zoran,
I would not recommend using/creating Odata entities when you have to pass a list of objects as input. I highly recommend creating a web service which can handle any type/any number of inputs. Refer to this link for creating a custom webservice community.dynamics.com/.../custom-web-service-in-few-steps-d365fo if you do not know how to.
If your web service is going to take a custom object, then you have to first create a data contract class for this object type so that the web service knows what it is.
Here below I have a web service method which takes 2 inputs. 'header' belongs to class InvoiceRec. This is a custom data contract class with invoice header fields. 'lines' input as you see is a list and it belongs to a custom data contract class called InvoiceLinesTab which has my invoice line fields. You have to define which class this list belongs to using the AifcollectionType annotation. You can learn how to create data contract class from here https://docs.microsoft.com/en-us/dynamicsax-2012/appuser-itpro/walkthrough-exposing-an-x-class-as-a-data-contract and other documentation.
[AifCollectionType('lines', Types::Class, classStr(InvoiceLinesTab))]
public void CreateInvoice(InvoiceRec header, List lines)
{
//your functional logic
}
Hi sriramgopalan58,
did you manage to find a solution for this?
I have the same issue trying to insert Header/Lines in entities but could not find a way for this.
Regards,
Zoran
Hi Anup,
Thank you for your explanation on the capabilities of ODATA. However, I still do not think ODATA methods/actions can be used to take a list of custom objects as input. The only way I know to do now in D365 is using webservice which works really well.
Hi Sergei,
The reason I cannot use the standard vendor invoice data entities is because I need a response back once the pending vendor invoice is posted. In my case, I want the voucher number from vendtrans after posting the pending vendor invoice.
I basically went down the path of creating a web service and exposing this service that my code can consume. Given that this is a web service, you can pass any number of inputs regardless of what type it is. You have to make sure to have data contract class if you are going to pass custom class objects to this service.
I highly recommend using this than ODATA methods for something more complex.
This are all the notes i have on using actions with ODATA entities:
Testing Odata actions
<vishwad365fo.blogspot.com/.../testing-odata-actions-for-instance.html>
vishwad365fo.blogspot.com/.../testing-odata-actions-in-d365.html
<community.dynamics.com/.../how-to-add-39-actions-39-on-odata-entities>
INSTANCE Methods
There is ane Odata action in the standard data entity 'ProjectEntity'
[SysODataActionAttribute("GetProjectTypes", true), SysODataCollectionAttribute("return", Types::String)]
public List GetProjectTypes()
{
DictEnum enumDict = new DictEnum(enumName2Id(enumstr("ProjType")));
List enumLabels = new List(Types::String);
int i ;
str enumValue;
for (i = 0; i < enumDict.values(); i++)
{
enumValue = enumDict.index2Label(i);
if (enumValue != "")
{
enumLabels.addEnd(enumValue);
}
}
return enumLabels;
}
The second parameter, highlighted in 'Yellow' set to 'true', signifies that it is an instance method.
So, if it is set to 'false' then it is a static method.
A. The call to static Odata actions, is the same syntax for instance methods?
B. No.
We have to make certain changes for that, since for the instance methods, we have to first select the data/record. As simple as selecting the table buffer first and then calling its instance methods.
In order to do that, we will have to pass 'primary key' for the entity and also the data area Id.
Syntax is as below:
https://<Base URL>/data/'dataentity name'('primary key', 'dataAreaId')/Microsoft.Dynamics.DataEntities.'Odata action method name'
For example, for Project entity, it would be:
https://<Base URL>/data/Projects(ProjectID='Proj-001', dataAreaId='USRT')/Microsoft.Dynamics.DataEntities.GetProjectTypes
STATIC methods:
With new Odata actions, we can now expose any custom business logic from D365, without having to create an custom service.
Apart from using the data entities for data integrations, we can also create and expose the methods too, as Odata actions. They can be used for the process integrations.
How to add ODATA Actions as Extensions of a Data Entity
There could be two possible scenarios,
· either you have custom data entity where you can directly add a method
· or you have standard data entity for that you need to create extension class.
(1)First, let's see how to add on the custom entity:
Add your method on data entity and add below attribute
[SysODataActionAttribute("<MethodName>", false)]
public static void <methodName>(<parameters(optional)>)
{
}
Save, Synch and Build your changes. This method should be available now on OData action in the Logic app or Flow.
(2) How to add the same in Data entity extension. Create a new class and add below attribute.
[ExtensionOf(tableStr(<data entity name>))]
final class <data entity name>_Extension
{
[SysODataActionAttribute("<MethodName>", false)]
public static void <methodName>(<parameters(optional)>)
{
}
}
Please make sure you use '_Extension' keyword for above class, it's mandatory.
Hi sriramgopalan58,
I believe there are default values that need to be populated in the pending vendor invoice tables that I can easily populate using the init() methods using x++ in ODATA method. I am not sure how to do that with a direct post to this table/
If you are referring to initValue method, it's executed automatically for entity and oData when you perform oData insert. You can bring more logic by modifying the entity if needed. By the way, there is already OOB entity for pending invoices (VendInvoiceHeaderEntity, VendInvoiceLineEntity, VendInvoiceSubLineEntity), so I assume you don't need to do to much development on D365 side.
What is the difference between webservice versus exposing a method via odata that can be consumed as a rest service outside . I would think its just the way the end point is going to be exposed but the logic that you can write or the arguments you can take as inputs or return params will remain the same in either case. Am I wrong in saying this ?
I don't know details about how web service definitions and oData metadata is generated by D365. Maybe it's a bug, maybe it's by design that oData can't work with the list of records input parameters. But you still have options to use oData CRUD or to use custom web service.
Hi Sergei,
What is the difference between webservice versus exposing a method via odata that can be consumed as a rest service outside . I would think its just the way the end point is going to be exposed but the logic that you can write or the arguments you can take as inputs or return params will remain the same in either case. Am I wrong in saying this ?
Could you, please, explain, why you can't import data directly to pending vendor invoice tables via oData?
I believe there are default values that need to be populated in the pending vendor invoice tables that I can easily populate using the init() methods using x++ in ODATA method. I am not sure how to do that with a direct post to this table/
If you use BatchWithSingleChangeset option, you don't need to parse the response to check if something is missing as if some requests inside changeset fail - all requests fail.
Thanks for this, I am going to try this where I just have one ChangeSet and multiple records within this.
Hi sriramgopalan58,
Your first link is about the list of string as input parameters. The second is about web service, but not about oData action.
- From my client code, I have to make 2 requests. First request to post to the custom AX table and upon successful post, I have to call my ODATA method to create the pending vendor invoice querying data from this table
Could you, please, explain, why you can't import data directly to pending vendor invoice tables via oData?
The response of 'batch' api is merely a text and not an object that I can easily handle in my client code. I have to parse through the entire text response to make sure all the invoice lines got posted to the table etc.
If you use BatchWithSingleChangeset option, you don't need to parse the response to check if something is missing as if some requests inside changeset fail - all requests fail.
Hi Sergie,
In my case, the input to ODATA (invoice header and lines) comes from a third party application. So ODATA method needs to be able to take them as inputs. I am not able to understand how to make use of CRUD operation here.
If you look at the 2 links I posted in my first message here, people have gotten a list of records as inputs to ODATA methods to work. I believe there is some annotation which is missing but cant figure out what and there is no example in microsoft docs about accepting a list as input from what I saw. It only has examples of returning a list as an output which you have tested and worked.
My only other feasible alternative (which I want to avoid) is post my invoice header and invoice lines to a custom AX table and have my ODATA method query this table to get the invoice header and lines information to create a pending vendor invoice. However this approach has the following cons in my opinion:
- From my client code, I have to make 2 requests. First request to post to the custom AX table and upon successful post, I have to call my ODATA method to create the pending vendor invoice querying data from this table
- Also given that I deal with invoice lines which are a collection, in order to post to a custom AX table exposed via ODATA, I have to use the 'batch' api request (cannot call the request once for every single line). The response of 'batch' api is merely a text and not an object that I can easily handle in my client code. I have to parse through the entire text response to make sure all the invoice lines got posted to the table etc.
I am open to any other suggestions that make the design clean and error handling is easy.
Hi Sriramgopalan58,
I also tested it today a bit, yes, it works for lists with simple types like string, but method definition can't be generated when you switch it to type "Record" (the code is still compilable). But the list of records (at least entities) works for return value.
In your case, it's better to utilize oData CRUD operation for entities with single transaction docs.microsoft.com/.../odata
André Arnaud de Cal...
293,302
Super User 2025 Season 1
Martin Dráb
232,114
Most Valuable Professional
nmaenpaa
101,156
Moderator