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
Related
I am trying to figure out how to provide endpoints etc to a WF Service that I created (MyFoo.xamlx). Typically when you create a Service you need to have the Service name="" reflect the type of service you've created? ie by filling out something like this:
<Service name="MyNameSpace.Foo" behaviorConfiguration="myFooBehaviorConfig">
This works in a typical .svc scenario and I can get that working fine. Only how does one map this concept across to a .xamlx service? In that if I have Foo.xamlx putting that inside the Service name doesn't work - or doesn't appear to work (as in no metadata is enabled).
If i then remove the behaviorConfiguration attribute from the Service and also remove the myFooBehaviorConfig from the <behavior/> tag - in that it essentially then takes on more of a global setting for the web config - boom! I can see the .xamlx exposed methods/operations?
<serviceBehaviors>
<behavior name="foo" >
vs
<serviceBehaviors>
<behavior >
At first this is great, no effort required but at the same time I don't want devs to access HTTP endpoints or netpipe for that matter. I only want them to come in through net.tcp port I specify. In order to do that I need to match the <Service name=""/> to the xamlx somehow?
I've also tried using the trick of creating an empty .svc file and then pointing to the xamlx that way (so that the devs always assume its a .svc and not a .xamlx)
<%# ServiceHost Language="C#" Debug="true" Service="ActivityHubService.xamlx" Factory="System.ServiceModel.Activities.Activation.WorkflowServiceHostFactory" %>
Again, if I remove naming the behavior etc this also works via foo.svc ..but i just can't seem to crack the whole matching a <service> with a freakin xamlx!! :) hehe.
Frustrating..
Anyone an expert in this space?
I'm gonna give this one to GuerreroTook as although the answer he gave was partially in the same area of right it did however spark a moment of "yeah maybe its that simple.." thinking..
The solution was this:
<services>
<service name="ActivityHubService" behaviorConfiguration="foo">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="netTCPStreamedBinding" contract="IService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://synapse.hub.security:669/SynapseHubSecurity/ActivityHubService" />
</baseAddresses>
</host>
</service>
</services>
Then inside a service i called PingService.svc I put this:
<%# ServiceHost Language="C#" Debug="true" Service="ActivityHubService.xamlx" Factory="System.ServiceModel.Activities.Activation.WorkflowServiceHostFactory" %>
That seemed to have done the trick!! :)
Not sure why but i swore I did this like 3x and it failed..but...i am also in need of sleep..
Hopefully this will help others in the same jam down the track.
Based on what you ask, I understand that you need to expose a WF Workflow as a WCF service and you want client to call directly WF through WCF infrastructure, is that right?
The problem you facing is that you need to provide the full qualified name of the class in order to the factory to find and activated your WF workflow. If you have a typical WP project inside VisualStudio the Full Qualified Name is the name of the project + the class name, try with this.
When you build your workflow xaml file the compile will generate a class that hold and the code and logic, and following the same behavior of the xaml (WPF and Silverlight) compiler the name of the class will be the same of the file (.xamlx)
I have a wcf webHttp endpoint and noticed today that when called httpGet with a long url ~364 total characters (http:// etc counted) IIS throws a 400 bad request error. Throwing a breakpoint in the code never gets reached. If I remove some parameters and have a url that is 354 the web service runs as expected.
I'm not sure where the issue is since urls can be like 2k. I'm not posting any data so I don't think I'm hitting a 4mb limit like from here
What gives?
Here's some wcf xml stuff.
<behaviors>
<endpointBehaviors>
<behavior name="REST">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<endpoint address="" behaviorConfiguration="REST" binding="customBinding" bindingConfiguration="jsonpBinding" contract="Interface"/>
<bindings>
<customBinding>
<binding name="jsonpBinding">
<jsonpMessageEncoding/>
<httpTransport manualAddressing="true"/>
</binding>
</customBinding>
</bindings>
<extensions>
<bindingElementExtensions>
<add name="jsonpMessageEncoding" type="Microsoft.Ajax.Samples.JsonpBindingExtension, service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</bindingElementExtensions>
</extensions>
I just removed the callback function name which significantly reduced the size of the url and it still threw a 400 error. This makes me think that there is a WCF size limit on the text that can be sent in as a string parameter.
here;'s some Contract stuff
[OperationContract]
[WebGet(UriTemplate = #"{userName}/layer({layerName})returnAttributes({attributeList})where({attributeName})({op})({attributeValue})", ResponseFormat = WebMessageFormat.Json)]
[JSONPBehavior(callback = "callback")]
DojoGrouping GetAttributes(string userName, string layerName, string attributeList, string attributeName, string attributeValue);
the issue is with attributeList which can be comma separated list.
so with a url call like
http://../demo/layer(OfficialsLookup)returnAttributes(USHOUSE,US_Photo,US_Web,US_Name,SENDIST,SEN_Name,SEN_Email,SEN_Party,SEN_Photo,REPDIST,REP_Name,REP_Email,REP_Party,REP_Web,REP_Photo)utmX(430)utmY(4502)
it busts. But if i shorten the return attribute text then it functions properly.
I've added I added the following entry into the registry:
Key Name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters
Class Name: <NO CLASS>
Last Write Time: 1/25/2011 - 3:34 PM
Value 0
Name: UrlSegmentMaxLength
Type: REG_DWORD
Data: 0x200
I rebooted the machine after adding it and am still receiving the same error. Is this the correct spot to be modifying the HTTP.sys settings that WCF and IIS are using? Is there a way to test that WCF is being affected by this registry value?
Here is a related post with no answer but saying that the httpsys stuff did not fix a 64 bit server which is what we are using.
Related Post
To fix our issue we had to use the .NET 4.0 framework. We moved the web service to run under a .net 4.0 app pool and changed the target framework to 4.0. This caused the site to spit out url is too long error instead of just a bad request. After adding a web config entry in
<system.web>
added
<httpRuntime maxUrlLength="500" />
for max url length, the service is up and running as expected.
WCF uses HTTP.sys to handle HTTP traffic. HTTP.sys has system wide settings to handle various restrictions around URL. You might be hitting one of them.
See the following article to find out those settings:
http://support.microsoft.com/kb/820129
You will need to restart http service and your WCF service. If it is hosted in IIS, restart IIS. UrlSegmentMaxLength seems to be an interesting for your URI template.
My WCF service exposes an https AND an http endpoint. Apart from the SSL they are identical. They map to the same code.
The ultimate intention is for external users to connect via https, internal users to use http.
In development this gives me a problem. Cassini, the development web server packaged in VS, hates SSL.
I'm wondering if I can configure the service from code, so when running under Cassini, I would not configure https.
Hence the question - How do I configure the service from code if it is IIS hosted? I'd be very happy with alternative answers on how I can persuade Cassini to NOT complain about the https part of the configuration.
"IIS will take care of spinning up the necessary ServiceHost based on your *.svc file - not a whole lot you can do about that, really."
Not too close to the truth. Exactly in the SVC file of your service there is attribute named Factory. Where you can specify the the class and the assebly where the class is located. This class may be your own descendant of Web|DataServiceHostFactory
So your svc markup would look like this
<%# ServiceHost
Language="C#"
Debug="true"
Service="name.space.myService"
CodeBehind="name.space.myService.svc.sc"
Factory = "name.space.WebServiceHostFactoryEx, assembly.name"
%>
The WebServiceHostFactory will be created for every service hit and will recreate your host the way you want it.
You will also need to inherith WebServiceHost and create it the way you need it with certain endpoins, behaviors, addresses, etc settings - whatever you like.
There is very nice post from Michele Bustamante here
EDIT: I figured out the above link is not working anymore, so here it is another one.
I am using this in IIS hosted enviroment for couple of services that are initialized same way.
When you're hosting in IIS, you're leaving a lot of care taking into the realm of IIS - you cannot really grab a hold of your service in this case.
IIS will take care of spinning up the necessary ServiceHost based on your *.svc file - not a whole lot you can do about that, really.
My solution would be different - externalize the <service> tag in your configuration file (web.config):
<system.serviceModel>
<services>
<service configSource="service.dev.config" />
</services>
</system.serviceModel>
In your dev environment, only expose the http endpoint - so your service.dev.config would look something like this:
<service name=".....">
<endpoint name="default"
address="....."
binding="basicHttpBinding" bindingConfiguration="insecure"
contract="......" />
</service>
Create a second service.prod.config which then contains both endpoints - http and https:
<service name=".....">
<endpoint name="default"
address="....."
binding="basicHttpBinding" bindingConfiguration="insecure"
contract="......" />
<endpoint name="secure"
address="....."
binding="basicHttpBinding" bindingConfiguration="secure"
contract="......" />
</service>
and reference that in your web.config on the deployment server.
I've been having major problems with WCF, which are not amenable to any wisdom I can find. I've tried basicHttpBinding, wsHttpBinding, netTcpBinding (with the Net.Tcp service running on both machines). The behaviour can be boiled down to:
works on same machine, when running in debugger, standalone (non-service) process, Windows service
cannot be accessed from other machine, unless I'm running the service in the debugger
I must confess to being baffled by the plethora of options for bindings/security/etc. I must also confess to being ignorant of the restrictions on running as a Windows service, although I'm sure I've read somewhere that the SYSTEM account does not have network credentials. I've tried running the service under my own credentials, with the same results.
I'm up against a deadline in a few hours, and at the moment I'm going to have to resort to running as a standalone process, which is pretty embarassing.
I'm sure I've made some simple but crucial mistake in my understanding, and would be happy to be enlightened. But for now I'd be happy if someone knows a quick and dirty way to run WCF between two machines on the same Windows network without any security necessary, where one is a windows service and the other is a Windows GUI app.
This calls for the NetNamedPipe binding (on-machine communication)!
Your config would have to look something like:
<system.serviceModel>
<bindings>
<netNamedPipeBinding>
<binding name="NoSecurityIPC">
<security mode="None" />
</binding>
</netNamedPipeBinding>
</bindings>
<client>
<endpoint name="internal"
address="channel1"
binding="netNamedPipeBinding"
bindingConfiguration="NoSecurityIPC"
contract="IYourService" />
</client>
<services>
<service name="Namespace.YourService">
<host>
<baseAddresses>
<add baseAddress="net.pipe://localhost/" />
</baseAddresses>
</host>
<endpoint
address="channel1"
binding="netNamedPipeBinding"
bindingConfiguration="NoSecurityIPC"
contract="IYourService" />
</service>
</services>
</system.serviceModel>
Marc
Is it a Windows Firewall issue? BasicHttpBinding defaults to security mode "none", and setting it on netTcp for client and service is as simple as putting a security mode="none" element under the root binding element in both the client and server config. Seems like the firewall on the server would be the only thing that'd keep you from connecting if the security mode is set to none.
1) as well as opening the firewall, you almost certainly need to explicitly permit binding the serving port via the APIs on HTTP.SYS. This can be done by the built in netsh http add command on Vista or later, via the downloadable httpcfg tool on earlier systems, or by directly P/Invoking against the HTTP APIs with administrative privilege as a set-up step.
2) if you have multiple services, getting them to share the address space on a given port is far easier if they talk HTTP than net.tcp
3) as a default, a service that talks across the network should be run with Network Service identity, ideally as a service-specific SID : even if the data are not sensitive, exposing a high privilege user like System on the network is not good practice.
I have a set of Service Contracts which split up my service interface into chunks of related functionality. I am currently implementing all contracts using a single service class (may want to split these later but for now the single service class suffices).
I am trying to use configure the endpoints using a config file (as opposed to via code). The problem is I get a ServiceActivationException because the two endpoints (one for each service contract) are trying to listen on the same uri. The exception details say that to achieve this the two endpoints must share the binding object, which makes sense but I can't figure out how to do this via config (I haven't tried doing this via code as I am hosting in IIS but I can imagine it being a simple exercise to configure in code).
The following is the config I am currently using (this is still dev so I'm not currently worried about security concerns etc. that some of these settings may expose):
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<services>
<service name="CDC.WebPortal.MidTier.MidTierAccessService"
behaviorConfiguration="MidTierServiceBehaviour" >
<endpoint address=""
binding="webHttpBinding"
bindingConfiguration="RestBindingConfiguration"
contract="****************************.IProductService" />
<endpoint address=""
binding="webHttpBinding"
bindingConfiguration="RestBindingConfiguration"
contract="****************************.ICategoryService" />
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="RestBindingConfiguration"
maxReceivedMessageSize="104857600">
<readerQuotas maxStringContentLength="104857600"/>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="MidTierServiceBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
So my question is how do I share this binding between the two endpoints?
Comments in this SO question suggest I may not be able to do this, but I don't beleive that is correct.
UPDATE 1 According to this MS publication what I'm doing should be ok...
UPDATE2 Here is the svc file content if it helps:
<%# ServiceHost Language="VB" Debug="true"
Service="*********************.MidTierAccessService"
Factory="Microsoft.ServiceModel.Web.WebServiceHost2Factory" %>
UPDATE 3 Here is the exception detail:
A binding instance has already been associated to listen URI '********************'. If
two endpoints want to share the same ListenUri, they must also share the same binding
object instance. The two conflicting endpoints were either specified in
AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint()
and config.
UPDATE 4 Ok I missed this before, stating "You will need to use relative addresses when exposing more than one endpoint for a particular .svc service". The cause of this is something to do with the IIS virtual directory determining the base address of the service, can anyone explain this in a little more detail, i.e. why IIS needs relative addressing for each contract.
To my knowledge, and I have been doing extensive work with WCF in the last month, you can not share the same exact URI for more than one endpoint. In WCF, a "service" is not defined by the implementation of a contract, but by the contract itself (which also follows WSDL and standard SOA practices.) Endpoints allow you to expose a single service via multiple protocols (and therefor different addresses), but you can not share different services on the same exact address. Logically that wouldn't work.
Assume the following scenario (which is what you are trying to accomplish):
IProductService exposed # http://localhost/service
ICategoryService exposed # http://localhost/service
IMetadataExchange exposed # http://localhost/service/mex
It is easy enough to access the MEX endpoint...it has a unique URI. However, how do you access either of IProductService or ICategoryService? There is nothing that allows you to differentiate the two other than a URI. WCF has nothing that will allow it to route between messages that are supposed to go to IProductservice, and those that are supposed to go to ICategoryService. Since both use the same URI, you do indeed have a conflict. Every service CONTRACT must be exposed via a unique URI. Every endpoint that utilizes the same exact binding must use a distinct address.
There is a way to achieve what you need. The problem is message routing. WCF does not natively support message routing OOB, however it does provide the ability to implement your own message router. (Or, if you are willing to use beta tech, .NET 4.0 comes with a message router out of the box, based on the articles linked below, but with improved configurability.) Michele Bustamante, a veritable sorceress of WCF, has provided a complete implementation and article describing message routing at the following links:
http://msdn.microsoft.com/en-us/magazine/cc500646.aspx
http://msdn.microsoft.com/en-us/magazine/cc546553.aspx
The general idea is that you set up a single service that listens on a single URI. This service uses wildcard dispatch to a single service operation, which then determines which unique URI to route each message to. You can make the determination any way you wish, however the simplest is via the request Action, assuming each action on your two interfaces, IProductService and ICategoryService, are globally unique. You will end up with more services, however...the router itself is a distinct WCF service that would need to be hosted just like any other.