Skip to main content

Notifications

Announcements

No record found.

Small and medium business | Business Central, N...
Suggested answer

WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service

Posted on by 70

Using Visual Code I created a web service for D365BC.

In MS Visual Studio I use the following code to read data from this web service (so far so good):

    private void button1_Click(object sender, EventArgs e)
    {
        string _url = "">api.businesscentral.dynamics.com/.../WorkersWebService";
        HttpWebRequest _request = (HttpWebRequest)WebRequest.Create(_url);
        _request.ContentType = "application/json; charset=utf-8";
        _request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes("USERNAME:PASSWORD"));
        _request.PreAuthenticate = true;
        HttpWebResponse _response = _request.GetResponse() as HttpWebResponse;
        using (Stream _responseStream = _response.GetResponseStream())
        {
            StreamReader _reader = new StreamReader(_responseStream, Encoding.UTF8);
            string _content = _reader.ReadToEnd();

            string _jasonPart = GetJsonPartMultiRecord(_content);

            List<WorkerClass> _jWorkers = JsonConvert.DeserializeObject<List<WorkerClass>>(_jasonPart);

            foreach (var _worker in _jWorkers)
            {
                WorkersReadFromAlWebService.Add(_worker);
            }
        }
    }

Now I would like to alter a value from a record or create a new record (using ODATA)

I tried something like this:

        private void btnUpdateWS_Click(object sender, EventArgs e)
        {
            string _url = "">api.businesscentral.dynamics.com/.../WorkersWS";
            HttpWebRequest _request = (HttpWebRequest)WebRequest.Create(_url);
            _request.ContentType = "application/json; charset=utf-8";
            _request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes("USERNAME:PASSWORD"));
            _request.Accept = "*/*";
            _request.KeepAlive = true;
            //_request.Method = "PUT";//error 405: Method is not allowed
            _request.Method = "PATCH";//error 405: Method is not allowed
            string _etag = rtbE_Tag.Text;
            _request.Headers["If-Match"] = String.Format("W/\"{0}\"", _etag);
            //_request.Method = "POST";//error 400: invalid method

            string body = "{" + Environment.NewLine +
                                "\"No\":" + tbNo.Text + "," + Environment.NewLine +
                                "\"First_name\":\"" + tbFirstName.Text + "\"," + Environment.NewLine +
                                "\"Last_Name\":\"" + tbLastName.Text + "\"," + Environment.NewLine +
                                "\"FunctionName\":\"" + tbFunctionName.Text + "\"," + Environment.NewLine +
                              "}";

            byte[] data = Encoding.ASCII.GetBytes(body);

            _request.ContentLength = data.Length;

            Stream requestStream = _request.GetRequestStream();
            requestStream.Write(data, 0, data.Length);
            requestStream.Close();

            HttpWebResponse _response = _request.GetResponse() as HttpWebResponse;
            Console.WriteLine(_response.StatusCode);
        }

It doesn't matter if I use Patch or Put in both cases I get: error 405: Method is not allowed?
Using a POST (setting Patch, Put and the request headers above the POST as comment) I get error 400 invalid method

I would like to find a way using ODATA to update and or create a new record.



What am I doing wrong??




Kind regards,



Clemens Linders
  • Suggested answer
    JorgeRamirez Profile Picture
    JorgeRamirez 19 on at
    WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service
    It's never too late to answer this, here is a C# Store procedure that works:
     
    With API V2.
     
    using System.Net.Http.Headers;
    using System.Net.Http;
    using Microsoft.Identity.Client;
    using System.Collections.Generic;
    using System.Linq;
    using Newtonsoft.Json;
     
    They key of solving this was adding a If-Match header and doing a PATCH.
     
            private static bool UpdateCustomereVending(string accessToken, string myCustAPI, string myCustID, string custeVendingValue)
            {
                            
               uri = "https://api.businesscentral.dynamics.com/v2.0/a0fda4ba-f9XX-47XX-8c46-5858a8XXXXXX/DEVELOPMENT/ODataV4/Company('XXXXX')/Customers(0e27f71a-4512-ef11-9f88-6045bde2f9dX)"; //with the customer ID
               
                bool success = false;

                using (var httpClient = new HttpClient())
                {
                    try
                    {
                        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
               
                        httpClient.DefaultRequestHeaders.Add("If-Match", "*");
                        var customerUpdate = new
                        {
                            eVendingNumber = custeVendingValue
                        };
                        string jsonData = JsonConvert.SerializeObject(customerUpdate);
                        var content = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json");
                        var request = new HttpRequestMessage(new HttpMethod("PATCH"), uri)
                        {
                            Content = content
                        };
                        HttpResponseMessage response = httpClient.SendAsync(request).Result;
         
               

                        if (response.IsSuccessStatusCode)
                        {
                            Console.WriteLine($"Customer {myCustID} updated successfully.");
                            success = true;
                        }
                        else
                        {
                            Console.WriteLine($"ERROR: Customer {myCustID} could not be updated. Status code: {response.StatusCode}");
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("ERROR:" + ex.Message);
                    }
                }
                return success;
            }
        }
    }
  • Clemens Linders Profile Picture
    Clemens Linders 70 on at
    RE: WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service

    It doesn't look as if I am going to get any answers!

  • Clemens Linders Profile Picture
    Clemens Linders 70 on at
    RE: WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service

    Hi Franz/Archer,

    Totally not important, but I figured out that this website shows your avatar name when I'm not logged in and your real name once logged in.

    It's weird/creepy how details of people can be found on the internet. In a dull moment at the office I decided to google your name and found that you have called your Avatar: Archer because you like star trek. If I use an Avatar I call it Jean-Luc Picard. I guess it shows that we have a common interest, but yet some age difference.

    Ok, as mentioned that was totally not important and also not why I decided to add to my original message.

    Perhaps it is interesting to see what code I used in Visual Code / D365BC:

    CpWorkers.AL
    page 50108 "Workers Card"
    {
    PageType = Card;
    ApplicationArea = All;
    UsageCategory = Administration;
    SourceTable = Workers;

    layout
    {
        area(Content)
        {
            group(General)
            {
                field("No."; "No.")
                {
                    ApplicationArea = Basic;
                    Importance = Promoted;
                }
    
                field("First name"; "First name")
                {
                    ApplicationArea = Basic;
                }
    
                field("Last name"; "Last name")
                {
                    ApplicationArea = Basic;
                }
    
                field(FunctionName; FunctionName)
                {
                    ApplicationArea = Basic;
    
                }
            }
        }
    }
    

    }

    LpWorkers.AL
    page 50109 "Workers List"
    {
    PageType = List;
    ApplicationArea = All;
    UsageCategory = Lists;
    SourceTable = Workers;

    layout
    {
        area(Content)
        {
            repeater(Group)
            {
                field("No."; "No.")
                {
                    ApplicationArea = Basic;
                }
    
                field("First name"; "First name")
                {
                    ApplicationArea = Basic;
                }
    
                field("Last Name"; "Last Name")
                {
                    ApplicationArea = Basic;
                }
    
                field(FunctionName; FunctionName)
                {
                    ApplicationArea = Basic;
                }
            }
        }
    }
    

    }

    TabWorkers.AL
    table 50109 "Workers"
    {
    DataClassification = ToBeClassified;

    fields
    {
        field(1; "No."; Code[20])
        {
            DataClassification = ToBeClassified;
        }
    
        field(10; "First name"; Text[50])
        {
            DataClassification = ToBeClassified;
        }
    
        field(20; "Last Name"; Text[50])
        {
            DataClassification = ToBeClassified;
    
        }
    
        field(40; FunctionName; Text[50])
        {
            DataClassification = ToBeClassified;
    
        }
    }
    
    trigger OnInsert()
    var
        myInt: Integer;
    begin
    
    end;
    
    trigger OnModify()
    var
        myInt: Integer;
    begin
    
    end;
    
    trigger OnDelete()
    var
        myInt: Integer;
    begin
    
    end;
    

    }

    WorkersWebService.xml (based on the Card Page, this would be the generally excepted choice)
    <?xml version = "1.0" encoding = "utf-8" ?>
    <ExportedData>
        <TenantWebServiceCollection>
            <TenanatWebService>
                <ObjectType>Page</ObjectType>
                <ObjectID>50108</ObjectID>
                <ServiceName>WorkersWebService</ServiceName>
                <Published>true</Published>
            </TenanatWebService>
        </TenantWebServiceCollection>
    </ExportedData>

    Hope this helps.

    Kind regards,

    Clemens Linders

  • Clemens Linders Profile Picture
    Clemens Linders 70 on at
    RE: WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service

    Hi Franz/Archer (bug in website one moment your name is Archer, than I click something and go back and than your name is Franz)

    I put the code in a Try Catch (always best)

                string _url = "">api.businesscentral.dynamics.com/.../WorkersWS";
                HttpWebRequest _request = (HttpWebRequest)WebRequest.Create(_url);
                _request.ContentType = "application/json; charset=utf-8";
                _request.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes("USERNAME:PASSWORD"));
                _request.Accept = "*/*";
                _request.KeepAlive = true;
                //_request.Method = "PUT";//error 405: Methode is niet toegestaan
                _request.Method = "PATCH";//error 405: Methode is niet toegestaan
                string _etag = rtbE_Tag.Text;
                _request.Headers["If-Match"] = String.Format("W/\"{0}\"", _etag);
                //_request.Method = "POST";//error 400: ongeldige opdracht
                string body = "{" + Environment.NewLine +
                                    "\"No\":" + tbNo.Text + "," + Environment.NewLine +
                                    "\"First_name\":\"" + tbFirstName.Text + "\"," + Environment.NewLine +
                                    "\"Last_Name\":\"" + tbLastName.Text + "\"," + Environment.NewLine +
                                    "\"FunctionName\":\"" + tbFunctionName.Text + "\"," + Environment.NewLine +
                                  "}";
                byte[] data = Encoding.ASCII.GetBytes(body);
                try
                {
                    _request.ContentLength = data.Length;
                    Stream requestStream = _request.GetRequestStream();
                    requestStream.Write(data, 0, data.Length);
                    requestStream.Close();
                    HttpWebResponse _response = _request.GetResponse() as HttpWebResponse; //It breaks at this line
                    Console.WriteLine(_response.StatusCode);
                }
                catch (Exception ex)
                {
                }
    The error code is: 
    ex = {"De externe server heeft een fout geretourneerd: (405) Methode is niet toegestaan."} This is Dutch in English this would be:
    The external server returned an error: (405) Method is not allowed.
    Hope this helps to find out what my problem is.
    Kind regards,
    Clemens Linders
  • keoma Profile Picture
    keoma 32,675 on at
    RE: WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service

    do you get a innerexception?

  • Clemens Linders Profile Picture
    Clemens Linders 70 on at
    RE: WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service

    Hi Franz,

    In the email I received from Microsoft it showed your avatar and the word Archer next to it. So I assumed your name was Archer. Later in the forum I saw it is Franz.

    Kind regards,

    Clemens Linders

  • Clemens Linders Profile Picture
    Clemens Linders 70 on at
    RE: WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service

    Hi Archer,

    Thanks for the input, but I am not getting any further.

    I looked at this Microsoft page.

    It says: All calls fail if the user does not have the relevant permissions, and if the relevant property on the page, InsertAllowed, ModifyAllowed, or DeleteAllowed, is set to No.

    When I use SOAP to Read/Insert/Update/Delete records, it all works fine. So ….Allowed must be set correctly (and I didn't make any changes to this)

    It also says that Triggers are activated:

    POST Creates a new entity. OnNewRecord and OnInsert
    PATCH Modifies the specified existing entity. OnModify
    DELETE Deletes the specified existing entity. OnDelete

    In all the samples I learned from the internet they remove these triggers, so this could be something.

    I added triggers (unfortunately I have no idea what to put in them)

    This is my table:

    table 50109 "Workers"
    {
        DataClassification = ToBeClassified;
        fields
        {
            field(1; "No."; Code[20])
            {
                DataClassification = ToBeClassified;
            }
            field(10; "First name"; Text[50])
            {
                DataClassification = ToBeClassified;
            }
            field(20; "Last Name"; Text[50])
            {
                DataClassification = ToBeClassified;
            }
            field(40; FunctionName; Text[50])
            {
                DataClassification = ToBeClassified;
            }
        }
        trigger OnInsert()
        var
            myInt: Integer;
        begin
        end;
        trigger OnModify()
        var
            myInt: Integer;
        begin
        end;
        trigger OnDelete()
        var
            myInt: Integer;
        begin
        end;
    }
    When I try this with the empty triggers I still get the error: Method not allowed, which is (I think) a bit strange.
    I would have expected to not give an error but at the same time also do nothing as the trigger is empty.
    To be complete I will add the Card Page
    page 50108 "Workers Card"
    {
        PageType = Card;
        ApplicationArea = All;
        UsageCategory = Administration;
        SourceTable = Workers;
        layout
        {
            area(Content)
            {
                group(General)
                {
                    field("No."; "No.")
                    {
                        ApplicationArea = Basic;
                        Importance = Promoted;
                    }
                    field("First name"; "First name")
                    {
                        ApplicationArea = Basic;
                    }
                    field("Last name"; "Last name")
                    {
                        ApplicationArea = Basic;
                    }
                    field(FunctionName; FunctionName)
                    {
                        ApplicationArea = Basic;
                    }
                }
            }
        }
    }
    The List Page
    page 50109 "Workers List"
    {
        PageType = List;
        ApplicationArea = All;
        UsageCategory = Lists;
        SourceTable = Workers;
        layout
        {
            area(Content)
            {
                repeater(Group)
                {
                    field("No."; "No.")
                    {
                        ApplicationArea = Basic;
                    }
                    field("First name"; "First name")
                    {
                        ApplicationArea = Basic;
                    }
                    field("Last Name"; "Last Name")
                    {
                        ApplicationArea = Basic;
                    }
                    field(FunctionName; FunctionName)
                    {
                        ApplicationArea = Basic;
                    }
                }
            }
        }
    }
    And the XML file for the web service:
    <?xml version = "1.0" encoding = "utf-8" ?>
    <ExportedData>
        <TenantWebServiceCollection>
            <TenanatWebService>
                <ObjectType>Page</ObjectType>
                <ObjectID>50108</ObjectID>
                <ServiceName>WorkersWebService</ServiceName>
                <Published>true</Published>
            </TenanatWebService>
        </TenantWebServiceCollection>
    </ExportedData>
    As mentioned when using SOAP it all works fine.
    But as ODATA is faster I would also like to be able to use ODATA.
    Kind regards,
    Clemens Linders
  • Suggested answer
    keoma Profile Picture
    keoma 32,675 on at
    RE: WinForms C# performing a Post (or Patch) to insert/update data through AL/D365BC ODATA web service

    follow the instructions on docs.microsoft.com/.../use-odata-to-modify-data

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

December Spotlight Star - Muhammad Affan

Congratulations to a top community star!

Top 10 leaders for November!

Congratulations to our November super stars!

Tips for Writing Effective Suggested Answers

Best practices for providing successful forum answers ✍️

Leaderboard

#1
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 291,280 Super User 2024 Season 2

#2
Martin Dráb Profile Picture

Martin Dráb 230,214 Most Valuable Professional

#3
nmaenpaa Profile Picture

nmaenpaa 101,156

Leaderboard

Featured topics

Product updates

Dynamics 365 release plans