IQueryable problems using WCF - c#

I have a quite simple WCF service method which returns an IQueryable, just for testing. Perhaps I got something wrong when trying to understand what IQueryable is designed for. I clearly plan to use this with the IQueryable provider of NHibernate later. But first I ran into some sort of serialization problems (at least I think it might be the problem) whenever using a WCF method returning an IQueryable. It doesn't even work for a simple string.
Here's my code:
public IQueryable<string> GetEquipmentConfigurations()
{
var returnValue = new List<string>();
returnValue.Add("test");
return returnValue.AsQueryable();
}
It might not have much sense, it's just for testing whether I really get those IQueryables over the wire using WCF. Whenever I call this method using a client like SoapUI I get a socket exception and a connection reset, just the same as if I was trying to return something that is not marked as DataContract. But the only thing I do here is trying to return some lousy string list. What's wrong with that?
I use basicHTTPBinding, here are my settings:
<system.serviceModel>
<services>
<service name="EquipmentConfigurationService" behaviorConfiguration="DefaultBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/Krones.KBase/Services/EquipmentConfigurationService"/>
</baseAddresses>
</host>
<endpoint address=""
binding="basicHttpBinding"
contract="Krones.MES.KBase.Public.Service.EquipmentDefinition.IEquipmentConfigurationService" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="DefaultBehavior">
<serviceMetadata httpGetEnabled="True"/>
<serviceDebug includeExceptionDetailInFaults="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
The OperationContract attribute is set for the interface:
[OperationContract]
IQueryable<string> GetEquipmentConfigurations();
It all works well when just returning a simple string. Anyway I want to take profit from the IQueryable features using LINQ later.
Anybody any idea what's going wrong here?
Thanks and Cheers,
Stefan

The core WCF is designed to send data, not queries. Stick to returning List<Foo> etc; it'll save you much head-scratching.
However, you might have more luck doing what you are after with WCF Data Services, which allows you to expose IQueryable<> sources.
The way this works is that the tooling builds a client that exposes similar looking IQueryable<> hooks; when you query data, it represents the expression on the wire, queries the data and brings it back to the client. But it is still the results (not the query) that goes over the wire.

(Obsolete)
AFAIK it isn't possible out of the box to serialize IQueryable<> or Expression Trees (think about it - it would mean that the expression tree / lambda would need to be serialized and the function rebuilt)
However, where there is a will, it seems there is a way - you might want to look at projects such as this
http://code.msdn.microsoft.com/exprserialization
Edit : Note that times have changed - See WCF RIA Services as per Marc Gravell's post.
Good luck!
HTH

Related

Can you mimic an existing service in WCF?

We have a 3rd party vendor service and an in-house service to communicate with it. Everything works perfectly with our in-house service, but I've been asked to write a fall back clone of our vendor's service. The intention is to be able to run up our clone, swap-out the client end-point and allow our in-house service to continue testing against the clone.
Is it possible to recreate/mimic a service such that an existing client can communicate with it (without modification) as if it were the original service?
So far I’ve tried 3 things and none of them work.
1st approach
Create a simple service, reference the 3rd party service to gain access to the custom types and mimic the [operation Contract]’s.
When I try to communicate with this service I get the following error.
A first chance exception of type 'System.ServiceModel.ActionNotSupportedException' occurred in System.ServiceModel.dll
Additional information: The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
There is no security requirements as we are using basic http (no ssl). The service model portion of the config file and the service behaviour class attributes are below:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="SimpleBinding" />
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="cloneBehavior" name="MyClone">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="SimpleBinding"
contract="MyService.IMyService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost/clone/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="cloneBehavior">
<serviceMetadata httpGetEnabled="True" httpGetUrl="http://localhost/clone/mex" />
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
and the service behaviour
[ServiceBehavior(Name = "CloneService",
ConfigurationName = "MyClone",
InstanceContextMode = InstanceContextMode.PerCall,
AddressFilterMode = AddressFilterMode.Any)]
public class MyService : IMyService
{
Everything looks good and I have come to my first dead end.
2nd approach
Get rid of my interface/service contract and inherit directly from the interface generated in the reference.cs file.
When I run this service up, I get the following error
System.InvalidOperationException: The operations myMethodA and myMethodB have the same action (). Every operation must have a unique action value.
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ActionDemuxer.Add(String action, DispatchOperationRuntime operation)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime..ctor(DispatchRuntime dispatch)
at System.ServiceModel.Dispatcher.DispatchRuntime.GetRuntimeCore()
at System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpened()
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at Microsoft.Tools.SvcHost.ServiceHostHelper.OpenService(ServiceInfo info)
Taking a look at the generated interface for this method, they are all decorated with the following attribute:
[System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
My understanding from msdn is that WCF defaults to adding a unique action of the pattern <namespace>/<service>/<operation>[Response].
If I try setting the action to * then I can hit the service (as expected with the catch all / unmatched message handler), but I can't hit a specific method.
Nevertheless, manually making all the actions unique (and conforming to the above pattern), give this error:
The contract ‘IMyService’ in client configuration does not match the name in service contract, or there is no valid method in this contract.
…
I have clearly defined methods in the service contract and have come to a second dead end.
3rd approach
Using the wsdl.exe tool to generate a service from the wsdl. I followed the instructions from this SO post to generate an interface and inherit from it.
I’ve also tried generating the service itself by using the clientWsdl.wsdl /l:CS /server command and following the instructions in this post.
After tidying up the generated code and running it up, I’m back to my original error:
A first chance exception of type 'System.ServiceModel.ActionNotSupportedException' occurred in System.ServiceModel.dll
Additional information: The message with Action '' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
At each attempt I have double checked all the config settings and updated the contract at each stage.
At this stage I'm wondering if it's even possible.
Were you ever able to mimic the service using WCF? I find myself in a similar situation where I've been tasked to write some integration tests where our method makes a call to a vendor's web service. I would have preferred to leverage Moq to write proper unit tests, but I'm constrained by 1) not being allowed to change the signature of the method that I'm testing, and 2) not really knowing anything about the web service itself except for what we send to the service and its expected response.
#jparram's suggestion was my next approach.
Note: I would have preferred to have posted this as a comment seeing that I'm not really answering your question.
If I understand your scenario, I would create wrapper service that decides which service to call. It sounds like that's what you want to do with Scenario 1. So, your client would always be calling your wrapper service, and your wrapper service would map the inputs to the required inputs of your 3rd party service or your back up service.
The error you received sounds like an issue in the generation of the proxy client where the "Action" is not getting mapped. Take a look at the generated proxy code from a known working configuration and compare it to what is in your scenario 1.
Edit
Compare the generated proxy client of your service and the 'real' service. The complaint about the Action = "" is probably due to the:
[System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="")]
On your client not getting mapped to the corresponding operation.

WCF call that references Windows Service

I have a long-running service that gets data from one source, manipulates it, stores it in a database, etc.
I'd like to expose some methods on that service to other applications. Currently we do this via .NET Remoting but I'd like to move to WCF.
Unfortunately, I the endpoint I connect to is never the one I exposed via my long-running service. Below is a simple example:
[ServiceContract]
public interface ITestWcfService
{
[OperationContract]
CounterResult GetCurrentValue();
}
public class TestWcfService : ITestWcfService
{
private ITestWindowsService _service;
public TestWcfService() { /*NOTE: For discoverability*/ }
public TestWcfService(ITestWindowsService service)
{
_service = service;
}
public CounterResult GetCurrentValue()
{
return _service.GetCurrentValue();
}
}
public interface ITestWindowsService
{
CounterResult GetCurrentValue();
}
Then I have my actual Windows Service, which self-hosts the WCF service via the ServiceHost class.
public partial class TestWindowsService : ServiceBase, ITestWindowsService
{
private static ServiceHost _wcfService;
public TestWindowsService()
{
InitializeComponent();
}
public void OnStart(string[] args)
{
//Create instance of WCF, passing in reference to this service instance
TestWcfService wcf = new TestWcfService(this);
_wcfService = new ServiceHost(wcf);
}
public CounterResult GetCurrentValue()
{
//Note: Some logic here
}
}
Now, this more-or-less works except that each time I make a call to the TestWcfServiceClient(), it uses the default constructor and creates a new instance of the Wcf Service and does not use the instance created by the windows service. This means that when I call GetCurrentValue() I get a null reference because the _service member hasn't been set.
I've looked around for solutions and found some citing ServiceHostFactory and ServiceHost and IInstanceProvider but each has seemed to be very, very complicated.
Any thoughts you could provide would be most appreciated.
EDIT: Here's my ServiceModel info
<system.serviceModel>
<services>
<service name="WcfService.TestWcfService">
<host>
<baseAddresses>
<add baseAddress = "http://localhost:8733/Design_Time_Addresses/WcfService/TestWcfService/" />
</baseAddresses>
</host>
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<endpoint address="" binding="basicHttpBinding" contract="WcfService.ITestWcfService">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<!-- Metadata Endpoints -->
<!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
<!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</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>
</system.serviceModel>
WCF is VERY extensible, but in order to provide such extensibility it is also very complicated. In my opinion, more than it should be so if you want to use this implementation that's the way to go. YOu have to create a behavior that changes the service factory so you can use the custom constructor instead of the empty one.
However, I think there might be another alternative. Since both the service and the WCF share the same app domain they can share values through static objects.
I would move the logic that carries the value you want to expose to un underlying assembly (if you didn't already) and both the service and WCF service would see the same instance therefore you don't need to change all the WCF plumbing.
You are trying to put a square peg in a round hole.
WCF is explicitly designed to enable calling business logic independent of how it is hosted. You are trying to maintain the old remoting design that has your business logic in the host layer (in this case a Window's service). As a result you are asking to pass information back up to the host in a way that is possible in WCF but ugly and is generally avoided for many good reasons.
If you want a clean WCF design you will need to create abstraction layers and move your business logic out of the existing TestWindowsService class. Then the windows service creates the WCF service host, the WCF host creates the WCF service, and the WCF service is not dependent on the classes that host it.
Having said all that...
To actually answer your question:
Conceptually the code that you have written should work. Passing an object instance into the ServiceHost constructor is by far the easiest way to avoid the complexity of writing a custom IInstanceProvider. A couple of things to check:
When you pass a service instance into the ServiceHost constructor it needs to be marked as a singleton.
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
Make sure that initialization is really working the way you think it is:
Put a guard on your TestService(ITestWindowsService) constructor that throws if service is null.
You have a comment on the default constructor for TestWcfService() that says "for discoverability". I would remove that constructor (at least while you are troubleshooting). A piece of code might be using that constructor to host a service instance you don't want. This is likely to cause your app to start throwing exceptions or compile errors that point out your real problem.
If you are still having problems: What does the Service Model section of your app.config look like?

Large amount of objects upload and download from WCF

I am quite new with .NET, C# and WCF and I am trying to create a service that will expose methods to allow upload and download of a large amount objects (array of objects). I have seen a lot of posts regarding large file transfers in WCF, but I can't seem to find anything that focuses on just uploading and downloading large amounts of serializable objects.
I have been able to "hack up" the web.config file for the allowed bytes and timeout limit etc, but I am wondering if there are actually better ways to configure the WCF for better speed and memory usage to allow for such transmission. Because I have configured the web.settings based on my testing results (if time out/byte limit exceeded, increase the limit with some crazy large numbers etc) I doubt my configuration is making sense at all.
Second, I have seen some implementation options like having binding TransferMode = Streaming or MTOM, yet I don't know if they will apply to my scenario at all. Can someone please point me to the correct direction?
Sorry I might not have constructed my question well enough, but ideas will be much appreciated.
Below is my web.config settings:
<system.web>
<httpRuntime maxRequestLength="409600000" executionTimeout="360000"/>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHTTP" closeTimeout="00:01:00" receiveTimeout="01:00:00"
sendTimeout="01:00:00" maxBufferSize="655360000" maxReceivedMessageSize="655360000"
messageEncoding="Text" />
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="ServiceBehavior" name="WebService.WebService">
<endpoint address="" behaviorConfiguration="BasicEndPoint" binding="basicHttpBinding"
bindingConfiguration="BasicHTTP" name="BasicHTTP" contract="WebService.IWebService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="BasicEndPoint">
<dataContractSerializer maxItemsInObjectGraph="65536000" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="204800000" />
</requestFiltering>
</security>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
I would recommend to use Stream mode only for tasks like video/audio streaming or similar, when you really have stream on host and you really need receive stream on client.
For any other tasks i would use Buffer mode because it is really easier to use and becase many useful wcf features rely on buffering. Using streaming with enabled SOAP message-level security (for example), can probably eliminate speed benefits of streaming mode (if you ever had some).
In your case i would recommend you to perform some workaround. Check these steps:
Try to use compression. It can greatly improve speed and resource usage, but it can not guarantee that your amount of data always will be the same. Who knows if your array will be 100x larger next year? So you may return to this point one year later.
Replace your method returning one giant array with method returning only one element or a small range of them. Then replace one GetAllStuff() method call with one call of GetItemsCountForDownload() and appropriate number of calls of GetElementAtIndex(int index) (or GetElementsRange(int startIndex,int endIndex)). Anyway, at this poin you will have to find some balance between message size (for each call) and number of calls performed.
If your data can not be easily splitted into small equal parts (e.g. first element of array is 10kb, second is 15 kb and third is 338mb), then try to combine both methods together: serialize your array to disk, compress it whith splittig to some number of parts of acceptable size and then trasfer them one after another.
Try to make parameters of your algorithm ajustable. Put them in your config file, so it will be possible to tune splitting process and compression level on every deployment machine according to avaliable resources.
Another benefit of splitting data and downloading chunk by chunk easiness to build some error-handling layer. If connection is unstable and transfer fails for some reason you can try to redownload only one chuk instead of all the data.
Some tips on architecture
Find appropriate crossplatform compression algorithm. Definitely you can find one that meets your needs. Look at BZip2 for C# <-> Python and GZip for C# <-> Java.
Try to make your architecture clear enough for other programmers. You can make different methods for compressed data transfer and for uncopressed (GetElementsRange, GetElementsRangeGZip, GetElementsRangeBZip2). Or you can make one method with compression type paramether (GetElementsRange(int startIndex,int endIndex,string compressionType). Anyway, other programmer must be able understand what data he is receiving and how to control compression mode.
You can move data splitting parameter from config file to method definition, so client will be able to define it by himself.
You can go further and implement two step architecture. Step one: remote client defines parameters of request (all of them, including compression mode, splitting mode and all other). Step two: receiving data. How about tokens? Methods should look like:
string GetTokenForDataRequest(string compressionMode, int maxChunkSize); //additional parameters like dates range, account number an other are defined in this method only
int GetChunkCount(string token);
byte[] GetDataChunkAtIndex(string token, int index);
Here compressionMode may be "None","BZip2" or "GZip"; If maxChunkSize is 0 then disable splitting (all data will be sent in one chunk), else split data in chunks with size equal to maxChunkSize (the last one will be smaller than other). So approximate scenario would be like:
Remote client sends a data request with desired parameters.
You generate session id (a token), save parameters for this session and prepare data according to parameters. "Prepare data" means loading data according to request, creating temp folder, serializind data, creating chunk files and persisting paths for further usage.
Client recieves a token, that is used for all other methods for retrieving data.
Every time remote client request data chunk throu your methods you will know where it stored on hard disk, how many chunks left and so on - thanks to provided token and persisted info.
You can easily handle simultaneous data transfer session with several clients at one time with no efforts (just make shure you store data chunks in different temp files and folders)
Client is able to redownload any chunk and you will not have to load data from database (or wherever it comes from).
When transfer is complete, you can wipe temp data and free some resources.
Do not look at this as at only possible solution. Just google around a little bit and you will find your very own way to solve your task!
You can use streaming message transfer, but it has some limitations.
If the streaming isn't suitable for you, you can implement paging of data (or try existing implementations, like in WCF Data Services).

One service, two behavior configurations

Is it possible to have one service with two behavior configurations? As you know it is possible to have one service and more than one endpoint where you can specify different binding configuration.
But I need to have, for the same service, different service behaviors.
If I try something like this:
<services>
<service name="Service.Service1" behaviorConfiguration="Behavior1">
<host>
<baseAddresses>
...
</baseAddresses>
</host>
<endpoint ...>
</endpoint>
</service>
<service name="Service.Service1" behaviorConfiguration="Behavior2">
<host>
<baseAddresses>
...
</baseAddresses>
</host>
<endpoint ...>
</endpoint>
</service>
</services>
... I got error "A child element named 'service' with same key already exists at the same configuration scope"
I know that I can create new class that inherits original service class but is there better solution?
Seems that you should register the same service class with another service name. Service behavior is a part of service configuration so if you want to use different behaviors you should configure different services. My question to you: if you will have one service with two behaviors, how wcf will decide to use this one or another?
Why you solution with inheritance is bad?
Quite intriguing question... I am not certain if what I am describing is possible but solution would be some thing like below:
Choose different name for service - there will be some scheme such as name="Service.Service1.Entry1"
Write custom ServiceHost (perhaps coupled with IInstanceProvider) that will ignore the last .Entry1 part and creates the service instance using Service.Service1 name.
Yet another way would be injecting dynamic type named "Service.Service1.Entry1" inherited from Service.Service1 when application is initializing. This part is very much possible - i.e. for every mentioned service, you would create n subtypes dynamically at app start-up so that you can use them as intended.

Managing multiple WCF endpoints for the same service

I am building an single application that uses WCF to call out to multiple external endpoints. All of the remote endpoints are identical except for the URI. I would like to treat them as a pool: add and remove endpoints through configuration and have the application understand what to do.
My original plan was to define one endoint in the app.config, then iterate over my list of endpoints and update client.Endpoint.Address on the fly to point to the right place. Unfortunately, that property is read-only, rendering that plan unworkable.
I'm a little bit stumped here. Any suggestions on how I might accomplish this?
How to: Create a Service Endpoint in Code shows you how to manage service endpoints in code rather than configuration.
Have you tried a separate name that is passed in to the client constructor?
<endpoint address="http://localhost:18000/MyService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService"
contract="MyServiceReference.IMyService" name="BasicHttpBinding_IMyService" />
<endpoint address="http://localhost:18001/MyService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService"
contract="MyServiceReference.IMyService" name="MyService_Secondary" />
<endpoint address="http://localhost:18002/MyService.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IMyService"
contract="MyServiceReference.IMyService" name="MyService_Tertiary" />
Store the end point addresses in a DB table and use Jason's suggestion for creating end points in code. When a new endpoint shows up you just add another row to the table and force the service to re-query the endpoint table.
How to: Create a Service Endpoint in Code
http://msdn.microsoft.com/en-us/library/ms731080.aspx

Categories

Resources