While trying to build a client-server WCF application in Mono we ran into some issues.
Reducing it to just a bare example we found that the service only accepts one client at a time. If another client attempts to connect, it hangs until the first one disconnects.
Simply changing to BasicHttpBinding fixes it but we need NetTcpBinding for duplex communication. Also the problem does not appear if compiled under MS .NET.
EDIT: I doubt (and hope not) that Mono doesn't support what I'm trying to do. Mono code usually throws NotImplementedExceptions in such cases as far as I noticed. I am using Mono v2.6.4
This is how the service is opened in our basic scenario:
public static void Main (string[] args)
{
var binding = new NetTcpBinding ();
binding.Security.Mode = SecurityMode.None;
var address = new Uri ("net.tcp://localhost:8080");
var host = new ServiceHost (typeof(Hello));
host.AddServiceEndpoint (typeof(IHello), binding, address);
ServiceThrottlingBehavior behavior = new ServiceThrottlingBehavior ()
{
MaxConcurrentCalls = 100,
MaxConcurrentSessions = 100,
MaxConcurrentInstances = 100
};
host.Description.Behaviors.Add (behavior);
host.Open ();
Console.ReadLine ();
host.Close ();
}
The client channel is obtained like this:
var binding = new NetTcpBinding ();
binding.Security.Mode = SecurityMode.None;
var address = new EndpointAddress ("net.tcp://localhost:8080/");
var client = new ChannelFactory<IHello> (binding, address).CreateChannel ();
As far as I know this is a Simplex connection, isn't it?
The contract is simply:
[ServiceContract]
public interface IHello
{
[OperationContract]
string Greet (string name);
}
Service implementation has no ServiceModel tags or attributes.
I'll update with details as required.
I've played around with this a bit, and it definitely looks like a Mono bug.
I'm porting a WCF application to run in Mono at the moment. I had played with some NetTcpBinding stuff, but I hadn't tried this scenario (multiple connections to a Mono-hosted service host). However now I try it out, I'm able to reproduce - both in 2.6 and the latest daily package.
It does work in .NET, however. Any difference in behavior between Mono and .NET is classed as a bug. You should log it on Bugzilla with a test case, I would also post in the Mono newslist.
Good luck.
Definately a bug. I'm wondering if there was a version it was working correctly...
I've posted it at Novell Bugzilla, if you are interested in its progress.
Related
Is there Any way of hosting WCF service on Linux.
I read about wine but i didn't see any example of hosting WCF service with it.
P.S : I have tried mono and mod_mono but to no avail.
You can host it in a stand-alone console application like so:
using System;
using System.ServiceModel;
using Service;
namespace Host
{
class MainClass
{
public static void Main (string[] args)
{
Console.WriteLine ("WCF Host!");
var binding = new BasicHttpBinding ();
var address = new Uri ("http://localhost:8080");
var host = new ServiceHost (typeof(GreeterWcfService));
host.AddServiceEndpoint (
typeof(IGreeterWcfService), binding, address);
host.Open ();
Console.WriteLine ("Type [Enter] to stop...");
Console.ReadLine ();
host.Close ();
}
}
}
Where GreeterWcfService is the WCF service class itself and IGreeterWcfService is the service contract.
Full working example solution in GitHub - with separate projects for the service, the hosting and a client. Check it out.
Its possible but you should refer to this link for understanding current state and known issues - http://www.mono-project.com/docs/web/wcf/. It's limited now. For eg. if you wish to use WSHttpBinding its not supported currently.
I have a client-server application based on WCF where I'm using ServiceDiscovery to find the server from the client. During development with security turned off discovery was working fine but when we turned on message security based on certificates the ServiceDiscovery stopped working.
When I searched for a solution I found this MSDN article, http://msdn.microsoft.com/en-us/library/dd456791%28v=vs.110%29.aspx where it says;
When using message level security it is necessary to specify an EndpointIdentity on the service discovery endpoint and a matching EndpointIdentity on the client discovery endpoint. For more information about message level security, see Message Security in WCF.
I have been searching, reading and writing code but I can't seem to get this into working code. Any ideas?
Exctract of original server code:
private Binding CreateBinding()
{
WSDualHttpBinding binding = new WSDualHttpBinding(WSDualHttpSecurityMode.Message);
// Set other binding properties
return binding;
}
private static void EnableServiceDiscovery(ServiceHostBase host)
{
host.AddServiceEndpoint(new UdpDiscoveryEndpoint());
host.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
}
Compact extract of original client code:
public IEnumerable<MyServiceEndpoint> FindServicesOnNetwork()
{
DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
var myServiceEndpoints = discoveryClient.Find(new FindCriteria(typeof (IMyService))).Endpoints;
discoveryClient.Close();
return myServiceEndpoints.Select(endpoint => new MyServiceEndpoint(endpoint.Address.Uri.ToString())).ToList();
}
I've got a service and have verified using "netstat -anb" that when the service is running, it's listening on the correct port (8040). The service contract contains the following contract:
[OperationContract]
bool RegisterPlayer();
The service class itself implements the contract explicitly:
bool IMechService.RegisterPlayer()
{
if (P1 != null)
{
P1 = OperationContext.Current.GetCallbackChannel<IMechServiceCallback>();
return true;
}
else if (P2 != null)
{
P2 = OperationContext.Current.GetCallbackChannel<IMechServiceCallback>();
return true;
}
return false;
}
And the svcutil generated proxy creates the following method:
public bool RegisterPlayer()
{
return base.Channel.RegisterPlayer();
}
This code attempts to generate a proxy and call the method. I've tried both using DuplexChannelFactory and the svcutil generated proxy class, and both give the same results:
client = new MechServiceClient(new InstanceContext(this));
//client = DuplexChannelFactory<IMechService>.CreateChannel(this, new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:8040/MechService"));
client.RegisterPlayer();
Code execution reaches the RegisterPlayer in the proxy class, but proceeds to time out, never running RegisterPlayer on the service. Unfortunately, as it's just timing out, I'm not getting any exceptions or errors to help indicate where to look for issues. So far, I've verified the service is running and appears to be listening on port 8040 using "netstat -anb", and I've established that the mex endpoint is working as intended and publishing metadata. I turned off Windows Firewall. I've also created a separate test project with much simpler implementations to verify I was doing the steps correctly, and the simpler test project works fine. I'm out of ideas for what's causing this to fail, and any advice would be appreciated.
Have you tried setting the ConcurrencyMode to ConcurrencyMode.Multiple?
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
class MechServiceImpl : IMechService
{
// ..
}
The default concurrency mode for a service is ConcurrencyMode.Single, which can cause complications with callbacks.
Andrew's suggestion to logging helped, basically what fixed it was declaring my OperationContracts to isoneway=true.
I have a class that implements a plugin for an existing application.
I also have exposed that class as a WCF service. That part is working so far. The problem I am running into is that the application I am plugging into creates the instance of my class that I want to use.
Is there a way to pass an existing class instance to the WCF service host, to expose as a service endpoint?
I know (or can figure out) how to make a singleton instance of a WCF service, but that still won't help me. From what I can tell, the singleton instance will still be created and provided by WCF.
I have thought of other approaches, but I'd rather take this one if it is available to me.
Some code. This is in the constructor of my plugin:
// Setup the service host
var baseAddress = new Uri("http://localhost:8080/MyService/");
this.serviceHost = new ServiceHost(this.GetType(), baseAddress);
// Add our service endpoint
// Todo: Is there somewhere around here that I can provide an instance?
// Maybe in behavior somewhere?
this.serviceHost.AddServiceEndpoint(
typeof(ITheInterfaceMyClassDerivesFrom),
new BasicHttpBinding(),
""
);
// Add metadata exchange (so we see something when we go to that URL)
var serviceMetadataBehavior = this.serviceHost.Description.Behaviors
.Find<ServiceMetadataBehavior>();
if (serviceMetadataBehavior == null)
this.serviceHost.Description.Behaviors.Add(new ServiceMetadataBehavior());
this.serviceHost.AddServiceEndpoint(
typeof(IMetadataExchange),
new CustomBinding(new HttpTransportBindingElement()),
"MEX"
);
This is in the plugin's OnStartedUp method (called by the application I am plugging into):
serviceHost.Open();
You need to use the other constructor for ServiceHost if you want to do this - check out the MSDN docs at http://msdn.microsoft.com/en-us/library/ms585487.aspx
public ServiceHost(
Object singletonInstance,
params Uri[] baseAddresses
)
I am looking to write some integration tests to compare the WSDL generated by WCF services against previous (and published) versions. This is to ensure the service contracts don't differ from time of release.
I would like my tests to be self contained and not rely on any external resources such as hosting on IIS.
I am thinking that I could recreate my IIS hosting environment within the test with something like...
using (ServiceHost host = new ServiceHost(typeof(NSTest.HelloNS), new Uri("http://localhost:8000/Omega")))
{
host.AddServiceEndpoint(typeof(NSTest.IMy_NS), new BasicHttpBinding(), "Primary");
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
host.Description.Behaviors.Add(behavior);
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
host.Open();
}
Does anyone else have any better ideas?
EDIT:
Obviously this code is simply creating a host for the service, I am still missing the client code to obtain the WSDL definition.
Just use WebClient and ?wsdl sufix in URL
using (ServiceHost host = new ServiceHost(typeof(NSTest.HelloNS),
new Uri("http://localhost:8000/Omega")))
{
host.AddServiceEndpoint(typeof(NSTest.IMy_NS), new BasicHttpBinding(), "Primary");
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
host.Description.Behaviors.Add(behavior);
host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
host.Open();
string wsdl = null;
using (WebClient wc = new WebClient())
{
using (var stream = wc.OpenRead("localhost:8000/Omega?wsdl"))
{
using (var sr = new StreamReader(stream))
{
wsdl = sr.ReadToEnd();
}
}
}
Console.Write(wsdl);
}
Check out the WsdlExporter on MSDN. Its used to generate wsdl in WCF.
You could also have a look in svcutil with reflector to see how its generating the wsdl information, since the tool can generate wsdl from a dll-file.
Another way to do your comparison would be to use the svcutil tool to generate the wsdl and compare it to a saved/baselined version of the service. Run the svcutil in your test and verify the output against the old files. Not really self-contained test since you'll need the svcutil...
How about something like this?
Creating a WSDL using C#
One thing you need to be careful of is to compare the entire WSDL. WCF breaks up WSDLs, unlike classic web services (asmx) WSDLs. This means that the core of the info is on the ?WSDL page, however, there will also be multiple XSDs (.svc?XSD=XSD0, 1, 2 ...) and possibly multiple WSDL pages (?WSDL and ?WSDL=WSDL0 for example).
One way to accomplish this would be to generate a webrequest to get the data from the root wsdl. Then you can search the WSDL for anything like (yourServicename).svc?WSDL=WSLD0 and (yourServicename)?XSD=XSD0 and so on, spawning additional webrequests for each WSDL and XSD.
You might be better off using SoapUI to test the WSDL rather than relying on NUnit directly.
If you want to call SoapUI from NUnit, it's possible, but a little clunky. See http://enthusiasm.soapui.org/forum/viewtopic.php?f=2&t=15 for more information.
Same answer translated to VB
Using host = New ServiceHost(GetType(MyHelloWorldWcfLib.HelloWorldServer), New Uri("http://localhost:8000/Omega"))
host.AddServiceEndpoint(GetType(MyHelloWorldWcfLib.IHelloWorld), New BasicHttpBinding(), "Primary")
Dim behavior = New ServiceMetadataBehavior()
behavior.HttpGetEnabled = True
host.Description.Behaviors.Add(behavior)
host.AddServiceEndpoint(GetType(IMetadataExchange), MetadataExchangeBindings.CreateMexHttpBinding(), "mex")
host.Open()
Dim wsdl As String = Nothing
Using wc = New System.Net.WebClient()
Using stream = wc.OpenRead("http://localhost:8000/Omega?wsdl")
Using sr = New IO.StreamReader(stream)
wsdl = sr.ReadToEnd()
End Using
End Using
End Using
Console.Write(wsdl)
End Using