JDEdwards XMLInterop - jdedwards

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.

Related

Implement partial method to login to Soap Service

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

How to call GL.IssuePlugInEvent in Unity3d and pass parameters to the plugin side in C

I'm trying to write a plugin for Unity that passes information between the native side of android (but same may apply to any other dll or so library) and the unity side. The information I want to share is OpenGL related. On Unity docs it's specified that everything that has to run on the rendering thread must be called through The GL.IssuePlugInEvent function. This function though is quite strict on the parameters: it needs a to call a function with a specific signature:
Callback must be a native function of "void UNITY_INTERFACE_API UnityRenderingEvent(int eventId)" signature
and has only the additional parameter eventId.
How can I then pass back on the unity side information from the so library?
The Unity examples for creating a plugin and using IssuePluginEvent cover the case where you want/need to call directly a C function, and this function accepts only an eventId as parameter. If the reader comes from a C\C++ background and is not familiar with C#, it may be misleading. In fact, you can actually call a C# function with this simple trick, and inside that function call your C functions passing multiple parameters.
The steps are:
get a function pointer for a C# function (notice that the function will be static)
pass that function pointer to GL.IssuePluginEvent
inside your static C# function, based on the case of eventId, call the appropriate C function
void Update()
{
GL.IssuePluginEvent(RenderThreadHandlePtr, GL_INIT_EVENT); // example
}
/// <summary> Renders the event delegate described by eventID. </summary>
/// <param name="eventID"> Identifier for the event.</param>
private delegate void RenderEventDelegate(int eventID);
/// <summary> Handle of the render thread. </summary>
private static RenderEventDelegate RenderThreadHandle = new RenderEventDelegate(RunOnRenderThread);
/// <summary> The render thread handle pointer. </summary>
public static IntPtr RenderThreadHandlePtr = Marshal.GetFunctionPointerForDelegate(RenderThreadHandle);
public const int GL_INIT_EVENT = 0x0001;
public const int GL_DRAW_EVENT = 0x0002;
/// <summary> Executes the 'on render thread' operation. </summary>
/// <param name="eventID"> Identifier for the event.</param>
[MonoPInvokeCallback(typeof(RenderEventDelegate))]
private static void RunOnRenderThread(int eventID)
{
switch (eventID)
{
case GL_INIT_EVENT:
glInit(par1, par2, par3); // C function with 3 parameters
break;
case GL_DRAW_EVENT:
glStep();
GL.InvalidateState();
break;
}
}
[DllImport("hello-jni")]
private static extern void glInit(int par1, int par2, int par3);
[DllImport("hello-jni")]
private static extern void glStep();

Using Mongo / BSON ObjectId with Parse Server

I currently have a project that's being migrated away from Parse Server but needs to maintain backwards compatibility.
Since Parse Server generates it's own object Ids, rather than using Mongo's I'd like to know:
How does Parse Server generate it's objectIds?
and why does it do this when MongoDB has great objectId generation natively?
Will parse be able to work with objects with non-Parse generated IDs?
An example:
_id: "LvIzxv5spL" // created by Parse Server
_id: "507f1f77bcf86cd799439011" // BSON.ObjectId created by MongoDB directly
Thanks for reading, any help would be greatly appreciated!
Cheers :)
edited for brevity
I found how the Parse Server generates a new Id on creation here.
The comment documentation above states that the below function is getting called to generate a new id for Parse Server.
I still don't know why it has to create an id in its way rather than using Mongo's native one. It shall help to remove Parse Server dependency easily.
Please find the code below in c# that I am using to generate a new id like the parse server. I have not tested it with all aspects but, I think it will pass most if not all test cases of others.
/// <summary>
/// Randoms the string.
/// </summary>
/// <param name="length">The length.</param>
/// <returns></returns>
public static string RandomString(int length)
{
string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789";
StringBuilder res = new();
using (RNGCryptoServiceProvider rng = new())
{
byte[] uintBuffer = new byte[sizeof(uint)];
while (length-- > 0)
{
rng.GetBytes(uintBuffer);
uint num = BitConverter.ToUInt32(uintBuffer, 0);
res.Append(chars[(int)(num % (uint)chars.Length)]);
}
}
return res.ToString();
}
/// <summary>
/// Gets the new object identifier.
/// </summary>
/// <param name="size">The size.</param>
/// <returns></returns>
public static string GetNewObjectId(int size = 10)
{
return RandomString(size);
}
I hope this sample code helps recreate the logic in your preferred language.

Cast error on SQLDataReader

My site is using enterprise library v 5.0. Mainly the DAAB. Some functions such as executescalar, executedataset are working as expected. The problems appear when I start to use Readers
I have this function in my includes class:
Public Function AssignedDepartmentDetail(ByVal Did As Integer) As SqlDataReader
Dim reader As SqlDataReader
Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = #did")
db.AddInParameter(Command, "#did", Data.DbType.Int32, Did)
reader = db.ExecuteReader(Command)
reader.Read()
Return reader
End Function
This is called from my aspx.vb like so:
reader = includes.AssignedDepartmentDetail(Did)
If reader.HasRows Then
TheModule = reader("templatefilename")
PageID = reader("id")
Else
TheModule = "#"
End If
This gives the following error on db.ExecuteReader line:
Unable to cast object of type 'Microsoft.Practices.EnterpriseLibrary.Data.RefCountingDataReader' to type 'System.Data.SqlClient.SqlDataReader'.
Can anyone shed any light on how I go about getting this working. Will I always run into problems when dealing with readers via entlib?
I would be careful with this implementation.
There is a thread on the Enterprise Library Codeplex site that explains the backgound for this:
http://entlib.codeplex.com/Thread/View.aspx?ThreadId=212973
Chris Tavares explains that it's not good to just return the .InnerReader, because then the connection tracking by Enterprise Library is thrown off (his response from May 20, 5:39PM):
"That approach will completely screw up your connection management. The whole reason for the wrapper is so that we could execute extra code to clean stuff up at dispose time. Grabbing the inner reader and throwing out the outer will leak connections!
"
So yes, this is a bit of a pain to deal with, we're in the same situation.
Regards,
Mike
ExecuteReader in Enterprise Library wraps IDataReader into RefCountingDataReader that as SqlDataReader implements IDataReader interface.
RefCountingDataReader has InnerReader property that you can cast to SqlDataReader. The sample below is in C# but you can easily convert it to VB.NET.
SqlDataReader reader;
reader = ((RefCountingDataReader)db.ExecuteReader(command)).InnerReader as SqlDataReader;
if (reader != null)
reader.Read();
return reader;
Hope it helps
I am having leaking connections because all my DA methods require a SqlDataReader.
Now I have to return the inner RefCountingDataReader and can never close the outer reader.
The old Enterprise Library was working fine with returning a SqlDataReader.
I've taken into account the comments and code posted by ctavars at http://entlib.codeplex.com/discussions/212973 and http://entlib.codeplex.com/discussions/211288, resulting in the following generic approach to obtaining an SQL data reader.
In general you use IDataReader in the using statement, then use that reference directly when you can. Call AsSqlDataReader on it when you need something SQL-specific.
Add this extension class somewhere:
/// <summary>
/// Obtains an <see cref="SqlDataReader"/> from less derived data readers in Enterprise Library
/// </summary>
/// <remarks>
/// See http://entlib.codeplex.com/discussions/212973 and http://entlib.codeplex.com/discussions/211288
/// for a discussion of why this is necessary
/// </remarks>
public static class SqlDataReaderExtension
{
/// <summary>
/// Allows the internal <see cref="SqlDataReader"/> of a <see cref="RefCountingDataReader"/> to be accessed safely
/// </summary>
/// <remarks>
/// To ensure correct use, the returned reference must not be retained and used outside the scope of the input
/// reference. This is so that the reference counting does not get broken. In practice this means calling this method
/// on the base reader every time a reference to it is required.
/// </remarks>
public static SqlDataReader AsSqlDataReader(this RefCountingDataReader reader)
{
return (SqlDataReader)(reader.InnerReader);
}
/// <summary>
/// Allows the internal <see cref="SqlDataReader"/> of a <see cref="IDataReader"/> to be accessed safely
/// </summary>
/// <remarks>
/// To ensure correct use, the returned reference must not be retained and used outside the scope of the input
/// reference. This is so that the reference counting does not get broken. In practice this means calling this method
/// on the base reader every time a reference to it is required.
/// </remarks>
public static SqlDataReader AsSqlDataReader(this IDataReader reader)
{
return (SqlDataReader)(((RefCountingDataReader)(reader)).InnerReader);
}
}
... then to read data with an SQLReader, do something like this:
using (IDataReader reader = db.ExecuteReader(command))
{
while (reader.Read())
{
reader.GetInt32(reader.GetOrdinal("SomeColumn")),
reader.GetInt32(reader.GetOrdinal("SomeOtherColumn")),
reader.GetInt32(reader.GetOrdinal("SomeFurtherColumn")),
// Obtain the SQL data reader each time it is used
// (Note that GetDateTimeOffset is not on the standard IDataReader)
reader.AsSqlDataReader().GetDateTimeOffset(reader.GetOrdinal("SQLSpecificColumn"))
reader.AsSqlDataReader().GetDateTimeOffset(reader.GetOrdinal("AnotherSQLSpecificColumn"))
reader.GetString(reader.GetOrdinal("SomeAdditionalColumn"))
}
}
I think I have a working solution.
enter code here
' Create the Database object, using the default database service. The
' default database service is determined through configuration.
Dim db As Microsoft.Practices.EnterpriseLibrary.Data.Database = EnterpriseLibraryContainer.Current.GetInstance(Of Microsoft.Practices.EnterpriseLibrary.Data.Database)(DatabaseName)
Dim dbCommand As DbCommand
dbCommand = db.GetStoredProcCommand(StoredProcedureName)
'create a new database connection based on the enterprise library database connection
Dim dbConnection As System.Data.Common.DbConnection
dbConnection = db.CreateConnection
dbConnection.Open()
'set the dbCommand equal to the open dbConnection
dbCommand.Connection = dbConnection
'return a ADO sqlDatareader but still managed by the EnterpriseLibrary
Return dbCommand.ExecuteReader(CommandBehavior.CloseConnection)
You should use the interface, not the concrete class.
Public Function AssignedDepartmentDetail(ByVal Did As Integer) As IDataReader
Dim reader As IDataReader
Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = #did")
db.AddInParameter(Command, "#did", Data.DbType.Int32, Did)
reader = db.ExecuteReader(Command)
reader.Read()
Return reader
End Function
and the usage. Personally, I would never use a datareader in a presentation layer page, but to each his/her own I guess.
Private Const TemplateFileName_Select_Column_Ordinal As Integer = 0
Private Const Id_Select_Column_Ordinal As Integer = 1
Private Sub DoSomething()
dim reader as IDataReader
reader = includes.AssignedDepartmentDetail(Did)
If reader.HasRows Then
TheModule = reader(TemplateFileName_Select_Column_Ordinal)
PageID = reader(Id_Select_Column_Ordinal)
Else
TheModule = "#"
reader.Close() ''Dude, close your reader(s)
End If

Opening an SQL CE file at runtime with Entity Framework 4

I am getting started with Entity Framework 4, and I an creating a demo app as a learning exercise. The app is a simple documentation builder, and it uses a SQL CE store. Each documentation project has its own SQL CE data file, and the user opens one of these files to work on a project.
The EDM is very simple. A documentation project is comprised of a list of subjects, each of which has a title, a description, and zero or more notes. So, my entities are Subject, which contains Title and Text properties, and Note, which has Title and Text properties. There is a one-to-many association from Subject to Note.
I am trying to figure out how to open an SQL CE data file. A data file must match the schema of the SQL CE database created by EF4's Create Database Wizard, and I will implement a New File use case elsewhere in the app to implement that requirement. Right now, I am just trying to get an existing data file open in the app.
I have reproduced my existing 'Open File' code below. I have set it up as a static service class called File Services. The code isn't working quite yet, but there is enough to show what I am trying to do. I am trying to hold the ObjectContext open for entity object updates, disposing it when the file is closed.
So, here is my question: Am I on the right track? What do I need to change to make this code work with EF4? Is there an example of how to do this properly?
Thanks for your help.
My existing code:
public static class FileServices
{
#region Private Fields
// Member variables
private static EntityConnection m_EntityConnection;
private static ObjectContext m_ObjectContext;
#endregion
#region Service Methods
/// <summary>
/// Opens an SQL CE database file.
/// </summary>
/// <param name="filePath">The path to the SQL CE file to open.</param>
/// <param name="viewModel">The main window view model.</param>
public static void OpenSqlCeFile(string filePath, MainWindowViewModel viewModel)
{
// Configure an SQL CE connection string
var sqlCeConnectionString = string.Format("Data Source={0}", filePath);
// Configure an EDM connection string
var builder = new EntityConnectionStringBuilder();
builder.Metadata = "res://*/EF4Model.csdl|res://*/EF4Model.ssdl|res://*/EF4Model.msl";
builder.Provider = "System.Data.SqlServerCe";
builder.ProviderConnectionString = sqlCeConnectionString;
var entityConnectionString = builder.ToString();
// Connect to the model
m_EntityConnection = new EntityConnection(entityConnectionString);
m_EntityConnection.Open();
// Create an object context
m_ObjectContext = new Model1Container();
// Get all Subject data
IQueryable<Subject> subjects = from s in Subjects orderby s.Title select s;
// Set view model data property
viewModel.Subjects = new ObservableCollection<Subject>(subjects);
}
/// <summary>
/// Closes an SQL CE database file.
/// </summary>
public static void CloseSqlCeFile()
{
m_EntityConnection.Close();
m_ObjectContext.Dispose();
}
#endregion
}
Here is the answer. I simplified my code and ran it on simpler EDM model, Disney Characters. Model has two entities, Character and Child, with a 1:* association between Character and Child. Children are character's kids--pretty simple stuff. I wrote the demo as a console app to keep it as simple as possible.
Complete code in Program.cs is as follows:
class Program
{
static void Main(string[] args)
{
/* See http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/8a89a728-6c8d-4734-98cb-11b196ba11fd */
// Configure an SQL CE connection string
var filePath = #"D:\Users\dcveeneman\Documents\Visual Studio 2010\Demos\SqlCeEf4Demo\SqlCeEf4Demo\DisneyChars.sdf";
var sqlCeConnectionString = string.Format("Data Source={0}", filePath);
// Create an EDM connection
var builder = new EntityConnectionStringBuilder();
builder.Metadata = "res://*/DisneyChars.csdl|res://*/DisneyChars.ssdl|res://*/DisneyChars.msl";
builder.Provider = "System.Data.SqlServerCe.3.5";
builder.ProviderConnectionString = sqlCeConnectionString;
var edmConnectionString = builder.ToString();
var edmConnection = new EntityConnection(edmConnectionString);
// Build and query an ObjectContext
using (var context = new DisneyCharsContainer(edmConnection))
{
var chars = context.Characters;
foreach(var character in chars)
{
Console.WriteLine("Character name: {0}", character.Name);
foreach(var child in character.Children)
{
Console.WriteLine("Child name: {0}", child.Name);
}
}
Console.ReadLine();
}
}
}
Link at the top of the code is to a forum thread that I used to write the code.
Here is the walkthrough: First, create a database connection. Since I am using SQL CE, I don't have a connection string builder--the connection string is simply a path, so I don't need one. Then I use an EntityConnectionStringBuilder to build an entity connection string, and then I use that to build an EntityConnection. Finally, I pass the connection to the constructor for my ObjectContext. I can then use the ObjectContext to query the EDM.
Finding / opening a SQL Server CE database is, for some weird reason, hard to do. Make sure you can make any kind of connection to the DB at all before trying to get it to work with the EF.