I have a simple WCF service with one method:
[ServiceContract]
public interface TestServiceContract
{
[OperationContract]
int[] Test();
}
public class TestService:TestServiceContract
{
public int[] Test()
{
return new int[1000000];
}
}
When on the client side I call
client.Test();
it fails, obviously because object I pass is too large.
BUT
instead of a meaningful description I get a totally useless
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication
because it is in the Faulted state.
I tried enabling
<serviceDebug includeExceptionDetailInFaults="true" />
but it doesn't help.
Is it possible to get a meaningful error description?
Use "try catch" to catch exceptions when creating service endpoints.According to your description, I did a test and found that if the passed object is too large, there will be exceptions. Here is the exception I got:
Here is my demo:
namespace Test
{
[ServiceContract]
public interface TestServiceContract
{
[OperationContract]
int[] Test();
}
public class TestService : TestServiceContract
{
public int[] Test()
{
return new int[1000000];
}
}
class Program
{
static void Main(string[] args)
{
Uri baseAddress = new Uri("http://localhost:8000/GettingStarted/");
ServiceHost selfHost = new ServiceHost(typeof(TestService), baseAddress);
try
{
selfHost.AddServiceEndpoint(typeof(TestServiceContract), new WSHttpBinding(), "Test");
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
selfHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <Enter> to terminate the service.");
Console.WriteLine();
Console.ReadLine();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
selfHost.Abort();
}
}
}
}
This is the server-side code.
static void Main(string[] args)
{
WSHttpBinding myBinding = new WSHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://localhost:8000/GettingStarted/Test");
ChannelFactory<TestServiceContract> myChannelFactory = new ChannelFactory<TestServiceContract>(myBinding, myEndpoint);
TestServiceContract wcfClient1 = myChannelFactory.CreateChannel();
wcfClient1.Test();
}
This is the client-side code.I create a channel factory to call the service. You can also use Svcutil to generate proxy classes to call services.
Related
I'm having a problem with a self-host WCF REST service.
When I try to issue a GET via browser or Fiddler, I get a 400 Bad Request. Tracing is reporting an inner exception of XmlException "The body of the message cannot be read because it is empty."
I don't have any configuration in app.config (do I need any?). I have tried changing WebServiceHost to ServiceHost, and WSDL is returned, but the operations still return 400.
What am I missing here?
// Add Reference to System.ServiceModel and System.ServiceModel.Web
using System;
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
namespace WCFRESTTest
{
class Program
{
static void Main(string[] args)
{
var baseAddress = new Uri("http://localhost:8000/");
var host = new WebServiceHost(typeof(RestService), baseAddress);
try
{
host.AddServiceEndpoint(typeof(IRestService), new WSHttpBinding(), "RestService");
var smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
host.Open();
Console.WriteLine("Service Running. Press any key to stop.");
Console.ReadKey();
}
catch(CommunicationException ce)
{
host.Abort();
throw;
}
}
}
[ServiceContract]
public interface IRestService
{
[OperationContract]
[WebGet(UriTemplate = "Test")]
bool Test();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RestService : IRestService
{
public bool Test()
{
Debug.WriteLine("Test Called.");
return true;
}
}
}
When you use the WebServiceHost, you typically don't need to add a service endpoint - it will add one with all behaviors required to make it a "Web HTTP" (a.k.a. REST) endpoint (i.e., an endpoint which doesn't use SOAP and you can easily consume with a tool such as Fiddler, which seems to be what you want). Also, Web HTTP endpoints aren't exposed in the WSDL, so you don't need to add the ServiceMetadataBehavior either.
Now for why it doesn't work - sending a GET request to http://localhost:8000/Test should work - and in the code below it does. Try running this code, and sending the request you were sending before with Fiddler, to see the difference. That should point out what the issue you have.
public class StackOverflow_15705744
{
[ServiceContract]
public interface IRestService
{
[OperationContract]
[WebGet(UriTemplate = "Test")]
bool Test();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class RestService : IRestService
{
public bool Test()
{
Debug.WriteLine("Test Called.");
return true;
}
}
public static void Test()
{
var baseAddress = new Uri("http://localhost:8000/");
var host = new WebServiceHost(typeof(RestService), baseAddress);
// host.AddServiceEndpoint(typeof(IRestService), new WSHttpBinding(), "RestService");
// var smb = new ServiceMetadataBehavior();
// smb.HttpGetEnabled = true;
// host.Description.Behaviors.Add(smb);
host.Open();
WebClient c = new WebClient();
Console.WriteLine(c.DownloadString(baseAddress.ToString().TrimEnd('/') + "/Test"));
Console.WriteLine("Service Running. Press any key to stop.");
Console.ReadKey();
}
}
I am experimenting with a WCF service in a Visual Studio unit test. Both the client and the service are configured programmatically.
Currently my code looks like this:
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Tests
{
public abstract class EntityBase
{
}
public class TestEntity : EntityBase
{
public string Name { get; set; }
}
[ServiceContract]
[ServiceKnownType("GetKnownTypes", typeof(ServiceKnownTypesDiscoveryHelper))]
public interface ITestService
{
[OperationContract]
EntityBase GetEntity(string entityName);
}
public class TestService : ITestService
{
public EntityBase GetEntity(string entityName)
{
Type t = Type.GetType(entityName);
return (EntityBase)Activator.CreateInstance(t);
}
}
[TestClass]
public class ServiceTests
{
private static ServiceHost ServiceHost { get; set; }
[ClassInitialize]
public static void ClassInitialize(TestContext testContext)
{
ServiceHost = new ServiceHost(typeof(TestService));
NetTcpBinding wsBinding = new NetTcpBinding();
ServiceHost.AddServiceEndpoint(typeof(ITestService), wsBinding,
"net.tcp://localhost:8011/TestService");
// trying to turn on debugging here
var behavior = ServiceHost.Description.Behaviors.Find<ServiceDebugBehavior>();
behavior.IncludeExceptionDetailInFaults = true;
ServiceHost.Open();
}
[ClassCleanup]
public static void ClassCleanup()
{
ServiceHost.Close();
}
[TestMethod]
public void TestSomething()
{
var binding = new NetTcpBinding();
var endpoint = new EndpointAddress("net.tcp://localhost:8011/TestService");
using (ChannelFactory<ITestService> testServiceFactory =
new ChannelFactory<ITestService>(binding, endpoint))
{
var proxy = testServiceFactory.CreateChannel();
using (proxy as IDisposable)
{
try
{
var entity = proxy.GetEntity(typeof(TestEntity).FullName);
Assert.IsInstanceOfType(entity, typeof(TestEntity));
}
catch (FaultException ex)
{
// copied this from MSDN example
string msg = "FaultException: " + ex.Message;
MessageFault fault = ex.CreateMessageFault();
if (fault.HasDetail == true)
{
var reader = fault.GetReaderAtDetailContents();
if (reader.Name == "ExceptionDetail")
{
ExceptionDetail detail = fault.GetDetail<ExceptionDetail>();
msg += "\n\nStack Trace: " + detail.StackTrace;
}
}
System.Diagnostics.Trace.WriteLine(msg);
}
}
}
}
}
}
If my ServiceKnownTypesDiscoveryHelper does not return known types, I know that my service and client should throw something serialisation related somewhere deep in .NET servicemodel code (if I modify it to return my TestEntity then of course everything works without any issues).
But currently if the service fails, I get only some vague exception messages like:
The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue.
and at the end of using() I get
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.
(which also is weird - why can't I even dispose the ServiceChannel if it's in a faulted state...)
How do I catch the actual fault which caused the service or the client to fail instead of those vague exception messages?
i create a small wcf apps when i am testing this apps in my machine then it is working but when i run the wcf server end on another pc at my home in same network then i am getting error
A remote side security requirement was not fulfilled during authentication.Try increasing the ProtectionLevel and/or ImpersonationLevel.
both the pc at my home in same work group and they can access each other. i try to find out the answer but people say this is firewall issue. so i disable firewall at both the pc but still getting the problem. here is my sample code. please guide me how can i run this wcf apps in two pc at my home network. thanks
Service end
namespace WCFSample
{
[ServiceContract]
public interface IService1
{
[OperationContract]
string MyName(string name);
}
}
namespace WCFSample
{
public class Service1 : IService1
{
public string MyName(string name)
{
return string.Format("My Name... {0}", name);
}
}
}
namespace ConsoleApplication1
{
class Program
{
static ServiceHost customerHost = null;
static void Main(string[] args)
{
try
{
HostCustomerService();
Console.WriteLine();
Console.WriteLine("Press any key to stop the services.");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
customerHost.Close();
}
}
private static void HostCustomerService()
{
customerHost = new ServiceHost(typeof
(Service1));
ServiceEndpoint tcpEndpoint = customerHost.AddServiceEndpoint(
typeof(IService1), new NetTcpBinding(),
"net.tcp://192.168.1.103:9020/Service1");
customerHost.Open();
Console.WriteLine("{0} {1}", tcpEndpoint.Address, tcpEndpoint.Name);
Console.WriteLine();
}
}
}
client end
namespace Client1
{
class Program
{
static void Main(string[] args)
{
IService1 channel = null;
var endPoint = new EndpointAddress(
"net.tcp://192.168.1.103:9020/Service1");
channel = ChannelFactory<IService1>.CreateChannel(new NetTcpBinding(), endPoint);
Console.WriteLine("Enter Name");
string line = Console.ReadLine();
Console.WriteLine(channel.MyName(line));
Console.ReadKey();
}
}
}
I think by default, NetTcpBinding requires a secure channel.
When you create your binding (on client and server), instead of:
new NetTcpBinding()
Try:
new NetTcpBinding(SecurityMode.None)
I need to have a generic Service contract but if I do that I receive this error:
[ServiceContract]
public interface IService<T> where T : MyClass
{
[OperationContract]
void DoWork();
}
The contract name 'x.y' could not be found in the list of contracts implemented by the service 'z.t'.
As long as you use a closed generic for your interface it does work - see below. What you cannot do is to have an open generic as the contract type.
public class StackOverflow_6216858_751090
{
public class MyClass { }
[ServiceContract]
public interface ITest<T> where T : MyClass
{
[OperationContract]
string Echo(string text);
}
public class Service : ITest<MyClass>
{
public string Echo(string text)
{
return text;
}
}
static Binding GetBinding()
{
BasicHttpBinding result = new BasicHttpBinding();
//Change binding settings here
return result;
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest<MyClass>), GetBinding(), "");
host.Open();
Console.WriteLine("Host opened");
ChannelFactory<ITest<MyClass>> factory = new ChannelFactory<ITest<MyClass>>(GetBinding(), new EndpointAddress(baseAddress));
ITest<MyClass> proxy = factory.CreateChannel();
Console.WriteLine(proxy.Echo("Hello"));
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
Your service contract is not interoperable. It's not possible to expose generics like that via WSDL.
Take a look at this article (link) for a possible workaround.
If you use an servicereference on thee client side generic will fail.
Use the following on client side with generic:
var myBinding = new BasicHttpBinding();
var myEndpoint = new EndpointAddress("");
var myChannelFactory = new ChannelFactory<IService>(myBinding, myEndpoint);
IService gks = myChannelFactory.CreateChannel();
How do I handle an exception thrown in a callback method on the client in a WCF duplex setup?
Currently, the client does not appear to raise the faulted event (unless I'm monitoring it incorrectly?) but any subsequent to call Ping() using the the client fails with CommunicationException: "The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it has been Aborted.".
How do I deal with this and recreate the client etc? My first question is how to find out when it happens. Secondly, how best to deal with it?
My service and callback contracts:
[ServiceContract(CallbackContract = typeof(ICallback), SessionMode = SessionMode.Required)]
public interface IService
{
[OperationContract]
bool Ping();
}
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void Pong();
}
My server implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
public class Service : IService
{
public bool Ping()
{
var remoteMachine = OperationContext.Current.GetCallbackChannel<ICallback>();
remoteMachine.Pong();
}
}
My client implementation:
[CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Single)]
public class Client : ICallback
{
public Client ()
{
var context = new InstanceContext(this);
var proxy = new WcfDuplexProxy<IApplicationService>(context);
(proxy as ICommunicationObject).Faulted += new EventHandler(proxy_Faulted);
//First Ping will call the Pong callback. The exception is thrown
proxy.ServiceChannel.Ping();
//Second Ping call fails as the client is in Aborted state
try
{
proxy.ServiceChannel.Ping();
}
catch (Exception)
{
//CommunicationException here
throw;
}
}
void Pong()
{
throw new Exception();
}
//These event handlers never get called
void proxy_Faulted(object sender, EventArgs e)
{
Console.WriteLine("client faulted proxy_Faulted");
}
}
As it turns out, you cannot expect the Faulted event to be raised. So, the best way to re-establish the connection is to do it when the subsequent call to Ping() fails:
I'll keep the code simple here:
public class Client : ICallback
{
public Client ()
{
var context = new InstanceContext(this);
var proxy = new WcfDuplexProxy<IApplicationService>(context);
(proxy.ServiceChannel as ICommunicationObject).Faulted +=new EventHandler(ServiceChannel_Faulted);
//First Ping will call the Pong callback. The exception is thrown
proxy.ServiceChannel.Ping();
//Second Ping call fails as the client is in Aborted state
try
{
proxy.ServiceChannel.Ping();
}
catch (Exception)
{
//Re-establish the connection and try again
proxy.Abort();
proxy = new WcfDuplexProxy<IApplicationService>(context);
proxy.ServiceChannel.Ping();
}
}
/*
[...The rest of the code is the same...]
//*/
}
Obviously, in my example code, the Exception will be thrown again but I hope this is useful to give people an idea of how to re-establish the connection.