我有一个问题,C#无法通过从D365BC运行的ODATA Web服务创建/更新/删除。
PS我正在使用VS 2019,C#winforms。
我们公司将使用Dynamics 365商业中心(D365BC),该中心使用编程语言AL。
AL是一种不错的语言,但是有些事情我们只是喜欢用C#来做。
在D365BC中,您可以轻松创建表和页面(使用这些表的对象)。对于每个页面,您都可以通过单击复选框来创建Web服务。这些Web服务可用于通过SOAP / ODATA v3和ODATA v4进行通信。所有这些都可以同时使用。
因此,创建允许与D365BC中创建的表进行通信的Web服务非常容易。
如果使用SOAP Web服务,则可以执行所有CRUD功能。但是,当我使用ODATA(v3或v4)时,我可以从Web服务中读取信息,但不能创建/更新或删除。我们可能偶尔要使用ODATA的原因是ODATA比SOAP快。
我在D365BC中拥有的代码:
一个简单的表,其中每个字段都是SQL表中的一条记录。
使用该表并能够从已使用表中制作Web服务的卡片页。
比有一个列表页面,D365BC中使用该列表页面来获取记录列表,该记录并不是Web服务真正需要的,但我提供它只是为了完整。
为了能够创建Web服务,我们需要向D365BC提供一些xml文件。我提供这个只是为了完整。
使用C#从ODATA Web服务读取是没有问题的:
我的C#尝试使用ODATA Web服务在表中更新或创建新记录:
因此,如果我使用Patch,Put或Post(错误号除外),则没有任何区别。
我试图通过D365BC论坛获取答复,但我只是没有得到答复(至少没有得到任何帮助的答复)。
自己找到答案就像口头上的“大海捞针”。 D365BC使用相对新语言AL。而且,如果您发现某些内容,通常是使用C / AL的Navision,这是完全不同的。
我还使用Nuget软件包管理器尝试了一种完全不同的方法,并查看那里有哪些ODATA软件包。
其中许多是为MVC创建的(我正在使用Win Forms)。
但是我尝试过:
-Microsoft.OData.Data(用于ODATA v3,它不只是安装在VS 2019上,而是使用我在网上找到的东西安装在VS 2019上,但是所有这些示例都假设没有身份验证的ODATA Web服务。使用身份验证需要在OdataClient.odata.config文件中设置一些设置,那么不会创建此文件(也许仅在使用MVC时),如果我自己创建它并设置身份验证设置,则它什么都不做。我收到验证错误,未设置密码/用户名)
-Microsoft.OData.Core(与ODATA v4一起使用,其行为与.DATA版本完全相同(同样的问题),再次没有运气)
-Simple.Odata.Client(无法通过身份验证,这是一个显示停止器)
-也尝试了第三个(忘记了名称),但是仅仅使用身份验证也是一个问题。
当我与某些人谈论这个问题时,他们或多或少都声称SOAP和ODATA之间没有太大区别。但是,SOAP有效,而ODATA无效。
我希望有人对此有经验,愿意并且有能力为我提供帮助。
我将以有效的SOAP代码结束,我们可以使用它来更新记录:
亲切的问候,
克莱门斯·林德斯
亲切的问候,
克莱门斯·林德斯
PS我正在使用VS 2019,C#winforms。
我们公司将使用Dynamics 365商业中心(D365BC),该中心使用编程语言AL。
AL是一种不错的语言,但是有些事情我们只是喜欢用C#来做。
在D365BC中,您可以轻松创建表和页面(使用这些表的对象)。对于每个页面,您都可以通过单击复选框来创建Web服务。这些Web服务可用于通过SOAP / ODATA v3和ODATA v4进行通信。所有这些都可以同时使用。
因此,创建允许与D365BC中创建的表进行通信的Web服务非常容易。
如果使用SOAP Web服务,则可以执行所有CRUD功能。但是,当我使用ODATA(v3或v4)时,我可以从Web服务中读取信息,但不能创建/更新或删除。我们可能偶尔要使用ODATA的原因是ODATA比SOAP快。
我在D365BC中拥有的代码:
一个简单的表,其中每个字段都是SQL表中的一条记录。
C#:
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;
}
使用该表并能够从已使用表中制作Web服务的卡片页。
C#:
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;
}
}
}
}
}
比有一个列表页面,D365BC中使用该列表页面来获取记录列表,该记录并不是Web服务真正需要的,但我提供它只是为了完整。
C#:
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;
}
}
}
}
}
为了能够创建Web服务,我们需要向D365BC提供一些xml文件。我提供这个只是为了完整。
XML:
<?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>
使用C#从ODATA Web服务读取是没有问题的:
C#:
listBox1.Items.Clear();
WorkersReadFromAlWebService = new List<WorkerClass>();
string _wsURL = "//api.businesscentral.dynamics.com/v2.0/SomeFunfyGuid/Sandbox/WS/CRONUS NL/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
我的C#尝试使用ODATA Web服务在表中更新或创建新记录:
C#:
string _url = "//api.businesscentral.dynamics.com/v2.0/SomeFunkyGuid/Sandbox/ODataV4/Company('CRONUS NL')/WorkersWS";
//string _url = "//api.businesscentral.dynamics.com/v2.0/SomeFunkyGuid/Sandbox/ODataV3/Company('CRONUS NL')/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("UserNameassword"));
_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)
{
}
因此,如果我使用Patch,Put或Post(错误号除外),则没有任何区别。
我试图通过D365BC论坛获取答复,但我只是没有得到答复(至少没有得到任何帮助的答复)。
自己找到答案就像口头上的“大海捞针”。 D365BC使用相对新语言AL。而且,如果您发现某些内容,通常是使用C / AL的Navision,这是完全不同的。
我还使用Nuget软件包管理器尝试了一种完全不同的方法,并查看那里有哪些ODATA软件包。
其中许多是为MVC创建的(我正在使用Win Forms)。
但是我尝试过:
-Microsoft.OData.Data(用于ODATA v3,它不只是安装在VS 2019上,而是使用我在网上找到的东西安装在VS 2019上,但是所有这些示例都假设没有身份验证的ODATA Web服务。使用身份验证需要在OdataClient.odata.config文件中设置一些设置,那么不会创建此文件(也许仅在使用MVC时),如果我自己创建它并设置身份验证设置,则它什么都不做。我收到验证错误,未设置密码/用户名)
-Microsoft.OData.Core(与ODATA v4一起使用,其行为与.DATA版本完全相同(同样的问题),再次没有运气)
-Simple.Odata.Client(无法通过身份验证,这是一个显示停止器)
-也尝试了第三个(忘记了名称),但是仅仅使用身份验证也是一个问题。
当我与某些人谈论这个问题时,他们或多或少都声称SOAP和ODATA之间没有太大区别。但是,SOAP有效,而ODATA无效。
我希望有人对此有经验,愿意并且有能力为我提供帮助。
我将以有效的SOAP代码结束,我们可以使用它来更新记录:
C#:
string _wsURL = "//api.businesscentral.dynamics.com/v2.0/SomeFunkyGuid/Sandbox/WS/CRONUS NL/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)
{
}
}
亲切的问候,
克莱门斯·林德斯
亲切的问候,
克莱门斯·林德斯
Last edited: