WCF server method - returning DataSet kills output parameters - wcf

Looking for some collective wisdom. Here is the situation. This is using Visual Studio 2008.
We have a simple WCF contract exposing the following method:
DataSet ExecuteQuery (out string someStuff);
This method is implemented as:
public DataSet ExecuteQuery (out string someStuff)
{
someStuff = "abc";
return new DataSet(); // simplified
}
Client side proxy is generated by svcutil, and appears to be correct.
Problem:
someStuff is always null on the client side.
Observations:
Same problem appears on our build machine.
Returning a string instead of DataSet makes client proxy receive proper value of out parameter:
public string ExecuteQuery(out string someStuff) ...
Removing the client proxy completely and regenerating it doesn't help.
Client definitely receives both DataSet and the "out" string, as seen in its trace log:
<ExecuteQueryResponse xmlns="http://tempuri.org/">
<ExecuteQueryResult>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded"></xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"></diffgr:diffgram>
</ExecuteQueryResult>
<someStuff>abc</someStuff>
</ExecuteQueryResponse>
Here is how the client proxy generates the code:
[System.ServiceModel.OperationContractAttribute
(Action="http://tempuri.org/IQueryContract/ExecuteQuery",
ReplyAction="http://tempuri.org/IQueryContract/ExecuteQueryResponse")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
System.Data.DataSet ExecuteQuery(out string someStuff);
We use netTcpBinding
Do we need to do anything special in order to return DataSet and out parameters? Anyone has come across anything similar?
Thanks!!!!

WCF, as defined by SOA, must not transport an object (state and behavior), only the state and strucuture of an object. BinaryFormatter, and SoapFormatter are inadequate for SOA because:
1) Requires both ends to share type
2) cannot be used for contracts
3) both formatters require streams
DataContractSerializer only shares types not contracts. It adds support for XML readers and writers, only captures state according to the schema, and deos not support IFormatter.
All of which I took from my WCF course that I took from Juval Lowry :)
Richard

I had [XmlSerializerFormat] at the top of my contract interface. Removing it fixed the problem, as WCF starts using the default [DataSerializerFormat]. Thanks Darin for point it out.
Still not sure why XmlSerializerFormat wouldn't work.

Related

Creating a Microsoft Access database connection to a REST-based API

I am trying to, in Microsoft Access 2013, create a real-time link to data provided by a REST-based API (this API, to be specific). The ultimate goal is for the data to be available in a query as if it were a local database.
How can this be accomplished? Specifically, I am struggling with how to have Access call the API upon request. The only way I can think to achieve a similar result is to write a script that pulls the entire database via the API and translates it to an Access-readable format, then run that script at set intervals. But I'd really like to find a solution that works in real time, even if it's a skosh slower than locally caching the database.
Since a call to a RESTful Web Service is really just a specific kind of HTTP request you could, at the very least, use the Microsoft XML library to shoot an HTTP request to the web service and parse whatever it returns. For example, when I run the following VBA code
' VBA project Reference required:
' Microsoft XML, v3.0
Dim httpReq As New MSXML2.ServerXMLHTTP
httpReq.Open "GET", "http://whois.arin.net/rest/poc/KOSTE-ARIN", False
httpReq.send
Dim response As String
response = httpReq.responseText
Debug.Print response
the string variable response contains the XML response to my request. It looks like this (after reformatting for readability):
<?xml version='1.0'?>
<?xml-stylesheet type='text/xsl' href='http://whois.arin.net/xsl/website.xsl' ?>
<poc xmlns="http://www.arin.net/whoisrws/core/v1" xmlns:ns2="http://www.arin.net/whoisrws/rdns/v1"
xmlns:ns3="http://www.arin.net/whoisrws/netref/v2" termsOfUse="https://www.arin.net/whois_tou.html"
inaccuracyReportUrl="http://www.arin.net/public/whoisinaccuracy/index.xhtml">
<registrationDate>2009-10-02T11:54:45-04:00</registrationDate>
<ref>http://whois.arin.net/rest/poc/KOSTE-ARIN</ref>
<city>Chantilly</city>
<companyName>ARIN</companyName>
<iso3166-1>
<code2>US</code2>
<code3>USA</code3>
<name>UNITED STATES</name>
<e164>1</e164>
</iso3166-1>
<firstName>Mark</firstName>
<handle>KOSTE-ARIN</handle>
<lastName>Kosters</lastName>
<emails>
<email>markk#kosters.net</email>
<email>markk#bjmk.com</email>
</emails>
<resources termsOfUse="https://www.arin.net/whois_tou.html"
inaccuracyReportUrl="http://www.arin.net/public/whoisinaccuracy/index.xhtml">
<limitExceeded limit="256">false</limitExceeded>
</resources>
<phones>
<phone>
<number>+ 1-703-227-9870</number>
<type>
<description>Office</description>
<code>O</code>
</type>
</phone>
</phones>
<postalCode>20151</postalCode>
<comment>
<line number="0">I'm really MAK21-ARIN</line>
</comment>
<iso3166-2>VA</iso3166-2>
<streetAddress>
<line number="0">3635 Concorde Parkway</line>
</streetAddress>
<updateDate>2015-05-26T11:36:55-04:00</updateDate>
</poc>
What gets returned by your web service might look somewhat different. Or, as in the case of the ARIN whois RWS above, you may have several data formats from which to choose; XML was just the default. I could have requested a plain text response using
httpReq.Open "GET", "http://whois.arin.net/rest/poc/KOSTE-ARIN.txt", False
in which case response would contain
#
# ARIN WHOIS data and services are subject to the Terms of Use
# available at: https://www.arin.net/whois_tou.html
#
Name: Kosters, Mark
Handle: KOSTE-ARIN
Company: ARIN
Address: 3635 Concorde Parkway
City: Chantilly
StateProv: VA
PostalCode: 20151
Country: US
RegDate: 2009-10-02
Updated: 2015-05-26
Comment: I'm really MAK21-ARIN
Phone: +1-703-227-9870 (Office)
Email: markk#bjmk.com
Email: markk#kosters.net
Ref: http://whois.arin.net/rest/poc/KOSTE-ARIN
#
# ARIN WHOIS data and services are subject to the Terms of Use
# available at: https://www.arin.net/whois_tou.html
#

For a BizTalk WCF-SQL adapter in Solicit-Response Send port. How to let adapter handle the response data like XmlPolling?

For a SP using For Xml returns a xml as response, If this sp was consumed by a WCF receive location, we can specify the Polling as XmlPolling to let adapter keep the responsed xml "as-is". But for a wcf-sql Solicit-Response Send port, It seems no same way to do it. Currently, the best result I can get is let adapter treat the xml response as a CDATA. Like below:
<usp_MySPResponse xmlns="http://schemas.microsoft.com/Sql/2008/05/TypedProcedures/dbo">
<StoredProcedureResultSet0>
<StoredProcedureResultSet0 xmlns="http://schemas.microsoft.com/Sql/2008/05/ProceduresResultSets/dbo/usp_MySP">
<UnNamedColumn0><![CDATA[ <!-- The XML content of sp returned goes here -->
....
Assuming the Stored Procedure uses the FOR XML clause, there are two binding properties you must set for the result to be recognized as Xml:
XmlStoredProcedureRootNodeName
XmlStoredProcedureRootNodeNamespace
It's these two properties that tell the Adapter it's a FOR XML Stored Procedure. These are essentially the same as the legacy SQL Adapter.
Details here: http://msdn.microsoft.com/en-us/library/dd787898.aspx
There is no way around this so your BizTalk Schema must match these values.
Reading the link https://learn.microsoft.com/en-us/biztalk/adapters-and-accelerators/adapter-sql/execute-stored-procedures-having-a-for-xml-clause-in-sql-server-using-biztalk carefully, it says that the request schema must be generated selecting from "Procedures", undeniably not "TypedProcedures". The request message will therefore be in the namespace http://schemas.microsoft.com/Sql/2008/05/Procedures/dbo which is not explicitly mentioned in
https://learn.microsoft.com/en-us/biztalk/adapters-and-accelerators/adapter-sql/message-schemas-for-procedures-and-functions but the action for a "for xml"-typed procedure is specified there. To summarize: A request message is thus:
<YOURSPNAME xmlns="http://schemas.microsoft.com/Sql/2008/05/Procedures/dbo" />
and the port configuration action header is
<BtsActionMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Operation Name="YOURSPNAME" Action="XmlProcedure/dbo/YOURSPNAME" />
</BtsActionMapping>
Note the discrepancy between the request message's namespace and the operation action, only the action has "Xml" in it. And don't forget to specify the (FOR XML) binding parameters, as answered by Johns-305.

WCF Service ref string param that accepts XML?

I'm writing a WCF service that receives events. It's to an agreed standard so I've got to stick to the service definition, and I don't control the data the clients send. Again this is to an agreed standard although the data can vary.
Here's one of the methods on my service:
complexType ErrorEvent(int requestId, complexType returnValue, ref string errorInfo)
Clients send XML in the errorInfo string that my function will manipulate and return.
The data I get is like this (full SOAP request):
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ErrorEvent xmlns="http://blah">
<requestId>1</requestId>
<returnValue>
<returnCode>0</returnCode>
</returnValue>
<errorInfo>
<ErrorMessage>An error message</ErrorMessage>
<DefaultTask><!-- Complex data --></DefaultTask>
<Task><!-- Complex data --></Task>
<Task><!-- Complex data --></Task>
<Task><!-- Complex data --></Task>
<ExtraMessage>hello</ExtraMessage>
<ExtraMessage>world</ExtraMessage>
</errorInfo>
</ErrorEvent>
</s:Body>
</s:Envelope>
However, when I try and run this I get this error (edited):
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter errorInfo. The InnerException message was 'There was an error deserializing the object of type System.String. End element 'errorInfo' from namespace '' expected. Found element 'ErrorMessage' from namespace ''.
So my question is, is there any way I achieve what I want to do without altering the signature of my method? For example adding attributes to my service etc? Or do I need to intercept the message?
Thanks for any pointers.
Does it have to be passed as a string? You know you can also receive XmlElement and XElements in WCF?
Are you generating the SOAP request yourself? Could you use a CDATA section, i.e.
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ErrorEvent xmlns="http://blah">
<requestId>1</requestId>
<returnValue>
<returnCode>0</returnCode>
</returnValue>
<errorInfo>
<![CDATA[<ErrorMessage>An error message</ErrorMessage>
<DefaultTask><!-- Complex data --></DefaultTask>
<Task><!-- Complex data --></Task>
<Task><!-- Complex data --></Task>
<Task><!-- Complex data --></Task>
<ExtraMessage>hello</ExtraMessage>
<ExtraMessage>world</ExtraMessage>]]>
</errorInfo>
</ErrorEvent>
</s:Body>
</s:Envelope>
I didn't find a way to make this work and as #luksan says, it's a misuse by a client to send unescaped XML to a string parameter.
The workaround I adopted was to make a class that implements IClientMessageInspector, IDispatchMessageInspector and IEndpointBehavior to intercept, check and modify any messages that were incorrect.
If I could have changed the interface, another workaround would have been to accept XmlNode[] instead of string.

ksoap2 and WCF complex types

I'm sending this request to my WCF webservice with a Android client using ksoap2:
<v:Envelope xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:d="http://www.w3.org/2001/XMLSchema" xmlns:c="http://schemas.xmlsoap.org/soap/encoding/" xmlns:v="http://schemas.xmlsoap.org/soap/envelope/"><v:Header />
<v:Body>
<HelloComplex xmlns="http://tempuri.org/" id="o0" c:root="1">
<complex i:type="n0:SampleComplexType" xmlns:n0="http://tempuri.org/">
<Value i:type="d:string">Hello!</Value>
</complex>
</HelloComplex>
</v:Body>
</v:Envelope>
and receive back this:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:DeserializationFailed</faultcode>
<faultstring xml:lang="pt-BR">
The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://tempuri.org/:complex. The InnerException message was 'Error in line 1 position 363. Element 'http://tempuri.org/:complex' contains data from a type that maps to the name 'http://tempuri.org/:SampleComplexType'. The deserializer has no knowledge of any type that maps to this name. Consider using a DataContractResolver or add the type corresponding to 'SampleComplexType' to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding it to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
</faultstring>
</s:Fault>
</s:Body>
</s:Envelope>
But when my other application (AspNew MVC) call this :
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<HelloComplex xmlns="http://tempuri.org/">
<complex xmlns:a="http://schemas.datacontract.org/2004/07/IssueCenter.Core" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Value>C#VALUE!</a:Value>
</complex>
</HelloComplex>
</s:Body>
</s:Envelope>
ant it works.
What can I do to fix my Android client requests?
Notes: I know about the namespace diference and I already try to make the same.
The service doesn't know how to deal with the "ComplexType" type attribute in the XML, so you basically need to tell the service-side serializer what it means. You can do it in a few ways: either you add a DataContractResolver that tells the serializer what this unknown type ID means, or you can add the type (if it exists on the service side) to the list of "known types", so that the service knows exactly what to do with it. I think you should use a DataContractResolver.
In the version 4.0 of the framework, WCF introduced the data contract resolver. Instead of defining the set of known types “statically” (e.g. by specifying the types directly in a KnownTypeAttribute like you did until .NET 4), the data contract resolver provides some hooks which allow you to specify, at the moment when the object is being serialized or deserialized, a mapping between the CLR type and the name / namespace in the XML which will be used to represent this “unknown” type. To do this, you just inherit from the DataContractResolver class and provide your mapping logic.
I think this is the better solution for you, because you likely don't want to create a dummy type on the server side just to deal with this problem, which is what you'd have to do if you were to use KnownTypeAttribute. Only thing to remember is that a resolver makes the serialization slower than using the “standard” known types feature (since known types are static, they can be cached and the calls don’t need to be made all the time), so be aware that when using the resolver, the additional functionality comes at a price in terms of execution time.

Why doesn't my ChannelFactory see my endpoint configuration?

I've been following Miguel Castro's excellent article on WCF here and it's all working nicely, except that I have the following code
public AdminClient()
{
ChannelFactory<IProductAdmin> factory = new ChannelFactory<IProductAdmin>();
productAdminChannel = factory.CreateChannel();
}
In my app.config file, I have the following configuration:
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:8002/ProductBrowser"
binding="netTcpBinding"
contract="Contracts.IProductAdmin" />
</client>
</system.serviceModel>
But, when I run the constructor for AdminClient I get an exception saying that the endpoint isn't defined. However, if I change my configuration to give the endpoint a name, and then create the factory as follows, it works.
public AdminClient()
{
var fac = new ChannelFactory<IProductAdmin>("admin");
productAdminChannel = fac.CreateChannel();
}
<system.serviceModel>
<client>
<endpoint name="admin"
address="net.tcp://localhost:8002/ProductBrowser"
binding="netTcpBinding"
contract="Contracts.IProductAdmin" />
</client>
</system.serviceModel>
I'd love an explanation for this. The documentation in MSDN isn't much help...
Use "*" to use the first qualifying endpoint.
public AdminClient()
{
ChannelFactory<IProductAdmin> factory
= new ChannelFactory<IProductAdmin>("*");
productAdminChannel = factory.CreateChannel();
}
MSDN Example
You need to specify the endpoint name, because you can have many endpoints for the same type of contract. (For instance a service being deployed on one tcp and one http ws endpoint). Microsoft could have of course built something in WCF to check if there is only one client specified for the contract interface, but that wouldn't have been very consistent. (that it would work if there is only one endpoint specified for the contract). When you would add another endpoint for the same contract later, the code would break in that case.
You can get away without specifying endpoint name on service side.
For client side, you need to specify the name, because you may be connecting to multiple services that have the same contract. How would WCF know then which one you want?
This has been bugging me for a few days, so I went through the examples shown in the article you linked above. Everything works correctly except for the second client proxy example that you're having a problem with. As you noted and the other answerers creating a proxy in that way requires an endpoint name, coupling it with the client (which is where the endpoint is defined). I'm still not sure why it behaves the way it does, but I found no way to use that example without explicitly coupling the proxy to the endpoint.
On the other hand, the first example showing how to create the proxy requires no explicit coupling of endpoint address or binding:
using System;
using System.ServiceModel;
namespace CoDeMagazine.ServiceArticle
{
public class ProductClient
: ClientBase<IProductBrowser>,
IProductBrowser
{
#region IProductBrowser Members
public ProductData GetProduct(
Guid productID)
{
return Channel.GetProduct(productID);
}
public ProductData[] GetAllProducts()
{
return Channel.GetAllProducts();
}
public ProductData[] FindProducts(
string productNameWildcard)
{
return Channel.FindProducts(
productNameWildcard);
}
#endregion
}
}
That seems to work just fine. So, maybe the second proxy example is just a poor way to do things, or maybe we're missing something obvious...
If you don't want to specify endpoint Name explicity, you may write:
public AdminClient()
{
ChannelFactory<IProductAdmin> factory =
new ChannelFactory<IProductAdmin>(string.Empty);
productAdminChannel = factory.CreateChannel();
}
Parameterless constructor unnecessary doesn't work.