Consume WCF service (using MSMQ) with legacy client - wcf

I created a WCF service which uses MSMQ.
Multiple .NET clients should be able to consume this service and send messages to it.
Partially the clients are written in .NET versions where there was no WCF (e.g. .NET 1.1).
For these clients I make direct use of the Msmq API.
The problem is, that the encoding of the messages don't fit the expected encoding on the service-side.
So I tried to alter the message encoding on the service-side using a customBinding:
<bindings>
<customBinding>
<binding name="MyCustomBinding">
<!-- available encodings: -->
<!-- <textMessageEncoding /> -->
<!-- <binaryMessageEncoding /> -->
<!-- <mtomMessageEncoding /> -->
<!-- <webMessageEncoding /> -->
<msmqTransport ...>
<msmqTransportSecurity ... />
</msmqTransport>
</binding>
</customBinding>
</bindings>
I guess I am restricted to one of the four pre-defined encodings: textMessageEncoding, binaryMessageEncoding, mtomMessageEncoding, webMessageEncoding.
On the client side I tried to alter the formatter:
System.Messaging.Message msmqMessage = new System.Messaging.Message();
msmqMessage.Formatter = new System.Messaging.ActiveXMessageFormatter();
//msmqMessage.Formatter = new System.Messaging.BinaryMessageFormatter();
//msmqMessage.Formatter = new System.Messaging.XMLMessageFormatter();
It seems that no formatter fits to an expected encoding.
Is there another way of unifying the encoding?
Perhaps a custom encoder or something like that?
Or am I completely wrong with adjusting formatter and encoder?
Thanks in advance.

You can use the built in MsmqIntegrationBinding and set the serialization format to xml:
<service name="MyQueueListenner">
<endpoint address="msmq.formatname:DIRECT=OS:.\private$\myQueue"
binding="msmqIntegrationBinding"
bindingConfiguration="DotNetBinding"
contract="MyContract" />
</service>
...
<msmqIntegrationBinding>
<binding serializationFormat="Xml" name="DotNetBinding" durable="false" exactlyOnce="false">
<security mode="None" />
</binding>
</msmqIntegrationBinding>
UPDATE
The whole point of integration binding is maximum interoperability so that wcf can support non-.Net msmq clients (ActiveX, Java).
For this reason exposing data contracts (beyond String) would not be meaningful.
I guess MS didn't really imagine people would use it for interop between lower .Net versions and WCF.
The only thing I can suggest is host a mex endpoint defining a set of one-way operations exposing your types over http and then allow clients to consume the wsdl from this endpoint.
They can then use this to build up their local type definitions for use with your msmq endpoint.
Just make it clear they should not actually call the http operations, or have the operations throw a NotImplementedException.

Related

How do I use WS-Addressing in WCF and set the wsa:replyto header?

I'm calling a BizTalk service using WCF. The service requires the wsa:replyto address to be set in the SOAP header to able to make a 'callback' when the process is done.
We are using a contract-first approch with auto-generated code from svcutil (we cannot 'just' change the contract)...
And it's not possible to do in the config file...
I have seen someone 'overriding' some methods to make their own custom header - but this is not a custom header it's a standard in the SOAP protocol.
How can I add the wsa:replyto in the (SOAP) header?
In order to invoke a service that requires WS-Addressing from WCF you'll have to configure the client endpoint to use a binding that supports it, such as the WSHttpBinding.
You can then set the wsa:ReplyTo header to a specific URL in your client code through the OperationContext.OutgoingMessageHeaders property:
using (new OperationContextScope((IContextChannel)channel))
{
OperationContext.Current.OutgoingMessageHeaders.ReplyTo =
new EndpointAddress("http://client/callback");
channel.DoSomething();
}
In this example we are setting the wsa:ReplyTo header to a known URL where the client channel listens for incoming callback messages from the service.
Alternatively, if the service supports it, you could use the WSDualHttpBinding, which has built in support for duplex communication through WS-Addressing. In this case you would set the callback address through the WSDualHttpBinding.ClientBaseAddress property:
<system.serviceModel>
<bindings>
<wsDualHttpBinding>
<binding clientBaseAddress="http://client/callback" />
</wsDualHttpBinding>
</bindings>
<client>
<endpoint address="http://server/service"
binding="wsDualHttpBinding"
contract="Namespace.Service" />
</client>
</system.serviceModel>

Content Type text/xml; charset=utf-8 was not supported by service

I have a problem with a WCF service.
I have a console application and I need to consume the service without using app.config, so I had to set the endpoint, etc. by code.
I do have a service reference to the svc, but I can't use the app.config.
Here's my code:
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress address = new EndpointAddress("http://localhost:8731/WcfServicio/MiServicio");
MiServicioClient svc = new MiServicioClient(binding, address);
object ob = svc.PaisesObtener();
At the last line when I do svc.PaisesObtener() I get the error:
Content Type text/xml; charset=utf-8 was not supported by service
http://localhost:8731/WcfServicio/MiServicio. The client and service bindings may be mismatched.
First Google hit says:
this is usually a mismatch in the client/server bindings, where the message version in the service uses SOAP 1.2 (which expects application/soap+xml) and the version in the client uses SOAP 1.1 (which sends text/xml). WSHttpBinding uses SOAP 1.2, BasicHttpBinding uses SOAP 1.1.
It usually seems to be a wsHttpBinding on one side and a basicHttpBinding on the other.
Do not forget check the bindings-related code too.
So if you wrote:
BasicHttpBinding binding = new BasicHttpBinding();
Be sure that all your app.config files contains
<endpoint address="..."
binding="basicHttpBinding" ...
not the
<endpoint address="..."
binding="wsHttpBinding" ...
or so.
I've seen this behavior today when the
<service name="A.B.C.D" behaviorConfiguration="returnFaults">
<endpoint contract="A.B.C.ID" binding="basicHttpBinding" address=""/>
</service>
was missing from the web.config. The service.svc file was there and got served. It took a while to realize that the problem was not in the binding configuration it self...
I saw this problem today when trying to create a WCF service proxy, both using VS2010 and svcutil.
Everything I'm doing is with basicHttpBinding (so no issue with wsHttpBinding).
For the first time in my recollection MSDN actually provided me with the solution, at the following link How to: Publish Metadata for a Service Using a Configuration File. The line I needed to change was inside the behavior element inside the MEX service behavior element inside my service app.config file. I changed it from
<serviceMetadata httpGetEnabled="true"/>
to
<serviceMetadata httpGetEnabled="true" policyVersion="Policy15"/>
and like magic the error went away and I was able to create the service proxy. Note that there is a corresponding MSDN entry for using code instead of a config file: How to: Publish Metadata for a Service Using Code.
(Of course, Policy15 - how could I possibly have overlooked that???)
One more "gotcha": my service needs to expose 3 different endpoints, each supporting a different contract. For each proxy that I needed to build, I had to comment out the other 2 endpoints, otherwise svcutil would complain that it could not resolve the base URL address.
I was facing the similar issue when using the Channel Factory. it was actually due to wrong Contract specified in the endpoint.
For anyone who lands here by searching:
content type 'application/json; charset=utf-8' was not the expected type 'text/xml; charset=utf-8
or some subset of that error:
A similar error was caused in my case by building and running a service without proper attributes. I got this error message when I tried to update the service reference in my client application. It was resolved when I correctly applied [DataContract] and [DataMember] attributes to my custom classes.
This would most likely be applicable if your service was set up and working and then it broke after you edited it.
I was also facing the same problem recently. after struggling a couple of hours,finally a solution came out by addition to
Factory="System.ServiceModel.Activation.WebServiceHostFactory"
to your SVC markup file. e.g.
ServiceHost Language="C#" Debug="true" Service="QuiznetOnline.Web.UI.WebServices.LogService"
Factory="System.ServiceModel.Activation.WebServiceHostFactory"
and now you can compile & run your application successfully.
Again, I stress that namespace, svc name and contract must be correctly specified in web.config file:
<service name="NAMESPACE.SvcFileName">
<endpoint contract="NAMESPACE.IContractName" />
</service>
Example:
<service name="MyNameSpace.FileService">
<endpoint contract="MyNameSpace.IFileService" />
</service>
(Unrelevant tags ommited in these samples)
In my case, I had to specify messageEncoding to Mtom in app.config of the client application like that:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="IntegrationServiceSoap" messageEncoding="Mtom"/>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:29495/IntegrationService.asmx"
binding="basicHttpBinding" bindingConfiguration="IntegrationServiceSoap"
contract="IntegrationService.IntegrationServiceSoap" name="IntegrationServiceSoap" />
</client>
</system.serviceModel>
</configuration>
Both my client and server use basicHttpBinding.
I hope this helps the others :)
I had this error and all the configurations mentioned above were correct however I was still getting "The client and service bindings may be mismatched" error.
What resolved my error, was matching the messageEncoding attribute values in the following node of service and client config files. They were different in mine, service was Text and client Mtom. Changing service to Mtom to match client's, resolved the issue.
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IMySevice" ... messageEncoding="Mtom">
...
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
I had this problem in .net 6.0
The problem was the Soap Version, the BasicHttpBinding targets Soap 1.1 by default, but the service uses Soap 1.2.
The solution was to create a custom binding that targets Soap 1.2:
private Binding GetBindingConfiguration()
{
var textBindingElement = new TextMessageEncodingBindingElement()
{
MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None)
};
var httpsBindingElement = new HttpsTransportBindingElement()
{
MaxReceivedMessageSize = int.MaxValue,
RequireClientCertificate = true //my service require certificate
};
return new CustomBinding(textBindingElement, httpsBindingElement);
}
var binding = GetBindingConfiguration();
var address = new EndpointAddress("https://nfe.sefa.pr.gov.br/nfe/NFeAutorizacao4"); //Brazil NF-e endpoint that I had to consume.
var svc = new MyService(binding, address);
//my service requires certificate
svc.ClientCredentials.ClientCertificate.Certificate = certificado;
object ob = svc.PaisesObtener(); //call the method

WCF set Endpoint and Binding dynamically in code

Yes, I've read the other questions on SO, MSDN, and other sites, but I found no answers as clear as I can understand. I need to set my Silverlight application's WCF references relative to the site that it's loaded from, but I can't get it to work. There is no problem with the service itself, it is working. When I move from local to my real server, I get errors in my SL app complaining about not connecting to localhost.
Here is my ServiceReferences.ClientConfig file:
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="CustomBinding_AccountManager">
<binaryMessageEncoding />
<httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
<binding name="CustomBinding_FileManager">
<binaryMessageEncoding />
<httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
<binding name="CustomBinding_SiteManager">
<binaryMessageEncoding />
<httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://localhost:60322/AccountManager.svc"
binding="customBinding" bindingConfiguration="CustomBinding_AccountManager"
contract="AccountManager.AccountManager" name="CustomBinding_AccountManager" />
<endpoint address="http://localhost:60322/FileManager.svc" binding="customBinding"
bindingConfiguration="CustomBinding_FileManager" contract="FileManager.FileManager"
name="CustomBinding_FileManager" />
<endpoint address="http://localhost:60322/SiteManager.svc" binding="customBinding"
bindingConfiguration="CustomBinding_SiteManager" contract="SiteManager.SiteManager"
name="CustomBinding_SiteManager" />
</client>
</system.serviceModel>
</configuration>
Yes, I will optimize the buffer/message sizes and I know the possible DoS exploits, forget about it for now, I need them for big file transfers. An approach I tried is while instantiating the clients, I've used this code:
fileManager = new FileManagerClient(new BasicHttpBinding(), new EndpointAddress("http://" + Settings.Host + "/FileManager.svc"));
accManager = new AccountManagerClient(new BasicHttpBinding(), new EndpointAddress("http://" + Settings.Host + "/AccountManager.svc"));
where Settings.Host is my own method which returns me the host that the SL app is running from, tested, works. When I uploaded my XAP and tried, it still wanted to go for http://localhost:60322/AccountManager.svc, after further investigation, I've realized that there are still lots of references to localhost, in unseen files:
AccountManager.disco:
<?xml version="1.0" encoding="utf-8"?>
<discovery xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/disco/">
<contractRef ref="http://localhost:60322/AccountManager.svc?wsdl" docRef="http://localhost:60322/AccountManager.svc" xmlns="http://schemas.xmlsoap.org/disco/scl/" />
</discovery>
Parts of AccountManager.wsdl:
... <wsdl:import namespace="" location="http://localhost:60322/AccountManager.svc?wsdl=wsdl0" />...
...[lots of operation declarations]...
<wsdl:service name="AccountManager">
<wsdl:port name="CustomBinding_AccountManager" binding="tns:CustomBinding_AccountManager">
<soap12:address location="http://localhost:60322/AccountManager.svc" />
<wsa10:EndpointReference>
<wsa10:Address>http://localhost:60322/AccountManager.svc</wsa10:Address>
</wsa10:EndpointReference>
</wsdl:port>
</wsdl:service>
Part of AccountManager1.xsd:
<xs:import schemaLocation="http://localhost:60322/AccountManager.svc?xsd=xsd2" namespace="http://schemas.datacontract.org/2004/07/Leftouch.Data.Summary" />
Part of configuration.svcinfo:
<endpoint normalizedDigest="<?xml version="1.0" encoding="utf-16"?><Data address="http://localhost:60322/AccountManager.svc" binding="customBinding" bindingConfiguration="CustomBinding_AccountManager" contract="AccountManager.AccountManager" name="CustomBinding_AccountManager" />" digest="<?xml version="1.0" encoding="utf-16"?><Data address="http://localhost:60322/AccountManager.svc" binding="customBinding" bindingConfiguration="CustomBinding_AccountManager" contract="AccountManager.AccountManager" name="CustomBinding_AccountManager" />" contractName="AccountManager.AccountManager" name="CustomBinding_AccountManager" />
Part of Reference.svcmap:
</ClientOptions>
<MetadataSources>
<MetadataSource Address="http://localhost:60322/AccountManager.svc" Protocol="http" SourceId="1" />
</MetadataSources>
<Metadata>
<MetadataFile FileName="AccountManager2.xsd" MetadataType="Schema" ID="e473b2d5-7af3-4390-87c3-a4fc3f54fb96" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?xsd=xsd2" />
<MetadataFile FileName="AccountManager1.xsd" MetadataType="Schema" ID="fd3a1ae0-b38b-4586-8622-5b0ee07e39fb" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?xsd=xsd0" />
<MetadataFile FileName="AccountManager.xsd" MetadataType="Schema" ID="6a49ee64-6eac-40e2-bcff-26418435e777" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?xsd=xsd1" />
<MetadataFile FileName="AccountManager.disco" MetadataType="Disco" ID="9ec9a8cc-0cf0-4264-a526-b5a6c08f7d36" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?disco" />
<MetadataFile FileName="AccountManager1.wsdl" MetadataType="Wsdl" ID="54a5b2c0-9d0e-4043-a7e4-d27ae6674bfc" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?wsdl=wsdl0" />
<MetadataFile FileName="AccountManager.wsdl" MetadataType="Wsdl" ID="f8923013-3a6c-412b-b7da-bee5a5a7bb64" SourceId="1" SourceUrl="http://localhost:60322/AccountManager.svc?wsdl" />
..and ALL these again for other 2 services too.
I am not a master of web services/bindings/endpoints/operation contracts or any related stuff. I just want to make my totally already-nice-working (when URI is hardcoded) system work for relative URIs, that's all I need. There must be a simple solution. Can someone explain what exactly those file types and declarations resemble, which are important and which are optional, and how can I create dynamic service references in the cleanest form. Please, with explanations. I've already seen lots of posts and articles about dynamic service bindings and references, but honestly, everything gets messed up and I end up not understanding anything from it. Any constructive criticism and solutions are welcome.
Is the config file for your service (not the client) using endpoints with fully qualified addresses (like your client config)? If so, the reason you're seeing all those localhost references is because when you add the service reference, it's going to carry through that address in the generated files.
Update the endpoint in your service config file to either use the fully qualified address on that machine (i.e., http://somname.com/service.svc), or set the baseAddresses in the config file to the machine name.
Additionally, if you're using WCF 4.0 (VS 2010/.NET 4.0), you can have WCF create a default endpoint for your service(s) by ommitting the endpoints from the config file altogether (as I understand it - we're just moving to 4.0 at work now, so I haven't played with the new features).
EDITED TO ADD
New approach, same basic idea (that the URI is being imported from somewhere). Based on your comments below, it sounds like your service's config is set up fine, with no hard-coded URIs pointing to localhost.
When you move your application to the target server(s), are you updating your reference to your service (via the Add Service Reference) as well, or simply moving the files that generates from your local box to the target server?
If you are, I'm wondering if that might be the source of your problem. I would think that specifying the service address when you create the client should override anything in the WSDL-related files, but maybe its not.
Something to try:
Delete the <client> section from your Web.config. Then, when you create the client, do so like this:
fileManager = new FileManagerClient(new BasicHttpBinding("CustomBinding_FileManager"), new EndpointAddress("http://" + Settings.Host + "/FileManager.svc"));
Make sure you pass in the name of the binding configuration section in the BasicHttpBinding constructor, otherwise you'll get the binding with the default values, not the larger values you specified.
The idea here is to eliminate any chance that the client config file settings are overriding what you're passing in on the FileManagerClient creation.
I'd consider it less than ideal to have to update the service reference for every deployment to each individual server - what you're trying to accomplish makes sense. I do something similar in an n-tier app I've written - the only difference is that I don't use service references, I generate the proxy via SvcUtil and then generate channels via ChannelFactory<T>, which is another route you might want to look at.
If none of this help, please feel free to e-mail me (my e-mail address is in my profile) - it might be easier to figure this out via e-mail exchange and then post the final solution.

WCF mex does not contain the complete binding information from the host

I'm publishing a service with a MEX endpoint for metadata exchange and I'm using the code below to discover it and get the metadata information
DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
FindCriteria findCriteria = FindCriteria.CreateMetadataExchangeEndpointCriteria(ContractType);
findCriteria.Duration = TimeSpan.FromSeconds(15);
findCriteria.MaxResults = 1;// MaxResults;
FindResponse result = discoveryClient.Find(findCriteria);
discoveryClient.Close();
ServiceEndpointCollection eps = MetadataResolver.Resolve(ContractType, result.Endpoints[0].Address);
return eps[0].Binding;
When I get the metadata information in my client the binding information (OpenTimeout,
ReceiveTimeout and SendTimeout) is back to its default values.
Here is the binding information in the host
<binding name="MyServiceBinding" closeTimeout="00:05:00" openTimeout="00:05:00"
receiveTimeout="23:50:00" sendTimeout="00:05:00" maxReceivedMessageSize="50000000">
<readerQuotas maxStringContentLength="50000000" maxArrayLength="50000000" />
<reliableSession ordered="true" inactivityTimeout="00:01:00" enabled="false" />
<security mode="None" />
</binding>
here is another question i've found that is almost the same as mine.
WCF Service Binding taking default values instead of custom values
I would like to know if I'm doing something wrong or if I misunderstood the concept of metadata exchange.
What I'm trying to do is send all the info necessary to my clients so they can auto config them self and do not have any hard code configuration.
I don't think you're doing anything wrong - you're just expecting too much from the metadata exchange.
The purpose of MEX is to be able to discover new services programmatically, and create client-side proxies for those services. For this, there's the WSDL - basically anything contained in the WSDL is part of the metadata exchange:
service contract / service methods
parameters needed for those service methods
data type declarations in XML schema for the data types used
additional service related information like bindings used etc.
But MEX does not contain all WCF specific configuration settings - which is what you've discovered. MEX will create a functioning client-side proxy - but it never had the intention of transporting all configuration settings from the server to the client. You'll need to hand-code this yourself, on the client side.

Silverlight application requesting a file from a WCF service

I have a Silverlight (v3) application that is communicating to a WCF service on my server. One of the things the Silverlight application does is request a dynamically generated data file - this data file is created by the service and needs to (ultimately) be saved on the local user's machine via the SaveFileDialog.
My question is, what is the best way in Silverlight to get this file? The file in question may be quite large.
Any help would be appreciated.
If you already know that the file being requested might be quite large, then you might want to create your own specific endpoint for this request, which supports streaming.
So you would have a regular endpoint (e.g. http://yourserver:8080/YourService) which you use for "normal" method calls, and a second endpoint (http://yourserver:8085/YourService) which would support streaming to send back the file with a reasonable amount of memory overhead.
Configuring this should be fairly simple - both on the server and the client, you need to specify a binding configuration to support streaming:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="streamed"
transferMode="StreamedResponse" />
</basicHttpBinding>
</bindings>
<services>
<service name="YourService">
<endpoint name="normal"
address="http://yourserver:8080/YourService"
binding="basicHttpBinding"
contract="IYourServiceContract" />
<endpoint name="filetransfer"
address="http://yourserver:8085/YourService"
binding="basicHttpBinding"
bindingConfiguration="streamed"
contract="IYourServiceContract" />
</service>
</services>
</system.serviceModel>
On the client, of course, you'd have to have the two endpoints inside a <client> tag, but otherwise, everything should be the same.
The "transferMode" is "buffered" by default, e.g. the whole message is buffered and sent in one block.
Your other options are "Streamed" (streaming both ways), "StreamedRequest" (if you have really large requests) or "StreamedResponse" (if only the response, the file being transferred, is really large).
In this case, you'd have a single method on your service that would return a stream (i.e. the file). From your client, when you call this service method, you get back a stream that you can then read in chunks, just like a MemoryStream or a FileStream.
Marc