I have a NAV web service. I'm making a client in C# with the ultimate goal to create a class that can cope with all possible web services, to be called from NAV and other C# programs. When I make a web reference, calling the web service goes well. Trying to achieve my ultimate goal, I get the following error:
Metadata for object of type Table with id 11151182 is in a failed state. This is caused by a previous exception:
Could not find a part of the path 'C:\Documents and Settings\All Users\Application Data\Microsoft\Microsoft Dynamics NAV\60\Server\MicrosoftDynamicsNavServer\source\Record\Record11151182.cs'.
at ProductionPlanTest.CDynamicWebServiceCaller.CallWebMethod()
at ProductionPlanTest.CDynamicWebServiceCaller.CallWebMethod(String methodName, Object[] parameters)
at ProductionPlanTest.Program.Main(String[] args)
The class generates code and an assembly from a wsdl supplied by a url or file. Then the function is called from that assembly. I found some other examples of ways to call a web service, but this seems to be the most flexible.
I hope someone can help me out here.
Thanx in advance,
Remco
Here is the code of the client class:
using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Web.Services.Description;
using System.CodeDom;
using System.CodeDom.Compiler;
namespace ProductionPlanTest
{
class CDynamicWebServiceCaller
{
private class WebMethodCall
{
public string MethodName;
public System.Reflection.MethodInfo MethodInfo;
public System.Reflection.ParameterInfo [] ParameterInfo;
public object[] ParameterValues;
}
private ServiceDescription _serviceDescription;
private System.Reflection.Assembly _proxyAssembly;
private object _serviceInstance;
private System.Type _assemblyType;
private WebMethodCall _currentWebMethodCall;
private bool _hasWSDL, _useDefaultCredentials, _credentialsSetExplicit;
public CDynamicWebServiceCaller()
{
ServicePointManager.ServerCertificateValidationCallback = SSLCallBack;
}
public bool HasWSDL
{
get
{
return _hasWSDL;
}
}
public bool UseDefaultCredentials
{
get
{
return _useDefaultCredentials;
}
set
{
_credentialsSetExplicit = true;
_useDefaultCredentials = value;
}
}
/// <summary>
/// Get the WSDL definition from a web service call
/// </summary>
/// <param name="uri">URI of the web service</param>
public void GetWSDLFromURI(string uri)
{
if (string.IsNullOrEmpty(uri))
throw new Exception("The uri is not supplied");
var _webrequest = WebRequest.Create(uri);
if (_credentialsSetExplicit)
_webrequest.UseDefaultCredentials = _useDefaultCredentials;
var _stream = _webrequest.GetResponse().GetResponseStream();
_serviceDescription = ServiceDescription.Read(_stream);
this.GenerateProxyClass();
}
/// <summary>
/// Get the WSDL definition from a file
/// </summary>
/// <param name="filename">Full path and file name of the requested WSDL file</param>
public void GetWSDLFromFile(string filename)
{
if (string.IsNullOrEmpty(filename))
throw new Exception("The file name is not supplied");
_serviceDescription = ServiceDescription.Read(filename);
this.GenerateProxyClass();
}
/// <summary>
/// Initializes a new web service method call. Ensure to provide all necessary information before calling CallWebMethod()
/// </summary>
/// <param name="methodName">Name of the methode</param>
public void NewMethodCall(string methodName)
{
CheckWSDL();
_currentWebMethodCall = new WebMethodCall();
_currentWebMethodCall.MethodName = methodName;
_currentWebMethodCall.MethodInfo = _assemblyType.GetMethod(methodName);
_currentWebMethodCall.ParameterInfo = _currentWebMethodCall.MethodInfo.GetParameters();
_currentWebMethodCall.ParameterValues = null;
}
public string CallWebMethodAsString()
{
return CallWebMethod().ToString();
}
public object CallWebMethod()
{
CheckWSDL();
if (_currentWebMethodCall == null)
throw new Exception("Please start a new call with NewMethodCall");
if (_credentialsSetExplicit)
{
var _propertyInfo = _assemblyType.GetProperty("UseDefaultCredentials");
_propertyInfo.SetValue(_serviceInstance, _useDefaultCredentials, null);
}
try
{
return _currentWebMethodCall.MethodInfo.Invoke(_serviceInstance, _currentWebMethodCall.ParameterValues);
}
catch (Exception e)
{
// The failed Invoke throws a general error containing the actual error
if (e.InnerException != null)
throw e.InnerException;
else
throw e;
}
}
public object CallWebMethod(string methodName)
{
CheckWSDL();
_currentWebMethodCall = new WebMethodCall();
_currentWebMethodCall.MethodName = methodName;
_currentWebMethodCall.MethodInfo = _assemblyType.GetMethod(methodName);
_currentWebMethodCall.ParameterInfo = _currentWebMethodCall.MethodInfo.GetParameters();
_currentWebMethodCall.ParameterValues = null;
return CallWebMethod();
}
public object CallWebMethod(string methodName, object [] parameters)
{
CheckWSDL();
_currentWebMethodCall = new WebMethodCall();
_currentWebMethodCall.MethodName = methodName;
_currentWebMethodCall.MethodInfo = _assemblyType.GetMethod(methodName);
_currentWebMethodCall.ParameterInfo = _currentWebMethodCall.MethodInfo.GetParameters();
_currentWebMethodCall.ParameterValues = parameters;
return CallWebMethod();
}
private void CheckWSDL()
{
if (!_hasWSDL)
throw new Exception("No WSDL specification has been loaded");
}
private void GenerateProxyClass()
{
// Service importer
var _serviceImporter = new ServiceDescriptionImporter();
_serviceImporter.AddServiceDescription(_serviceDescription, null, null);
_serviceImporter.ProtocolName = "Soap";
_serviceImporter.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;
// Import
var _codeNameSpace = new CodeNamespace();
var _codeCompileUnit = new CodeCompileUnit();
_codeCompileUnit.Namespaces.Add(_codeNameSpace);
var _warnings = _serviceImporter.Import(_codeNameSpace, _codeCompileUnit);
// Check import
if (_warnings == ServiceDescriptionImportWarnings.NoCodeGenerated || _warnings == ServiceDescriptionImportWarnings.NoMethodsGenerated)
throw new Exception(string.Format("The following error occurred while importing the WSDL: {0}", _warnings.ToString()));
// Genereate code
using (var _stringWriter = new StringWriter(CultureInfo.CurrentCulture))
{
var _codeProvider = CodeDomProvider.CreateProvider("CSharp");
_codeProvider.GenerateCodeFromNamespace(_codeNameSpace, _stringWriter, null);
// Compiler parameters
var _params = new CompilerParameters(new string[] { "System.dll", "System.Xml.dll", "System.Web.Services.dll", "System.Data.dll" });
_params.GenerateExecutable = false;
_params.GenerateInMemory = true;
_params.TreatWarningsAsErrors = false;
_params.WarningLevel = 4;
_params.IncludeDebugInformation = false;
var _compilerResults = _codeProvider.CompileAssemblyFromSource(_params, _stringWriter.ToString());
_proxyAssembly = _compilerResults.CompiledAssembly;
// Create instance
_assemblyType = _proxyAssembly.GetType(_serviceDescription.Services[0].Name);
_serviceInstance = Activator.CreateInstance(_assemblyType);
}
_hasWSDL = true;
}
private static bool SSLCallBack(
object sender,
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
return true;
}
}
}
And the test program:
Using System;
using System.IO;
namespace ProductionPlanTest
{
class Program
{
static void Main(string[] args)
{
try
{
CDynamicWebServiceCaller ws = new CDynamicWebServiceCaller();
ws.UseDefaultCredentials = true;
if (args.Length > 0)
Console.WriteLine("Argument: {0}", args[0]);
if (args.Length > 0)
ws.GetWSDLFromURI(@"http://webserver:7047/DynamicsNAV/WS/webserver_BV/Codeunit/TL_CTS_Communication");
else if (args.Length > 0 && File.Exists(args[0]))
ws.GetWSDLFromFile(args[0]);
else
ws.GetWSDLFromFile(@C:\Indigo Solutions\ISAL\TL_CTS_Communication.xml);
Console.WriteLine("Call 1 {0}", ws.CallWebMethod("UpdateContainerAction", new object[] { "3017", "TL", "OPPOTTEN", "OPPOTTEN", "GROOT", "PV100003", 123 }));
Console.WriteLine("Call 2 {0}", ws.CallWebMethod("UpdateContainerAction", new object[] { "3017", "TL", "OPPOTTEN", "OPPOTTEN", "KLEIN", "PV100003", 123 }));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
finally
{
Console.ReadLine();
}
}
}
}
And, yes this is the last, (part of) the code generated by the class (dump from the stringwriter):
using System.Diagnostics;
using System.Web.Services;
using System.ComponentModel;
using System.Web.Services.Protocols;
using System;
using System.Xml.Serialization;
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("ProductionPlanTest", "1.0.0.0")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(
Name = "TL_CTS_Communication_Binding",
Namespace = "urn:microsoft-dynamics-schemas/codeunit/TL_CTS_Communication")]
public partial class TL_CTS_Communication : System.Web.Services.Protocols.SoapHttpClientProtocol
{
/// <remarks/>
public TL_CTS_Communication()
{
this.Url = "http://terlaakdb01:7047/DynamicsNAV/WS/Ter_Laak_Orchideeën_BV/Codeunit/TL_CTS_Communication";
}
/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
"urn:microsoft-dynamics-schemas/codeunit/TL_CTS_Communication:UpdateContainerAction",
RequestNamespace = "urn:microsoft-dynamics-schemas/codeunit/TL_CTS_Communication",
ResponseElementName = "UpdateContainerAction_Result",
ResponseNamespace = "urn:microsoft-dynamics-schemas/codeunit/TL_CTS_Communication",
Use = System.Web.Services.Description.SoapBindingUse.Literal,
ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
[return: System.Xml.Serialization.XmlElementAttribute("return_value")]
public bool UpdateContainerAction(string containerCode, string locationCode, string growthAction, string growthPhase, string sortingCode, string jobNo, int quantity)
{
object[] results = this.Invoke("UpdateContainerAction", new object[] {
containerCode,
locationCode,
growthAction,
growthPhase,
sortingCode,
jobNo,
quantity});
return ((bool)(results[0]));
}
}
Comments
Thanks for your reply. I tried it and the original error message is exchanged for another, but this is at an earlier part of the program (WSDLfromURI) that before the compilation did not go wrong. I get compilation errors (NAV client crashes during compilation). So I will try to get the errors fixed before trying the web service again.
I'll let you know.