Implement partial method to login to Soap Service - soap

I have problems logging in to old style Soap Service using NET 6.0. If I use 4.8 the login process is straight forward. But the generetad code from http://media.atg.se:80/info/PartnerInfoService/version11?WSDL differs between 6.0 and 4.8. In 6.0 I get this "todo" from generated code:
public partial class PartnerInfoServicePortClient : System.ServiceModel.ClientBase<ATGAIS.PartnerInfoServicePort>, ATGAIS.PartnerInfoServicePort
{
/// <summary>
/// Implement this partial method to configure the service endpoint.
/// </summary>
/// <param name="serviceEndpoint">The endpoint to configure</param>
/// <param name="clientCredentials">The client credentials</param>
public static partial void ConfigureEndpoint(string serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
.
.
.
//more generated methods
.
.
.
}
I just can't figure out HOW to implement it. The site requires ClientCredentials which I don't know how to set in 6.0. For 4.8 it was very simple as follows:
var partnerInfoService = new PartnerInfoService();
//Log in with credentials
try
{
partnerInfoService.Credentials = new NetworkCredential("Username", "Password");
}
catch (Exception ex)
{
MessageBox.Show($#"Error: {ex.Message}");
throw;
}

As it says you should implement the ConfigureEndpoint method, but for that you should create a new class which inherits from PartnerInfoServicePortClient, and... this is so boring...
So the easy way is convert this partial method into a real method, and add the code of the method according to your needs. What I mean is replacing this line:
public static partial void ConfigureEndpoint(string serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);
whith this line:
static public void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials)
{
}
As an example, I use this code to modify the configuration:
static public void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials)
{
var httpBinding = serviceEndpoint.Binding as BasicHttpBinding;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
httpBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
}
This code setup the service client to use Kerberos (Windows domain\username) authentication, but you can adjust the code to use any other authentication method.
PD: I am using Visual Studio 2022 and Net Core 6

Related

Can I inject dependency into migration (using EF-Core code-first migrations)?

I tried to inject IConfiguration into the migration (in constructor), and got exception: "No parameterless constructor defined for this object."
any workaround?
you cannot, the migrations need to be able to run outside the context of your application.
Since the Entity-framework command-line tool analyzes your code but does not run the startup.cs class.
Also it is not advisable. your migrations should be plain simple and not depend on anything. if it would, it could lead to major runtime side-effects where missing config could lead to missing tables or columns in production.
additional advise
If it involves a lot of small/equal/manual changes. Best way is to generate your migration file. Why? This way your migration will be deterministic: you know what the outcome will be. If a line in your migration fails, it is simple and clear why that is and easily(er) fixable.
There's a way to do what you want to do. In my scenario, I would like to use the database name in the connection string through the DbContext. EF core 2.1.1 is used. The code is modified from here
Create a custom MigrationsAssembly service
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using System;
using System.Reflection;
public class ContextAwareMigrationsAssembly : MigrationsAssembly
{
private readonly DbContext context;
public ContextAwareMigrationsAssembly(
ICurrentDbContext currentContext,
IDbContextOptions options,
IMigrationsIdGenerator idGenerator,
IDiagnosticsLogger<DbLoggerCategory.Migrations> logger) : base(currentContext, options, idGenerator, logger)
{
context = currentContext.Context;
}
/// <summary>
/// Modified from http://weblogs.thinktecture.com/pawel/2018/06/entity-framework-core-changing-db-migration-schema-at-runtime.html
/// </summary>
/// <param name="migrationClass"></param>
/// <param name="activeProvider"></param>
/// <returns></returns>
public override Migration CreateMigration(TypeInfo migrationClass, string activeProvider)
{
var hasCtorWithDbContext = migrationClass
.GetConstructor(new[] { typeof(DbContext) }) != null;
if (hasCtorWithDbContext)
{
var instance = (Migration)Activator.CreateInstance(migrationClass.AsType(), context);
instance.ActiveProvider = activeProvider;
return instance;
}
return base.CreateMigration(migrationClass, activeProvider);
}
}
Replace the IMigrationAssembly service in your DbContext with your custom class
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IMigrationsAssembly, ContextAwareMigrationsAssembly>();
}
Then you can add a DbContext parameter in your migration.
public Migration20180801(DbContext context)
{
DatabaseName = context.Database.GetDbConnection().Database;
}
In your case, you can replace all the DbContext references with IConfiguration and the relevant instance in the CreateMigration override.
If it is just about your connection-string (is it?), you may want to check this answer, which basically suggests this code in your startup-project (not in your migrations-project):
var myConnectionString = Configuration.GetConnectionString(myConnectionStringName);
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(
myConnectionString ,
x => x.MigrationsAssembly(myDbContextAssemblyName)));

Configuring NServicebus 4.7 with a predefined Autofac container

I'm a bit at a loss here. I'm developing a hosted NServicebus (v4.7) windows service and using Autofac (v3.5) to do my own type resolution. Given the NServicebus documentation I thought it was possible to add my own container in NServicebus's endpoint configuration. However no matter what I do, I can't get it to work. My current endpointconfig is the following:
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using Autofac;
using HyFlo.Base;
using NServiceBus;
using NServiceBus.ObjectBuilder.Autofac;
using NServiceBus.ObjectBuilder.Common.Config;
namespace HyFlo.Service
{
/// <summary>
/// EndpointConfig
/// </summary>
public class EndpointConfig : IConfigureThisEndpoint, INeedInitialization, AsA_Publisher
{
public void Init()
{
TraceWriter.Info("Scannning for Hyflo assemblies .. ");
var hyfloAssemblies = Directory.GetFiles(Directory.GetCurrentDirectory(), "Hyflo.*.dll",
SearchOption.TopDirectoryOnly);
TraceWriter.Info("Initializing Autofac with assemblies .. ");
try
{
var builder = new ContainerBuilder();
Assembly[] allAssemblies = hyfloAssemblies.Select(Assembly.LoadFile).ToArray();
builder.RegisterAssemblyTypes(allAssemblies).AsImplementedInterfaces().InstancePerLifetimeScope();
IContainer container = builder.Build();
TraceWriter.Trace(
String.Format(
"Found {0} assembl{1}. Configuring NServicebus to use Autofac preinitialized with these assembl{1} ..",
allAssemblies.Count(), allAssemblies.Count() == 1 ? "y" : "ies"));
Configure.With().UsingContainer(new AutofacObjectBuilder(container));
}
catch (Exception ex)
{
TraceWriter.Trace(String.Format("Exception occurred during initialization. Exception is: {0}\n{1}",
ex.Message, ex.StackTrace));
}
TraceWriter.Info("Initializing database en msmqsubscription .. ");
string databusBasePath = ConfigurationManager.AppSettings["DataBusBasePath"] ?? "";
TraceWriter.Trace(String.Format("Setting Databus's basepath to '{0}' ..", databusBasePath));
Configure.With().FileShareDataBus(databusBasePath);
Configure.With().MsmqSubscriptionStorage();
}
}
}
This code itself works however when the NServicebus initialisation continues an exception pops up:
Failed to execute installers: System.InvalidOperationException: Cannot configure properties for a type that hasn't been configured yet: NServiceBus.Un
icast.UnicastBus
at NServiceBus.ObjectBuilder.Autofac.AutofacObjectBuilder.ConfigureProperty(Type component, String property, Object value) in c:\BuildAgent\work\1b
05a2fea6e4cd32\src\NServiceBus.Core\ObjectBuilder\Autofac\AutofacObjectBuilder.cs:line 0
at NServiceBus.ObjectBuilder.Common.CommonObjectBuilder.ConfigureProperty[T](String propertyName, Object value) in c:\BuildAgent\work\1b05a2fea6e4c
d32\src\NServiceBus.Core\ObjectBuilder\Common\CommonObjectBuilder.cs:line 110
at NServiceBus.Unicast.Config.FinalizeUnicastBusConfiguration.RegisterMessageOwnersAndBusAddress(IEnumerable'1 knownMessages) in c:\BuildAgent\work
\1b05a2fea6e4cd32\src\NServiceBus.Core\Unicast\Config\FinalizeUnicastBusConfiguration.cs:line 57
at NServiceBus.Unicast.Config.FinalizeUnicastBusConfiguration.FinalizeConfiguration() in c:\BuildAgent\work\1b05a2fea6e4cd32\src\NServiceBus.Core\U
nicast\Config\FinalizeUnicastBusConfiguration.cs:line 24
at NServiceBus.Configure.<>c__DisplayClass23`1.b__20(Type t) in c:\BuildAgent\work\1b05a2fea6e4cd32\src\NServiceBus.Core\Configu
re.cs:line 555
at System.Collections.Generic.List'1.ForEach(Action'1 action)
at NServiceBus.Configure.ActivateAndInvoke[T](Action'1 action, Nullable'1 thresholdForWarning) in c:\BuildAgent\work\1b05a2fea6e4cd32\src\NServiceB
us.Core\Configure.cs:line 561
at NServiceBus.Configure.Initialize() in c:\BuildAgent\work\1b05a2fea6e4cd32\src\NServiceBus.Core\Configure.cs:line 361
at NServiceBus.Hosting.Windows.Installers.WindowsInstaller.RunInstall() in c:\BuildAgent\work\1b05a2fea6e4cd32\src\NServiceBus.Hosting.Windows\Inst
allers\WindowsInstaller.cs:line 38
No matter what I do, for some reason NServicebus's initialisation can't continue. It looks like my own Autofac container isn't properly configured in NServicebus by using the Configure.With().UsingContainer() method but I've tried all kind of options but every time it boils down to the same exception that is being thrown. Any ideas what I'm doing wrong here?
I found the answer to the problem. I scanned pretty much all DLL's to be included in Nservicebus's Autofac container. This also includes the DLL that contains the endpointconfig. Now when I exclude the DLL containing the endpoint, all goes well. I've now isolated pretty much all classes from the endpointconfig-DLL except for the message handlers. And now all goes well!
Try changing the code like this:
Configure configure = Configure.With();
// rest of container initialization
configure.AutofacBuilder(container);
// other stuff
configure.FileShareDataBus(databusBasePath);
configure.MsmqSubscriptionStorage();
and see if it works.

Servicestack (rest) incorrect WSDL with mono

I've written a simple self-hosted (in a ConsoleApplication) rest service with service stack 3.9.70.
using System;
using System.Runtime.Serialization;
// service stack support
using ServiceStack.ServiceHost;
using ServiceStack.WebHost.Endpoints;
namespace HelloWorldConsole
{
namespace DTO
{
[DataContract(Namespace = "http://localhost:8080/types")]
[Route("/hello/{Name}")]
class Hello : IReturn<HelloResponse>
{
[DataMember]
public string Name { get; set; }
}
[DataContract(Namespace = "http://localhost:8080/types")]
class HelloResponse
{
[DataMember]
public string Response { get; set; }
}
}
class HelloService : IService
{
public Object Any(DTO.Hello request)
{
return new DTO.HelloResponse { Response = "Hello " + request.Name };
}
}
public class HelloHost : AppHostHttpListenerBase
{
public HelloHost()
: base("Hello Service Self-Host",
typeof(HelloService).Assembly)
{ }
public override void Configure(Funq.Container container)
{
SetConfig(new EndpointHostConfig
{
DebugMode = true,
WsdlServiceNamespace = "http://localhost:8080/",
WsdlSoapActionNamespace = "http://localhost:8080/",
SoapServiceName = "HelloService"
});
}
}
class MainClass
{
public static void Main (string[] args)
{
string listenOn = "http://localhost:8080/";
HelloHost host = new HelloHost ();
host.Init ();
host.Start (listenOn);
Console.WriteLine ("AppHost created at {0} on {1}",
DateTime.Now, listenOn);
Console.ReadKey ();
}
}
}
Under Windows the generated WSDL is good, and if I try to create a client application and add a web reference to the soap service on localhost, I'm able to call Hello.
If I run the same code under Linux using Mono, the generated WSDL does not contain the types defined inside the DTO namespace. If I try to add a web service reference on a client, I'm not able to exploit hello method.
At this link I've read that by default the same ServiceStack Console app binary runs on both Windows/.NET and Mono/Linux as-is. I've tried to launch the binary under windows; the service runs but the generated WSDL is incorrect (without types defined in DTO namespace).
I use mono 2.10.8.1.
Does anyone have any suggestion?
I also have another question. If I use new version Servicestack last release (4.0.33) I'm not able to exploit soap endpoint.
At this link I've read that SOAP endpoints are not available when hosted on a HttpListener Host. Is it a feature introduced with new version 4.0?
Isn't there the posbility to exploit soap endpoints with servicestack releases higher than 3.9?
Any help is appreciated.
Mono has a weak and partial WCF/SOAP support which will fail to generate WSDLs for many non-trivial Service definitions. This situation may improve in the near future now that Microsoft has Open Sourced .NET server libraries, but in the interim I recommend avoiding Mono if you want to use SOAP.

How to consume a complex object from a sproc using WCF Data Services / OData?

Using WCF Data Services (and the latest Entity Framework), I want to return data from a stored procedure. The returned sproc fields do not match 1:1 any entity in my db, so I create a new complex type for it in the edmx model (rather than attaching an existing entity):
Right-click the *.edmx model / Add / Function Import
Select the sproc (returns three fields) - GetData
Click Get Column Information
Add the Function Import Name: GetData
Click Create new Complex Type - GetData_Result
In the service, I define:
[WebGet]
public List<GetData_Result> GetDataSproc()
{
PrimaryDBContext context = new PrimaryDBContext();
return context.GetData().ToList();
}
I created a quick console app to test, and added a reference to System.Data.Services and System.Data.Services.Client - this after running Install-Package EntityFramework -Pre, but the versions on the libraries are 4.0 and not 5.x.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Services.Client;
using ConsoleApplication1.PrimaryDBService;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataServiceContext context = new DataServiceContext(new Uri("http://localhost:50100/PrimaryDataService1.svc/"));
IEnumerable<GetData_Result> result = context.Execute<GetData_Result>(new Uri("http://localhost:50100/PrimaryDataService1.svc/GetDataSproc"));
foreach (GetData_Result w in result)
{
Console.WriteLine(w.ID + "\t" + w.WHO_TYPE_NAME + "\t" + w.CREATED_DATE);
}
Console.Read();
}
}
}
I didn't use the UriKind.Relative or anything else to complicate this.
When I navigate in the browser to the URL, I see data, but when I consume it in my console app, I get nothing at all.
Adding tracing to the mix:
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Information, ActivityTracing" propagateActivity="true">
<listeners>
<add name="traceListener" type="System.Diagnostics.XmlWriterTraceListener" initializeData="c:\temp\WebWCFDataService.svclog" />
</listeners>
</source>
</sources>
</system.diagnostics>
... and opening using the Microsoft Service Trace Viewer, I see two idential warnings:
Configuration evaluation context not found.
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>524312</EventID>
<Type>3</Type>
<SubType Name="Warning">0</SubType>
<Level>4</Level>
<TimeCreated SystemTime="2012-04-03T14:50:11.8355955Z" />
<Source Name="System.ServiceModel" />
<Correlation ActivityID="{66f1a241-2613-43dd-be0c-341149e37d30}" />
<Execution ProcessName="WebDev.WebServer40" ProcessID="5176" ThreadID="10" />
<Channel />
<Computer>MyComputer</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Warning">
<TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.EvaluationContextNotFound.aspx</TraceIdentifier>
<Description>Configuration evaluation context not found.</Description>
<AppDomain>fd28c9cc-1-129779382115645955</AppDomain>
</TraceRecord>
</DataItem>
</TraceData>
</ApplicationData>
</E2ETraceEvent>
So why am I able to see data from the browser, but not when consumed in my app?
-- UPDATE --
I downloaded the Microsoft WCF Data Services October 2011 CTP which exposed DataServiceProtocolVersion.V3, created a new host and client and referenced Microsoft.Data.Services.Client (v4.99.2.0). Now getting the following error on the client when trying iterate in the foreach loop:
There is a type mismatch between the client and the service. Type
'ConsoleApplication1.WcfDataServiceOctCTP1.GetDataSproc_Result' is an
entity type, but the type in the response payload does not represent
an entity type. Please ensure that types defined on the client match
the data model of the service, or update the service reference on the
client.
I tried the same thing by referencing the actual entity - works fine, so same issue.
Recap: I want to create a high-performing WCF service DAL (data access layer) that returns strongly-typed stored procedures. I initially used a "WCF Data Services" project to accomplish this. It seems as though it has its limitations, and after reviewing performance metrics of different ORM's, I ended up using Dapper for the data access inside a basic WCF Service.
I first created the *.edmx model and created the POCO for my sproc.
Next, I created a base BaseRepository and MiscDataRepository:
namespace WcfDataService.Repositories
{
public abstract class BaseRepository
{
protected static void SetIdentity<T>(IDbConnection connection, Action<T> setId)
{
dynamic identity = connection.Query("SELECT ##IDENTITY AS Id").Single();
T newId = (T)identity.Id;
setId(newId);
}
protected static IDbConnection OpenConnection()
{
IDbConnection connection = new SqlConnection(WebConfigurationManager.ConnectionStrings["PrimaryDBConnectionString"].ConnectionString);
connection.Open();
return connection;
}
}
}
namespace WcfDataService.Repositories
{
public class MiscDataRepository : BaseRepository
{
public IEnumerable<GetData_Result> SelectAllData()
{
using (IDbConnection connection = OpenConnection())
{
var theData = connection.Query<GetData_Result>("sprocs_GetData",
commandType: CommandType.StoredProcedure);
return theData;
}
}
}
}
The service class:
namespace WcfDataService
{
public class Service1 : IService1
{
private MiscDataRepository miscDataRepository;
public Service1()
: this(new MiscDataRepository())
{
}
public Service1(MiscDataRepository miscDataRepository)
{
this.miscDataRepository = miscDataRepository;
}
public IEnumerable<GetData_Result> GetData()
{
return miscDataRepository.SelectAllData();
}
}
}
... and then created a simple console application to display the data:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Service1Client client = new Service1Client();
IEnumerable<GetData_Result> result = client.GetData();
foreach (GetData_Result d in result)
{
Console.WriteLine(d.ID + "\t" + d.WHO_TYPE_NAME + "\t" + d.CREATED_DATE);
}
Console.Read();
}
}
}
I also accomplished this using PetaPOCO, which took much less time to setup than Dapper - a few lines of code:
namespace PetaPocoWcfDataService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
public class Service1 : IService1
{
public IEnumerable<GetData_Result> GetData()
{
var databaseContext = new PetaPoco.Database("PrimaryDBContext"); // using PetaPOCO for data access
databaseContext.EnableAutoSelect = false; // use the sproc to create the select statement
return databaseContext.Query<GetData_Result>("exec sproc_GetData");
}
}
}
I like how quick and simple it was to setup PetaPOCO, but using the repository pattern with Dapper will scale much better for an enterprise project.
It was also quite simple to create complex objects directly from the EDMX - for any stored procedure, then consume them.
For example, I created complex type return type called ProfileDetailsByID_Result based on the sq_mobile_profile_get_by_id sproc.
public ProfileDetailsByID_Result GetAllProfileDetailsByID(int profileID)
{
using (IDbConnection connection = OpenConnection("DatabaseConnectionString"))
{
try
{
var profile = connection.Query<ProfileDetailsByID_Result>("sq_mobile_profile_get_by_id",
new { profileid = profileID },
commandType: CommandType.StoredProcedure).FirstOrDefault();
return profile;
}
catch (Exception ex)
{
ErrorLogging.Instance.Fatal(ex); // use singleton for logging
return null;
}
}
}
So using Dapper along with some EDMX entities seems to be a nice quick way to get things going. I may be mistaken, but I'm not sure why Microsoft didn't think this all the way through - no support for complex types with OData.
--- UPDATE ---
So I finally got a response from Microsoft, when I raised the issue over a month ago:
We have done research on this and we have found that the Odata client
library doesn’t support complex types. Therefore, I regret to inform
you that there is not much that we can do to solve it.
*Optional: In order to obtain a solution for this issue, you have to use a Xml to Linq kind of approach to get the complex types.
Thank you very much for your understanding in this matter. Please let
me know if you have any questions. If we can be of any further
assistance, please let us know.
Best regards,
Seems odd.

JDEdwards XMLInterop

Wondering if anybody out there has any success in using the JDEdwards XMLInterop functionality. I've been using it for a while (with a simple PInvoke, will post code later). I'm looking to see if there's a better and/or more robust way.
Thanks.
As promised, here is the code for integrating with JDEdewards using XML. It's a webservice, but could be used as you see fit.
namespace YourNameSpace
{
/// <summary>
/// This webservice allows you to submit JDE XML CallObject requests via a c# webservice
/// </summary>
[WebService(Namespace = "http://WebSite.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class JdeBFService : System.Web.Services.WebService
{
private string _strServerName;
private UInt16 _intServerPort;
private Int16 _intServerTimeout;
public JdeBFService()
{
// Load JDE ServerName, Port, & Connection Timeout from the Web.config file.
_strServerName = ConfigurationManager.AppSettings["JdeServerName"];
_intServerPort = Convert.ToUInt16(ConfigurationManager.AppSettings["JdePort"], CultureInfo.InvariantCulture);
_intServerTimeout = Convert.ToInt16(ConfigurationManager.AppSettings["JdeTimeout"], CultureInfo.InvariantCulture);
}
/// <summary>
/// This webmethod allows you to submit an XML formatted jdeRequest document
/// that will call any Master Business Function referenced in the XML document
/// and return a response.
/// </summary>
/// <param name="Xml"> The jdeRequest XML document </param>
[WebMethod]
public XmlDocument JdeXmlRequest(XmlDocument xmlInput)
{
try
{
string outputXml = string.Empty;
outputXml = NativeMethods.JdeXmlRequest(xmlInput, _strServerName, _intServerPort, _intServerTimeout);
XmlDocument outputXmlDoc = new XmlDocument();
outputXmlDoc.LoadXml(outputXml);
return outputXmlDoc;
}
catch (Exception ex)
{
ErrorReporting.SendEmail(ex);
throw;
}
}
}
/// <summary>
/// This interop class uses pinvoke to call the JDE C++ dll. It only has one static function.
/// </summary>
/// <remarks>
/// This class calls the xmlinterop.dll which can be found in the B9/system/bin32 directory.
/// Copy the dll to the webservice project's /bin directory before running the project.
/// </remarks>
internal static class NativeMethods
{
[DllImport("xmlinterop.dll",
EntryPoint = "_jdeXMLRequest#20",
CharSet = CharSet.Auto,
ExactSpelling = false,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
private static extern IntPtr jdeXMLRequest([MarshalAs(UnmanagedType.LPWStr)] StringBuilder server, UInt16 port, Int32 timeout, [MarshalAs(UnmanagedType.LPStr)] StringBuilder buf, Int32 length);
public static string JdeXmlRequest(XmlDocument xmlInput, string strServerName, UInt16 intPort, Int32 intTimeout)
{
StringBuilder sbServerName = new StringBuilder(strServerName);
StringBuilder sbXML = new StringBuilder();
XmlWriter xWriter = XmlWriter.Create(sbXML);
xmlInput.WriteTo(xWriter);
xWriter.Close();
string result = Marshal.PtrToStringAnsi(jdeXMLRequest(sbServerName, intPort, intTimeout, sbXML, sbXML.Length));
return result;
}
}
}
You have to send it messages like the following one:
<jdeRequest type='callmethod' user='USER' pwd='PWD' environment='ENV'>
<callMethod name='GetEffectiveAddress' app='JdeWebRequest' runOnError='no'>
<params>
<param name='mnAddressNumber'>10000</param>
</params>
</callMethod>
</jdeRequest>
To anyone trying to do this, there are some dependencies to xmlinterop.dll.
you'll find these files on the fat client here ->c:\E910\system\bin32
this will create a 'thin client'
PSThread.dll
icudt32.dll
icui18n.dll
icuuc.dll
jdel.dll
jdeunicode.dll
libeay32.dll
msvcp71.dll
ssleay32.dll
ustdio.dll
xmlinterop.dll
I changed our JDE web service to use XML Interop after seeing this code, and we haven't had any stability problems since. Previously we were using the COM Connector, which exhibited regular communication failures (possibly a connection pooling issue?) and was a pain to install and configure correctly.
We did have issues when we attempted to use transactions, but if you're doing simple single business function calls this shouldn't be an problem.
Update: To elaborate on the transaction issues - if you're attempting to keep a transaction alive over multiple calls, AND the JDE application server is handling a modest number of concurrent calls, the xmlinterop calls start returning an 'XML response failed' message and the DB transaction is left open with no way to commit or rollback. It's possible tweaking the number of kernels might solve this, but personally, I'd always try to complete the transaction in a single call.