Hey Rick,
I'm using the proxy generator to do a SOAP integration my clients are wanting. It's going well until a method returns back a dataset. I can't figure out how to access the data in the object that's returned to VFP.
I've used Fidler to see the exact XML that's returned and it doesn't look crazy so I tried to call oTSO.oBridge.dataSetToCursors(oResp)
and I'm getting an error:
My full code looks like:
DO tsoUnits
LOCAL oTSO, oResp
oTSO = CREATEOBJECT("TSOUnits","V4")
oResp = oTSO.getUnitList(oToken.token)
IF !EMPTY(oTSO.cErrorMsg)
oRet.errorMsg = oTSO.cErrorMsg
RETURN oRet
ENDIF
In the command window I was doing the oTSO.oBridge.datasettocursors(oResp)
Is this a known issue or would you recommend a better way to get to the data in the data set that's returned? Is there a way to get back the raw XML as a string I can manipulate?
I've also tried to do things like: oHold = oTSO.oBridge.getPropertyEx(oResp, 'Table[0]')
and that errors out on me as well. I can get you the raw XML string (that I captured in Fiddler) if that helps point us in the right direction.
Here's the code in the C# file the proxy generator created if that helps?
/// <remarks/>
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("TSOAPI/GetUnitList", RequestNamespace="TSOAPI", ResponseNamespace="TSOAPI", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public System.Data.DataTable GetUnitList(string token) {
object[] results = this.Invoke("GetUnitList", new object[] {
token});
return ((System.Data.DataTable)(results[0]));
}
Old version?
As to DataSet/Table access it's possible but it's freaking ugly due to the multi-dimensional collections used.
There was a recent discussion regarding this here:
Access .NET DataSet from Dotnet in FoxPro
In general it's better to use the XML Adapter conversions. You can use DataSetToCursors(), but in your case you have a DataTable
so that likely won't work.
Also offhand not sure if that particular function has made it into the Proxy tooling - I think that might be newer.
+++ Rick ---
Hey Rick,
You gave me an 'experimental' wwDotNetBridge about a week ago in relation to the bug in serializing dateTimes in cursors so I don't think it's an old version problem.
From the article you linked, I was able to get it working. However, I was wondering if there's a way to get the properties by name instead of by index?
Basically instead of
oTSO.oBridge.getPropertyEx(oRow, 'ItemArray[0]')
I want to do something like
oTSO.oBridge.getPropertyEx(oRow, 'ItemArray.ID')
but it keeps erroring out on my to do the .ID (I know ID is one of the properties returned). Is there a way to do this?
Not that I know of...
+++ Rick ---
Fair enough. Figured it was worth asking. The product we're integrating in with has a history of changing the order of fields returned when they add a new one, which is why I'd rather go by the name than the index but I'll make it work.
Thanks for your help.
The problem is the DataSet and DataRow are explicitly marked to be [ComVisible(false)]
which means none of the interfaces automatically work over COM. Everything has to go through Reflection() which makes things much slower. The only thing that can access the data row value is the overridden indexer which is difficult to do with Reflection. There probably is a way but I'm not going to waste time on that because this is very very rare. These days everything lives in typed collections or string dictionaries which is more explicit and does work as you'd expect.
And just to clarify I would not recommend using direct table/dataset access via properties. It's bound to be considerably slower than the serialization except for very small tables due to the chatty nature of having to make a COM call + Reflection for each and every value retrieved. Making the one call to have the XML parsed is almost always quicker even though it seems very expensive.
+++ Rick ---
Ok so I took another look at how this works in wwdotnetbridge
. It can deal with indexers but the problem with DataRow is that it's a multi-dimensional array like structure. It's not really an array or collection itself, but rather a plain object with an indexer. Normally you can do:
lcResult = loBridge.GetProperty(someObject,'someCollection["value"]')
And that works. For a DataRow though you have to do:
lcName = loBridge.GetProperty(loRow,'["name"]')
and it turns out that didn't work because the old code assumed there was a base property that identifies the collection/array (ie. someCollection
in the first example).
With a little bit of tweaking I've made that work in the wwDotnetBridge.dll
so you can now do:
do wwDotNetBridge
LOCAL loBridge as wwDotNetBridge
loBridge = GetwwDotnetBridge()
loNet = loBridge.CreateInstance("Westwind.WebConnection.TypePassingTests")
? loNet
loDs = loNet.GetDataSet()
? loDs
? loBridge.cErRORMSG
loTable = loBridge.GetProperty(loDs,"Tables[0]")
loRows = loBridge.GetProperty(loTable, "Rows")
? loBridge.ToString(loRows)
lnRecs = loBridge.GetProperty(loRows,"Count")
? lnRecs
FOR lnX = 0 TO lnRecs - 1
loRow = loBridge.GetPropertyEx(loTable,"Rows[" + TRANS(lnX) + "]")
? loBridge.GetProperty(loRow,'["name"]')
? loBridge.GetProperty(loRow,'["company"]')
? loBridge.GetProperty(loRow,'["entered"]')
ENDFOR
You can grab the WebConnection Experimental file again.
+++ Rick ---
Thanks Rick! I'll give it a try.
Also just to clarify you should also be able to do:
loNet = loBridge.CreateInstance("Westwind.WebConnection.TypePassingTests")
loDs = loNet.GetDataSet()
loBridge.DataseToCursors(loDs)
BROWSE
This will create Cursors for each for the DataTable
objects in a DataSet, which accomplishes essentially what the other code does, but likely is quicker and more efficient especially if you need to use all the data. Individual DataRow field access might be useful if you need to access just a few fields of a very wide table perhaps.
+++ Rick ---