Im building a WPF 3.5 desktop app that has a self-hosted WCF service.
The service has an PollingDuplexHttpBinding endpoint defined like so:
public static void StartService()
{
var selfHost = new ServiceHost(Singleton, new Uri("http://localhost:1155/"));
selfHost.AddServiceEndpoint(
typeof(IMyService),
new PollingDuplexHttpBinding(PollingDuplexMode.MultipleMessagesPerPoll) {ReceiveTimeout = new TimeSpan(1,0,0,0)},
"MyService"
);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
selfHost.Description.Behaviors.Add(smb);
selfHost.AddServiceEndpoint(typeof(IPolicyRetriever), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
selfHost.Open();
}
Note: the IPolicyRetriever is a service that enables me to define a policy file
This works and I can see my service in my client Silverlight application. I then create a reference to the proxy in the Silverlight code like so:
EndpointAddress address = new EndpointAddress("http://localhost:1155/MyService");
PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding(PollingDuplexMode.MultipleMessagesPerPoll);
binding.ReceiveTimeout = new TimeSpan(1, 0, 0, 0);
_proxy = new MyServiceClient(binding, address);
_proxy.ReceiveReceived += MessageFromServer;
_proxy.OrderAsync("Test", 4);
And this also works fine, the communication works!
But if I leave it alone (i.e. dont sent messages from the server) for longer than 1 minute, then try to send a message to the client from the WPF server application, I get timeout errors like so:
The IOutputChannel timed out attempting to send after 00:01:00. Increase the timeout value passed to the call to Send or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout.
Its all running on localhost and there really should not be a delay, let alone a 1 minute delay. I dont know why, but the channel seems to be closed or lost or something...
I have also tried removing the timeouts on the bindings and I get errors like this
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it has been Aborted
How can I try to find out whats wrong here?
WPF uses wsDualHttpBinding, Silverlight - Polling Duplex.
WPF solution is simple; Silverlight requires ServiceHostFactory and a bit more code. Also, Silverlight Server never sends messages, rather Client polls the server and retrieves its messages.
After many problems with PollingDuplexHttpBinding I have decided to use CustomBinding without MultipleMessagesPerPoll.
web.config
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="SlApp.Web.DuplexServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service behaviorConfiguration="SlApp.Web.DuplexServiceBehavior" name="SlApp.Web.DuplexService">
<endpoint address="WS" binding="wsDualHttpBinding" contract="SlApp.Web.DuplexService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
DuplexService.svc:
<%# ServiceHost Language="C#" Debug="true" Service="SlApp.Web.DuplexService" Factory="SlApp.Web.DuplexServiceHostFactory" %>
DuplexServiceHostFactory.cs:
public class DuplexServiceHostFactory : ServiceHostFactoryBase
{
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
return new DuplexServiceHost(baseAddresses);
}
}
class DuplexServiceHost : ServiceHost
{
public DuplexServiceHost(params Uri[] addresses)
{
base.InitializeDescription(typeof(DuplexService), new UriSchemeKeyedCollection(addresses));
}
protected override void InitializeRuntime()
{
PollingDuplexBindingElement pdbe = new PollingDuplexBindingElement()
{
ServerPollTimeout = TimeSpan.FromSeconds(3),
//Duration to wait before the channel is closed due to inactivity
InactivityTimeout = TimeSpan.FromHours(24)
};
this.AddServiceEndpoint(typeof(DuplexService),
new CustomBinding(
pdbe,
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement()), string.Empty);
base.InitializeRuntime();
}
}
Silverlight client code:
address = new EndpointAddress("http://localhost:43000/DuplexService.svc");
binding = new CustomBinding(
new PollingDuplexBindingElement(),
new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement()
);
proxy = new DuplexServiceClient(binding, address);
Related
I have 5 SilverLight projects in my ASP .NET MVC 4 application. For each of these projects, there is a web service which passes data to and from the silverlight control.
When we deploy the application on the client's server, we update the .ClientConfig file of each SilverLight project by changing the address of the web service.
That is we change -
<endpoint address="http://localhost:52213/SchemeReimbursementMasterService.asmx"
binding="basicHttpBinding" bindingConfiguration="SchemeReimbursementMasterServiceSoap"
contract="SchemeServiceReference.SchemeReimbursementMasterServiceSoap"
name="SchemeReimbursementMasterServiceSoap" />
to -
<endpoint address="http://192.168.5.48/SCHEME/SchemeReimbursementMasterService.asmx"
binding="basicHttpBinding" bindingConfiguration="SchemeReimbursementMasterServiceSoap"
contract="SchemeServiceReference.SchemeReimbursementMasterServiceSoap"
name="SchemeReimbursementMasterServiceSoap" />
Then we build it and publish the application.
So, in future, if the address changes, we would have to change it in all the five .ClientConfig files and build/publish it again. Is there any way we can remove this hardcoding so that it figures out the base part of the url (i.e: http://192.168.5.48/SCHEME/) on its own.
In my application's web.config, I added keys for the service names.
<appSettings>
<add key="ReviewUrl" value="ReviewClaimsService.asmx"/>
<add key="ReimbursementUrl" value="SchemeReimbursementMasterService.asmx"/>
<add key="DiscountUrl" value="DiscountMasterSchemeService.asmx"/>
</appSettings>
In the controller, I created the URL using the HttpContext and the service name from web.config and passed it to the view.
var baseUrl = "http://" + HttpContext.Request.Headers["Host"];
var applicationPath = HttpContext.Request.ApplicationPath;
if (applicationPath == "/")
baseUrl = baseUrl + "/";
var url = baseUrl + ConfigurationManager.AppSettings["ReimbursementUrl"];
In the View, I passed this URL to the silverlight control using InitParams.
<object id="TownsGrid" data="data:application/x-silverlight-2," type="application/x-silverlight-2">
<param name="InitParams" value="ControlName=TownDataGrid,ServiceUrl=#Model.ServiceUrl" />
<param name="source" value="#Url.Content("~/ClientBin/SilverlightApplication.xap")"/>
</object>
In the Application_Startup method of the silverlight project, I passed the URL on to the SilverLight control.
private void Application_Startup(object sender, StartupEventArgs e)
{
var url = e.InitParams["ServiceUrl"];
this.RootVisual = new SilverlightApplication.TownDataGrid(url);
}
And finally, in the silverlight control, I assign the url to the endpoint.
public TownDataGrid(string serviceUrl)
{
InitializeComponent();
EndpointAddress endpoint = new EndpointAddress(serviceUrl);
BasicHttpBinding binding = new BasicHttpBinding();
client = new SchemeReimbursementMasterServiceSoapClient(binding,endpoint);
}
I'm tryin to run a WCF service within a Windows Forms Application. I copied and modified code found in the WCF samples from Microsoft. When running the WCF Sample the service shows up in a port monitor (CurrPorts) I use. When I run my code I can't see my service...
This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace NoName.Server
{
[ServiceContract(Namespace="http://NoName")]
public interface IApplicationService
{
[OperationContract()]
NoName.Entities.MediaParameter[] GetParametersForMediaObject(string mediaObjectId);
[OperationContract()]
NoName.Entities.MediaParameter GetMediaParameter(string parameterId);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NoName.Server
{
public class ApplicationService : IApplicationService
{
public Entities.MediaParameter[] GetParametersForMediaObject(string mediaObjectId)
{
throw new NotImplementedException();
}
public Entities.MediaParameter GetMediaParameter(string parameterId)
{
throw new NotImplementedException();
}
}
}
I'm running it from a Form as
private void toolStripButton1_Click(object sender, EventArgs e)
{
using (ServiceHost host = new ServiceHost(typeof(ApplicationService)))
{
host.Open();
}
}
And the configuration in app.config:
<system.serviceModel>
<services>
<service name="NoName.Server.ApplicationService" behaviorConfiguration="ApplicationServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/NoName/ApplicationService"/>
</baseAddresses>
</host>
<!-- this endpoint is exposed at the base address provided by host: http://localhost:8000/NoName/ApplicationService -->
<endpoint address="" binding="wsHttpBinding" contract="NoName.Server.IApplicationService"/>
<!-- the mex endpoint is exposed at soap.tcp://localhost:8000/NoName/ApplicationService/mex -->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<!--For debugging purposes set the includeExceptionDetailInFaults attribute to true-->
<behaviors>
<serviceBehaviors>
<behavior name="ApplicationServiceBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
It compiles without errors, no exceptions when I run it. It just won't exist. Ideas?
Well - you're using a using block, which is normally a good thing - but here, the service host will be closed again right away at the end of the using block - which is certainly not what you want!
using (ServiceHost host = new ServiceHost(typeof(ApplicationService)))
{
host.Open(); // host is opened here
} // host is disposed and closed here
So your ServiceHost was open and ready to receive requests - for a fraction of a second only... and then it was closed and disposed of since the using { .. } block ended.....
What you need to do is this:
add a private member variable to e.g. your main form
private ServiceHost _serviceHost = null;
open that service host at some point in your code, e.g. in your method you have there:
private void toolStripButton1_Click(object sender, EventArgs e)
{
_serviceHost = new ServiceHost(typeof(ApplicationService));
}
leave it open until e.g. the Winforms app closes (or the user choses some other menu item to actually close the service host)
I'm running SL4 on VS2010. I've got an app that authenticates via a web service to SPROC in my db. Unfortunately this is not WCF/WCF RIA, as I'm inheriting the DB/services from my client.
This works perfectly inside of a browser (via HTTPS). I'm attempting to move this OOB, and it's at this point that my authentication fails. Here's the steps I took...
1) SL App Properties > Enable running app Out of Browser
2) SL App Properties > Out of Browser Settings > Require elevated trust when running OOB
If i set a breakpoint on my logon button click, I see the service call is being made. However, if I step through it (or set a breakpoint on the actual logon web service), the code never gets that far. Here's the block it fails on:
public LogonSVC.LogonResponse EndLogon(System.IAsyncResult result) {
object[] _args = new object[0];
LogonSVC.LogonResponse _result = ((LogonSVC.LogonResponse)(base.EndInvoke("Logon", _args, result)));
return _result;
}
I know using Elevated Trust means the crossdomain.xml isn't necessary. I dropped one in that allows everything, just to test, and that still fails.
here's the code that calls the service:
private void loginButton_Click(object sender, RoutedEventArgs e)
{
string Username = txtUserName.Text;
string Password = txtPassword.Password;
Uri iSilverlightServiceUriRelative = new Uri(App.Current.Host.Source, "../Services/Logon.asmx");
EndpointAddress iSilverlightServiceEndpoint = new EndpointAddress(iSilverlightServiceUriRelative);
BasicHttpBinding iSilverlightServiceBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);// Transport if it's HTTPS://
LogonService = new LogonSVC.LogonSoapClient(iSilverlightServiceBinding, iSilverlightServiceEndpoint);
LogonService.LogonCompleted += new EventHandler<LogonSVC.LogonCompletedEventArgs>(LogonService_LogonCompleted);
LogonService.LogonAsync(Username, Password);
}
My LogonService_LogonCompleted doesn't fire either (which makes sense, just a heads up).
I don't know how to fiddler this, as this is running OOB with the site served via localhost/IIS. I know this works though in browser, so I'm curious what would break it OOB.
UPDATE
I changed my ServiceReferences.ClientConfig to relative URLs instead of absolute, at the recommendation of another post I read. Here's the code:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="CommonSoap" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="Transport" />
</binding>
<binding name="LogonSoap" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="/Services/Common.asmx"
binding="basicHttpBinding" bindingConfiguration="CommonSoap"
contract="CommonSVC.CommonSoap" name="CommonSoap" />
<endpoint address="/Services/Logon.asmx"
binding="basicHttpBinding" bindingConfiguration="LogonSoap"
contract="LogonSVC.LogonSoap" name="LogonSoap" />
</client>
</system.serviceModel>
UPDATE 2
Stack trace, if it helps anyone...
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.ClientBase`1.ChannelBase`1.EndInvoke(String methodName, Object[] args, IAsyncResult result)
at TSMVVM.TSMVVMLogonSVC.LogonSoapClient.LogonSoapClientChannel.EndLogon(IAsyncResult result)
at TSMVVM.TSMVVMLogonSVC.LogonSoapClient.TSMVVM.TSMVVMLogonSVC.LogonSoap.EndLogon(IAsyncResult result)
at TSMVVM.TSMVVMLogonSVC.LogonSoapClient.EndLogon(IAsyncResult result)
at TSMVVM.TSMVVMLogonSVC.LogonSoapClient.OnEndLogon(IAsyncResult result)
at System.ServiceModel.ClientBase`1.OnAsyncCallCompleted(IAsyncResult result)
Thank you,
Scott
I'm not allowed to comment so I'll have to post this question as an answer
Is it because Silverlight treats all http error codes other than 200 as a 404.
Use fiddler to check the response status code and try an http handler to change it to a 200. This was a common problem in silverlight 2 which I thought they'd fixed but maybe the fix was only for WCF and RIA services.
I have created a very basic service operation that needs to write content to my database. This service looks like the following:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(IncludeExceptionDetailInFaults = false)]
public class myService : ImyService
{
public MyServiceResult MyMethod(string p1, string p2)
{
try
{
// Do stuff
MyResponseObject r = new MyResponseObject();
r.Property1 = DateTime.Now;
r.Property2 = "Some other data";
return r;
}
catch (Exception ex)
{
return null;
}
}
}
ImyService is defined as shown here:
[ServiceContract]
public interface ImyService
{
[OperationContract]
[WebInvoke(BodyStyle = WebMessageBodyStyle.Wrapped)]
MyServiceResult MyMethod(string p1, string p2);
}
This service will be exposed to both WP7 and iPhone client applications. Because of this, I believe I need to use webHttpBinding. This has caused me to use the following settings in my web.config file:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="myServiceBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
<services>
<service name="myService">
<endpoint address=""
behaviorConfiguration="myServiceBehavior"
binding="webHttpBinding"
contract="ImyService" />
</service>
</services>
</system.serviceModel>
Both the service and WP7 app are part of the same solution. I can successfully add a reference to the service in my application. When I run the application though, the page that references the service throws an error. The error says:
Could not find default endpoint element that references contract 'MyServiceProxy.ImyService' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.
What am I doing wrong? It just seems like this should be a pretty straightforward thing. Thank you for your help.
Have you copied the file "ServiceReferences.ClientConfig" into your Windows Phone 7 project? This file is in your WCF project. Also, WP7 clients support basicHttpBinding only. So, you may see an empty "ServiceReferences.ClientConfig" file unless you switch over to basicHttpBinding
Hi guys I just want a simple WinForm app with one button. When I press the button
i want to start the selfhosted WCF service. I want to able to connect to this service with for example another client app (winforms) by just adding a service reference.
However the solution that I created is not working. I can't get connected with adding a service reference to this service. I don't actually know what address to call than except the address that I defined in the app.config file. Any help would be great.
Here is the app.config file.
<configuration>
<system.serviceModel>
<services>
<service name="WindowsFormsApplication11.WmsStatService">
<endpoint address="http://192.168.0.197:87" binding="basicHttpBinding"
bindingConfiguration="" contract="WindowsFormsApplication11.IWmsStat"/>
</service>
</services>
</system.serviceModel>
</configuration>
And forms code:
namespace WindowsFormsApplication11
{
public partial class Form1 : Form
{
public ServiceHost _host = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
_host = new ServiceHost(typeof(WmsStatService));
_host.Open();
}
}
// Define a service contract.
[ServiceContract(Namespace = "http://WindowsFormsApplication11")]
public interface IWmsStat
{
[OperationContract]
string sayHello(string name);
}
public class WmsStatService : IWmsStat
{
public string sayHello(string name)
{
return "hello there " + name + " nice to meet you!";
}
}
}
I changed the app.config file. The problem is solved. Also thanks for the tips and your answers. The config is changed to.
<configuration>
<system.serviceModel>
<services>
<service name="WindowsFormsApplication11.WmsStatService" behaviorConfiguration="mex">
<host>
<baseAddresses>
<add baseAddress="http://192.168.0.197:87/" />
</baseAddresses>
</host>
<endpoint address="http://192.168.0.197:87/Test" binding="basicHttpBinding" bindingConfiguration="" contract="WindowsFormsApplication11.IWmsStat" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="mex">
<serviceMetadata httpGetEnabled="true" httpGetUrl=""/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
The host should open on http://192.168.0.197:81 as in the config file.
So once the host is up and running then, try and broswe to it using the service reference.
I assume that the address is that your machine, and you don't have anything else on that port address. The other things to check are firewalls blocking that port.
I would not have the service class that implements the service contract (interface) be the form - make it a separate interface, a separate class. The reasoning behind this is the fact that the service host will have to create (instantiate) one instance of the service class for each request it needs to handle --> make those classes as small as possible and don't bloat them by baggage (like the Winform) that they don't need for their job!
Then instantiate a ServiceHost inside your Winform - but make that a global member variable of the form! Otherwise, the ServiceHost is gone once your ButtonClick event is finished!
// Define a service contract.
[ServiceContract(Namespace = "http://WindowsFormsApplication11")]
public interface IWmsStat
{
[OperationContract]
string sayHello(string name);
}
public class YourServiceClass : IWmsStat
{
public string sayHello(string name)
{
return "hello there " + name + " nice to meet you!";
}
}
public partial class Form1 : Form
{
private ServiceHost _host = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Create a ServiceHost for the CalculatorService type and
// provide the base address.
_host = new ServiceHost(typeof(YourServiceClass));
// Open the ServiceHostBase to create listeners and start
// listening for messages.
_host.Open();
}
Don't mix the class that contains the ServiceHost, with the ServiceClass (which will need to be instantiated by the host to satisfy incoming requests) - the Service implementation should be standalone, and as lean as possible!
Also, it's good practice to follow the Single Responsability Principle - one class should have one job and one job only - don't pack up your whole app logic into a single, huge class - separate out the different jobs into separate classes and compose those together.
Marc