I have WCF Service hosted on WindowsServiceHost (to communicate WindowsFormsApp <> WindowsServiceHost)
Is there any way get data from WCFService to WindowsServiceHost?
And in other way (set data from WindowsServiceHost to WCFService)
That is what have i done:
I've made a project of WCF Service Library, implemented interface, contracts etc.
I created new project - Windows service and added reference to project from #1 and to System.ServiceModel
Configured app.conf:
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcp">
<security mode="Message">
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="mexBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="mexBehavior" name="KSPDJOBWinWCFService.KSPDJOBWinWCFService" >
<endpoint address="KSPDJOBWinWCFService" binding="netTcpBinding" contract="KSPDJOBWinWCFService.IKSPDJOBWinWCFService" bindingConfiguration="netTcp" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8079"/>
<add baseAddress="net.tcp://localhost:8090"/>
</baseAddresses>
</host>
</service>
</services>
I've hosted the WCF in OnStart method of Windows Service
protected override void OnStart(string[] args)
{
host = new ServiceHost(typeof(KSPDJOBWinWCFService.KSPDJOBWinWCFService));
host.Open();
}
Added new solution with WinformsClient app (as WCF Client) and tested communication - all working fine.
The problem is when i send a value from WinFormsClient to WCF Service, and want to read it from Windows Service aplication
Thanks for any Help.
You could hold the WCF service instance in a global variable and work with events. In this sample the WCF Service KSPDJOBWinWCFService exposes an event EventA and the Service Host will handle it. This is the place where you can process the values sent by your WCF Client.
public partial class Service : ServiceBase
{
private ServiceHost _host;
private KSPDJOBWinWCFService _instance;
protected override void OnStart(string[] args)
{
try
{
_instance = new KSPDJOBWinWCFService();
_instance.EventA += HandleEventA;
_host = new ServiceHost(_instance);
_host.Open();
}
catch (Exception ex)
{
// Logging
}
}
public void HandleEventA(object sender, CustomEventArgs e)
{
// do whatever you want here
var localVar = e.Value;
}
protected override void OnStop()
{
try
{
if (_instance != null)
{
_instance.Dispose();
}
_host.Close();
}
catch (Exception ex)
{
// Logging
}
}
}
The WCF Service then fires this event together with the values sent from the WCF client:
public class KSPDJOBWinWCFService : IKSPDJOBWinWCFService
{
public event EventHandler<CustomEventArgs> EventA;
public bool SomeWcfOperation(int value)
{
EventA?.Invoke(this, new CustomEventArgs(value));
return true;
}
}
Create event args that fulfill your needs:
public class CustomEventArgs : EventArgs
{
public int Value { get; set; }
public CustomEventArgs(int value)
{
Value = value;
}
}
You can also expose values with public properties in your WCF Service. But events are also necessary.
Related
I have created a web service that exposes web interface.
the service run in console mode and i see the web interface
public ServiceHost serviceHost = null; public ServiceHost serviceHost = null;;
private readonly TestService s;
public Service()
{
InitializeComponent();
s = new TestService();
}
protected override void OnStart(string[] args)
{
Logger.Info("Start event");
if (serviceHost != null)
{
serviceHost.Close();
}
// Create a ServiceHost for the CalculatorService type and
// provide the base address.
string baseAddress = "http://localhost:8000/Service";
serviceHost = new ServiceHost(typeof(Service1), new System.Uri(baseAddress));
serviceHost.AddServiceEndpoint(typeof(WindowsServiceTemplate.IService1),
new BasicHttpBinding(), baseAddress);
// Check to see if the service host already has a ServiceMetadataBehavior
ServiceMetadataBehavior smb = serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
// If not, add one
if (smb == null)
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
serviceHost.Description.Behaviors.Add(smb);
// Add MEX endpoint
serviceHost.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex"
);
// Open the ServiceHostBase to create listeners and start
// listening for messages.
serviceHost.Open();
s.Start();
}
protected override void OnStop()
{
Logger.Info("Stop event");
if (serviceHost != null)
{
serviceHost.Close();
serviceHost = null;
}
s.Stop();
}
protected override void OnShutdown()
{
Logger.Info("Windows is going shutdown");
Stop();
}
public void Start()
{
OnStart(null);
}
}
}
and app.config file :
<system.serviceModel>
<services>
<!-- Note: the service name must match the configuration name for the service implementation. -->
<service name="WindowsServiceTemplate.IService1" behaviorConfiguration="MyServiceTypeBehaviors" >
<!-- Add the following endpoint. -->
<!-- Note: your service must have an http base address to add this endpoint. -->
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mexHttpBinding" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors" >
<!-- Add the following element to your service behavior configuration. -->
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
the configuration file inside app.config (console application project)
i am able to access
http://localhost:8000/Service
but when i try to call the test method
http://localhost:8000/Service/test
i get 404 error.
what am i missing?
Use Webhttpbinding to create the service and webservicehost to host service. I have made a demo, wish it is useful to you.
public partial class Service1 : ServiceBase
{
ServiceHost sh = new ServiceHost(typeof(MyService), new Uri("http://localhost:5900"));
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (sh.State==CommunicationState.Opened)
{
Log("Service open Fail");
}
else
{
WebHttpBinding webHttpBinding = new WebHttpBinding();
ServiceEndpoint se = sh.AddServiceEndpoint(typeof(IService), webHttpBinding, "");
se.EndpointBehaviors.Add(new WebHttpBehavior());
sh.Open();
Log("Service is ready....");
}
}
protected override void OnStop()
{
if (sh.State==CommunicationState.Opened)
{
sh.Close();
Log("Service closed successfully");
}
}
private void Log(string text)
{
using (StreamWriter sw=new StreamWriter(#"D:\log.txt",true))
{
sw.WriteLine($"{text}----Time:{DateTime.Now.ToShortTimeString()}");
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
[WebGet]
string SayHello();
}
public class MyService : IService
{
public string SayHello()
{
return "Hello Stranger";
}
}
}
Install.
Result.
Here is an official document.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-a-basic-wcf-web-http-service
Feel free to let me know if there is anything I can help with.
I need to write WCF service using TLS 1.2.I need to use only this security protocol and (as i think) refuse connections with other secure protocol types. I have created certificate. Bind it to port. Https works well. I read everywhere that i need to write next string of code:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Ok, i wrote it, but had no effect. Service side code:
using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Net;
using System.ServiceModel;
using System.Runtime.Serialization;
using static System.Console;
namespace ConsoleHost
{
public class DistributorValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
throw new SecurityTokenException("Both username and password required");
if (userName != "login" || password != "pass")
throw new FaultException($"Wrong username ({userName}) or password ");
}
}
public class Service1 : IService1
{
public string GetData(int value)
{
return $"You entered: {value}";
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException(nameof(composite));
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
}
[DataContract]
public class CompositeType
{
[DataMember]
public bool BoolValue { get; set; } = true;
[DataMember]
public string StringValue { get; set; } = "Hello ";
}
class Program
{
static void Main(string[] args)
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServiceHost host = new ServiceHost(typeof(Service1));
host.Open();
WriteLine("Press any key to stop server...");
ReadLine();
}
}
}
App.config contains:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<system.serviceModel>
<services>
<service name="ConsoleHost.Service1">
<host>
<baseAddresses>
<add baseAddress = "https://localhost:8734/Service1/" />
</baseAddresses>
</host>
<endpoint address="" binding="wsHttpBinding" contract="ConsoleHost.IService1" bindingConfiguration="securityBinding">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="securityBinding">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" />
<message clientCredentialType="UserName" />
<!--establishSecurityContext="false" />-->
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="ConsoleHost.DistributorValidator,ConsoleHost"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Client side code:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
Service1Client client = new Service1Client();
client.ClientCredentials.UserName.UserName = "login";
client.ClientCredentials.UserName.Password = "pass";
Console.WriteLine(client.GetData(10));
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("Inner: " + ex.InnerException.Message);
if (ex.InnerException.InnerException != null)
Console.WriteLine("Inner: " + ex.InnerException.InnerException.Message);
}
}
Console.ReadLine();
}
}
}
As you can see on service side i have set security protocol to Tls 1.2. On client side i have set security protocol to Ssl3. I am waiting that service will refuse client connection, because server must work and accept clients who will work with only Tls 1.2 security protocol. But i'm not getting this result. Client connects and works well. What's the problem?
I understand that i can change some settings on IIS to use only Tls 1.2. But i am making self hosting wcf service and that's the problem.
It can't be done for server using ServicePointManager.SecurityProtocol option, that's used to connection to sth through a specific security protocol. You can't turn off some security protocol for an separate application, you able to allow or dissallow connections for whole server. If u want disable all protocols except TLS 1.2 u have to open registry windows and find out the next key:
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\
And set the next values for each protocol in key [Server]: DisabledByDefault = 1, Enabled = 0
How to enable TLS
I am getting an error when the client reference is used a second time for calling the underlying method. On first call, It works perfectly. I have googled it and did so many tries like setting timeouts but nothing worked. Any suggestions will be highly appreciated. Also, if more details are required please let me know, I will post the required code.
A MDI Child form will call this method.
Debugger shows CommunicationException.
Trace Viewer shows message: The operation cannot be completed because the pipe was closed. This may have been caused by the application on the other end of the pipe exiting.
Contract
[ServiceContract(Namespace = "http://Company/ManagementInformationSystemServices/", SessionMode = SessionMode.Required)]
public interface IPrincipalService
{
#region Service contracts for Reports
[OperationContract]
[FaultContract(typeof(ProcessExecutionFault))]
Parcel InsertParcel(Parcel singleParcel, out bool Exists);
[OperationContract]
[FaultContract(typeof(ProcessExecutionFault))]
Parcel GetByParcelId(int id);
#endregion
}
Contract Implementation
[ServiceBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
public class PrincipalService : IPrincipalService
{
#region Constructor
public PrincipalService()
{
}
#endregion
#region Public Methods
#region Parcel Methods
public Parcel InsertParcel(Parcel singleParcel, out bool Exists)
{
bool ExistsInner = false;
try
{
ParcelComponent parcelComponent = new ParcelComponent();
singleParcel = parcelComponent.Insert(singleParcel, out ExistsInner);
Exists = ExistsInner;
return singleParcel;
}
catch (Exception ex)
{
throw new FaultException<ProcessExecutionFault>
(new ProcessExecutionFault(LogResource.InsertParcelExistsError, ex), ex.Message);
}
}
public Parcel GetByParcelId(int id)
{
try
{
ParcelComponent parcelComponent = new ParcelComponent();
return parcelComponent.GetById(id);
}
catch (Exception ex)
{
throw new FaultException<ProcessExecutionFault>
(new ProcessExecutionFault(LogResource.ReadParcelError, ex), ex.Message);
}
}
#endregion
#endregion
}
Server Configuration
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<services>
<service behaviorConfiguration="serviceBehavior" name="ManagementInformationSystem.Services.PrincipalService">
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/ManagementInformationSystemServices" />
</baseAddresses>
</host>
<endpoint address="PrincipalService"
binding="netNamedPipeBinding"
contract="ManagementInformationSystem.Services.Contracts.IPrincipalService" />
<endpoint address="PrincipalService/mex"
binding="mexNamedPipeBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</serviceBehaviors>
</behaviors>
<diagnostics wmiProviderEnabled="true">
</diagnostics>
Client Configuration
<system.serviceModel>
<bindings>
<netNamedPipeBinding>
<binding name="NetNamedPipeBinding_IPrincipalService" />
</netNamedPipeBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="net.pipe://localhost/ManagementInformationSystemServices/PrincipalService"
binding="netNamedPipeBinding" bindingConfiguration="NetNamedPipeBinding_IPrincipalService"
contract="PrincipalServiceReference.IPrincipalService" name="NetNamedPipeBinding_IPrincipalService">
<identity>
<userPrincipalName value="company-238\company" />
</identity>
</endpoint>
</client>
<diagnostics wmiProviderEnabled="true">
</diagnostics>
Service Helper Class and I am getting Communication Exception here
public static class ServiceHelper<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>(GlobalResource.PrincipalServiceEndPointName);
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
catch (CommunicationException ex)
{
throw ex;
}
catch (TimeoutException ex)
{
throw ex;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
I'm new to wcf and learning how to build one, with callbacks. I got an example from the following link:
http://architects.dzone.com/articles/logging-messages-windows-0
I tried implementing the wcf but when i run this in as a test by pressing f5, the test client says:
The service contract is not supported in the wcf client.
Service:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.PerCall)]
public class RatsService : IRatsService
{
public static List<IRatsServiceCallback> callBackList = new List<IRatsServiceCallback>();
public RatsService()
{
}
public void Login()
{
IRatsServiceCallback callback = OperationContext.Current.GetCallbackChannel<IRatsServiceCallback>();
if (!callBackList.Contains(callback))
{
callBackList.Add(callback);
}
}
public void Logout()
{
IRatsServiceCallback callback = OperationContext.Current.GetCallbackChannel<IRatsServiceCallback>();
if (callBackList.Contains(callback))
{
callBackList.Remove(callback);
}
callback.NotifyClient("You are Logged out");
}
public void LogMessages(string Message)
{
foreach (IRatsServiceCallback callback in callBackList)
callback.NotifyClient(Message);
}
Service Interface
[ServiceContract(Name = "IRatsService", SessionMode = SessionMode.Allowed, CallbackContract = typeof(IRatsServiceCallback))]
public interface IRatsService
{
[OperationContract]
void Login();
[OperationContract]
void Logout();
[OperationContract]
void LogMessages(string message);
// TODO: Add your service operations here
}
public interface IRatsServiceCallback
{
[OperationContract]
void NotifyClient(String Message);
}
App.config:
<system.serviceModel>
<services>
<service name="RatsWcf.RatsService">
<endpoint address="" binding="wsDualHttpBinding" contract="RatsWcf.IRatsService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8733/Design_Time_Addresses/RatsWcf/Service1/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information,
set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
Just wondering what could be wrong here.
There's nothing wrong. The WCF Test Client is a tool which can be used to test many types of WCF services, but not all of them - duplex contracts being one category which is not supported. You'll need to create some sort of client app to test that. In your client app you'll need to write a class which implements the callback interface, so that it can receive the messages initiated by the service.
For example, this is a very simple duplex client / service which uses WCF duplex:
public class DuplexTemplate
{
[ServiceContract(CallbackContract = typeof(ICallback))]
public interface ITest
{
[OperationContract]
string Hello(string text);
}
[ServiceContract(Name = "IReallyWantCallback")]
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void OnHello(string text);
}
public class Service : ITest
{
public string Hello(string text)
{
ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();
ThreadPool.QueueUserWorkItem(delegate
{
callback.OnHello(text);
});
return text;
}
}
class MyCallback : ICallback
{
AutoResetEvent evt;
public MyCallback(AutoResetEvent evt)
{
this.evt = evt;
}
public void OnHello(string text)
{
Console.WriteLine("[callback] OnHello({0})", text);
evt.Set();
}
}
public static void Test()
{
string baseAddress = "net.tcp://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITest), new NetTcpBinding(SecurityMode.None), "");
host.Open();
Console.WriteLine("Host opened");
AutoResetEvent evt = new AutoResetEvent(false);
MyCallback callback = new MyCallback(evt);
DuplexChannelFactory<ITest> factory = new DuplexChannelFactory<ITest>(
new InstanceContext(callback),
new NetTcpBinding(SecurityMode.None),
new EndpointAddress(baseAddress));
ITest proxy = factory.CreateChannel();
Console.WriteLine(proxy.Hello("foo bar"));
evt.WaitOne();
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
quick overview of what I've got here:
WCF Soap service is running over HTTPS, with Message credential of type certificate. I've got 2 endpoints that I use (besides mex), 1 for my normal service calls, the other for streaming files. Here's my web.config for the service (edited some names of items):
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
<basicHttpBinding>
<binding name="streamBinding" transferMode="Streamed" messageEncoding="Mtom" maxReceivedMessageSize="2147483646">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="Services.Behavior" name="MyInterface">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="wsHttpEndpoint" contract="IMyInterface" />
<endpoint address="stream" binding="basicHttpBinding" bindingConfiguration="streamBinding" name="streamEndpoint" contract="IMyInterface" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Service.Behavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
<serviceCredentials>
<serviceCertificate findValue="CN=Server.Temp.Dev" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
The service has a method, DownloadDocument, that I'm testing. Here's the signature:
DocumentDownloadResponse DownloadDocument(DocumentDownloadRequest request);
It's worth noting the the service throws Exceptions when the data passed in is not valid, and DownloadDocument catches those exceptions and passes back the error message in the response object.
The DocumentDownloadRequest looks like this:
[MessageContract]
public class DocumentDownloadRequest
{
[MessageHeader]
public string SecurityToken { get; set; }
[MessageHeader]
public string LoginName { get; set; }
[MessageHeader]
public string DocumentId { get; set; }
}
And DownloadDocumentResponse:
[MessageContract]
public class DocumentDownloadResponse : ServiceResponse<Stream>
{
public DocumentDownloadResponse()
{
Data = Stream.Null;
}
[MessageHeader(MustUnderstand = true)]
public bool Success { get; set; }
[MessageHeader(MustUnderstand = true)]
public string ErrorMessage { get; set; }
[MessageBodyMember(Order = 1)]
public Stream Data { get; set; }
}
Here's how I call it from the client:
var soapServiceClient = new SoapServiceClient("streamEndpoint");
bool success;
Stream stream;
string errorMessage =
soapServiceClient.DownloadDocument(documentId, loginName, securityToken, out success, out stream);
serviceClient.Close();
Where SecurityToken and LoginName are items that need to validates. What's strange is that from my test client, when I call DownloadDocument with valid data, I am able to download the file perfectly as many times as I want. However, if I pass in an invalid LoginName or SecurityToken, I get error messages indicating incorrect data (as expected). If I pass in invalid data 3 times, however, the client times out. Running the service locally I don't get this issue, everythign runs as expected. Strangely enough, whenI run with fiddler open, I don't get this issue. When I run with the service on my dev server, I have the problems.
The configuration on the dev server matches what I run locally. Using the SvcTraceTool I don't see any errors, except that it only documents the first 2 successful calls, and not the one that failed. It almost makes me think the endpoint just closed itself somehow.
Cliffs:
1) Service with 2 endpoints, one of if which is streaming (the one I'm concerned with).
2) Able to use streaming endpoint to call method to Download files with valid data
3) Service correctly catches bad data 2 times, hangs the 3rd time. No logs in SvcTraceTool, client times out.
Any ideas?
Thanks
In order to answer Rodrigo, I figured I'd post more details:
First off, wrap your generated proxy class in something sort of like this to handle error properly:
public class ProxyWrapper<TServiceClientType, TResultType>
where TServiceClientType : class, ICommunicationObject
{
private static string _endPoint;
public ProxyWrapper(string endPoint = "")
{
_endPoint = endPoint;
}
public TResultType Wrap(Func<string, TServiceClientType> constructWithEndpoint,
Func<TServiceClientType, TResultType> codeBlock)
{
TResultType result = default(TResultType);
TServiceClientType client = default(TServiceClientType);
try
{
client = constructWithEndpoint(_endPoint);
result = codeBlock(client);
client.Close();
}
catch (Exception)
{
if (client != null)
{
client.Abort();
}
throw;
}
return result;
}
}
I then have a client class that wraps around the service calls. Here's the DownloadDocument method:
public MyServiceResponse<Stream> DownloadDocument(string loginName,
string documentId)
{
var proxyWrapper = new MyProxyWrapper<DocumentDownloadResponse>(StreamEndpoint);
DocumentDownloadResponse response =
proxyWrapper.Wrap((client) =>
{
Stream data;
bool success;
string errorMessage = client.DownloadDocument(documentId, loginName,
out success,
out data);
return new DocumentDownloadResponse
{
Data = data,
Success = success,
ErrorMessage = errorMessage
};
});
var result = new MyServiceResponse<Stream>
{
Success = response.Success,
ErrorMessage = response.ErrorMessage
};
if (!response.Success)
{
result.Data = null;
response.Data.Close();
}
else
{
result.Data = response.Data;
}
return result;
}
Note: MyProxyWrapper inherits from ProxyWrapper and specified the WCF client proxy class. Now the actual call looks like this:
var myClient = new MyClient();
var downloadDocumentResponse = myClient.DownloadDocument(someId);
using (
Stream output =
File.OpenWrite(someFilePath))
{
downloadDocumentResponse.Data.CopyTo(output, 2048);
downloadDocumentResponse.Data.Close();
}
Note the two areas that that I call .Close() on the stream, once after writing the file, and once is response.Success == false.