Click here to Skip to main content
15,885,216 members
Home / Discussions / C#
   

C#

 
GeneralRe: problem registering in the access database Pin
ago248617-Mar-20 3:14
ago248617-Mar-20 3:14 
GeneralRe: problem registering in the access database Pin
ago248617-Mar-20 1:10
ago248617-Mar-20 1:10 
GeneralRe: problem registering in the access database Pin
Richard MacCutchan17-Mar-20 2:18
mveRichard MacCutchan17-Mar-20 2:18 
GeneralRe: problem registering in the access database Pin
ago248617-Mar-20 2:26
ago248617-Mar-20 2:26 
GeneralRe: problem registering in the access database Pin
Richard MacCutchan17-Mar-20 2:36
mveRichard MacCutchan17-Mar-20 2:36 
QuestionJava Api Callbacks crashes on dotnet framework version 4.5 and above Pin
isaketranjan16-Mar-20 0:06
isaketranjan16-Mar-20 0:06 
AnswerRe: Java Api Callbacks crashes on dotnet framework version 4.5 and above Pin
Gerry Schmitz16-Mar-20 5:05
mveGerry Schmitz16-Mar-20 5:05 
QuestionC# and ODATA CRUD communication with D365BC web service Pin
clemenslinders12-Mar-20 0:13
clemenslinders12-Mar-20 0:13 
I have a problem where C# cannot Create/Update/Delete through an ODATA web service running from D365BC.

PS I am using VS 2019, C# winforms.


Our company is going to use Dynamics 365 Business Central (D365BC), which uses the programming language AL.

AL is a nice language but some things we will simply prefer to do in C#.

In D365BC you can easily create tables and pages (objects that consume these tables). And for each page you can create a web service by simply clicking a checkbox. These web service can be used to communicate via SOAP/ODATA v3 and ODATA v4. All are available at the same time.

So creating a web service that allows communication with a table created in D365BC is fairly easy.

If I use the SOAP webservice I can perform all CRUD functions. But when I use ODATA (either v3 or v4) I can read from the web service but I cannot Create/Update or Delete. The reason that we may occasionally want to use ODATA, is that ODATA is faster than SOAP.

The code I have in D365BC:
A simple table where every field is a record in a SQL 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;
}



The card page that uses the table and has the ability to make a web service out of the used table.
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;

                }
            }
        }
    }
}


Than there is a list page, that is used in D365BC to get a list of records, not really needed for the web service, but I provide it just to be complete.

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;
                }
            }
        }
    }
}


In order to be able to create the web service we need to provide a little xml file to D365BC. I provide this, just to be complete.

<?xml version = "1.0" encoding = "utf-8" ?>
<ExportedData>
    <TenantWebServiceCollection>
        <TenanatWebService>
            <ObjectType>Page</ObjectType>
            <ObjectID>50108</ObjectID>
            <ServiceName>WorkersWS</ServiceName>
            <Published>true</Published>
        </TenanatWebService>
    </TenantWebServiceCollection>
</ExportedData>


Using C3 to read from the ODATA web service is no problem:

listBox1.Items.Clear();
WorkersReadFromAlWebService = new List<WorkerClass>();
string _wsURL = "https://api.businesscentral.dynamics.com/v2.0/SomeFunfyGuid/Sandbox/WS/CRONUS%20NL/Page/WorkersWS";
string _userName = "UserName";
string _wsKey = "Password";

//Create an instance of the D365BC SOAP WS
BasicHttpBinding _binding = new BasicHttpBinding();

//Set https usage
_binding.Security.Mode = BasicHttpSecurityMode.Transport;
_binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

using (WorkersWS_PortClient _ws = new WorkersWS_PortClient(_binding, new EndpointAddress(_wsURL)))
{
    _ws.ClientCredentials.UserName.UserName = _userName;
    _ws.ClientCredentials.UserName.Password = _wsKey;

    //Filter
    List<WorkersWS_Filter> _filters = new List<WorkersWS_Filter>();
    WorkersWS_Filter _filter = new WorkersWS_Filter
    {
        Field = WorkersWS_Fields.No,
        Criteria = "*"
    };
    _filters.Add(_filter);

    try
    {
        foreach (WorkersWS _workerWS in _ws.ReadMultiple(_filters.ToArray(), "", 0))
        {
            WorkerClass _wc = new WorkerClass();
            _wc.E_Tag = _workerWS.Key;
            _wc.First_Name = _workerWS.First_name;
            _wc.Last_Name = _workerWS.Last_Name;
            _wc.FunctionName = _workerWS.FunctionName;
            WorkersReadFromAlWebService.Add(_wc);
            listBox1.Items.Add(_workerWS.Last_Name + " / " + _workerWS.First_name + " (No: " + _workerWS.No + ")");
        }
    }
    catch (Exception _ex)
    {

    }
}

ClearWorker();//Clears some textboxes



My C# attempt to Update or Create a new record in the table using the ODATA web service:

//using WorkersWS uses a List page in the background, which is capable of updating and inserting. WorkersWebService uses a card page, which is NOT capable of updating and inserting. But unfortunately the errors remain.

string _url = "https://api.businesscentral.dynamics.com/v2.0/SomeFunkyGuid/Sandbox/ODataV4/Company('CRONUS%20NL')/WorkersWS";
//string _url = "https://api.businesscentral.dynamics.com/v2.0/SomeFunkyGuid/Sandbox/ODataV3/Company('CRONUS%20NL')/WorkersWS";//ODATA V3, makes no difference
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: Methode is not allowed
string _etag = rtbE_Tag.Text;
_request.Headers["If-Match"] = String.Format("W/\"{0}\"", _etag);
//_request.Method = "POST";//(when using POST, the 4 lines above are commented out)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);

try
{
    _request.ContentLength = data.Length;

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

    HttpWebResponse _response = _request.GetResponse() as HttpWebResponse;//Here we get the exception errors 400 or 405
    Console.WriteLine(_response.StatusCode);
}
catch (Exception ex)
{

}


So it makes no difference if I use Patch, Put or Post (except for the error number).

I tried to get an anser through D365BC forums, but I simply get no reply (at least not a reply that is of any help).

Finding answers yourselves is like the verbal 'needle in the haystack'. D365BC uses the relative new language AL. And if you find something it usually is for Navision, which uses C/AL, which is completely different.


I also tried a completely different approach by using the Nuget package manager and see what ODATA packages are there.

Many of these are created for MVC (I am using Win Forms).
But I tried:
- Microsoft.OData.Data (for use with ODATA v3, It doesn't simply install on VS 2019, but using something that I found online I got it to install on VS 2019, but all these sample assume an ODATA web service without authentication. Using authentication, requires setting some settings in the OdataClient.odata.config file. Well this file isn't created (perhaps only when using MVC), if I create it myself and I set the settings for authentication, it simply does nothing. I get an authentication error, password/username not set)
- Microsoft.OData.Core (for use with ODATA v4, behaves exactly like the .DATA version (so same problems), again no luck)
- Simple.Odata.Client (couldn't get authenticated, which is a show stopper)
- tried a third one as well (forgot the name), but also simply using the authentication is a problem.

When I speak about this problem with some people they more or less all claim that there isn't much difference between SOAP and ODATA. Yet SOAP works and ODATA doesn't.

I hope there is someone who has experience with this and is willing and capable to help me.

I will end with the SOAP code that works and which we can use to update a record:
string _wsURL = "https://api.businesscentral.dynamics.com/v2.0/SomeFunkyGuid/Sandbox/WS/CRONUS%20NL/Page/WorkersWS";
string _userName = "Username";
string _wsKey = "Password";

//Create an instance of the D365BC SOAP WS
BasicHttpBinding _binding = new BasicHttpBinding();

//Set https usage
_binding.Security.Mode = BasicHttpSecurityMode.Transport;
_binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

using (WorkersWS_PortClient _ws = new WorkersWS_PortClient(_binding, new EndpointAddress(_wsURL)))
{
    _ws.ClientCredentials.UserName.UserName = _userName;
    _ws.ClientCredentials.UserName.Password = _wsKey;

    try
    {
        WorkersWS _wws = _ws.Read(tbNo.Text);
        if (_wws.No == tbNo.Text)
        {
            _wws.First_name = tbFirstName.Text;
            _wws.Last_Name = tbLastName.Text;
            _wws.FunctionName = tbFunctionName.Text;
            _ws.Update(ref _wws);

            if (_wws.No == tbNo.Text)
            {
                tbResult.Text = "Update success.";
            }
        }
    }
    catch (Exception _ex)
    {

    }
}



Kind regards,



Clemens Linders
SuggestionRe: C# and ODATA CRUD communication with D365BC web service Pin
Richard Deeming12-Mar-20 1:12
mveRichard Deeming12-Mar-20 1:12 
GeneralRe: C# and ODATA CRUD communication with D365BC web service Pin
clemenslinders12-Mar-20 3:48
clemenslinders12-Mar-20 3:48 
GeneralRe: C# and ODATA CRUD communication with D365BC web service Pin
Richard Deeming12-Mar-20 4:29
mveRichard Deeming12-Mar-20 4:29 
GeneralRe: C# and ODATA CRUD communication with D365BC web service Pin
clemenslinders12-Mar-20 4:54
clemenslinders12-Mar-20 4:54 
GeneralRe: C# and ODATA CRUD communication with D365BC web service Pin
Richard Deeming12-Mar-20 5:43
mveRichard Deeming12-Mar-20 5:43 
GeneralRe: C# and ODATA CRUD communication with D365BC web service Pin
clemenslinders12-Mar-20 21:25
clemenslinders12-Mar-20 21:25 
QuestionC# code -Compare 2 pdf and give output as 2 pdfs with color Pin
Member 1476942610-Mar-20 20:13
Member 1476942610-Mar-20 20:13 
AnswerRe: C# code -Compare 2 pdf and give output as 2 pdfs with color Pin
OriginalGriff10-Mar-20 20:49
mveOriginalGriff10-Mar-20 20:49 
AnswerRe: C# code -Compare 2 pdf and give output as 2 pdfs with color Pin
Maciej Los10-Mar-20 21:25
mveMaciej Los10-Mar-20 21:25 
JokeRe: C# code -Compare 2 pdf and give output as 2 pdfs with color Pin
Richard Deeming11-Mar-20 0:37
mveRichard Deeming11-Mar-20 0:37 
GeneralRe: C# code -Compare 2 pdf and give output as 2 pdfs with color Pin
kalberts12-Mar-20 3:01
kalberts12-Mar-20 3:01 
Questiontime requirements in real-times systems? Pin
auting8210-Mar-20 3:08
auting8210-Mar-20 3:08 
AnswerRe: time requirements in real-times systems? Pin
OriginalGriff10-Mar-20 3:49
mveOriginalGriff10-Mar-20 3:49 
GeneralRe: time requirements in real-times systems? Pin
auting8210-Mar-20 4:43
auting8210-Mar-20 4:43 
GeneralRe: time requirements in real-times systems? Pin
OriginalGriff10-Mar-20 4:51
mveOriginalGriff10-Mar-20 4:51 
AnswerRe: time requirements in real-times systems? Pin
Gerry Schmitz10-Mar-20 6:30
mveGerry Schmitz10-Mar-20 6:30 
AnswerRe: time requirements in real-times systems? Pin
Richard MacCutchan10-Mar-20 7:02
mveRichard MacCutchan10-Mar-20 7:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.