WCF Chunking / Streaming - wcf

I'm using WCF and want to upload a large file from the client to the server. I have investigated and decided to follow the chunking approach outlined at http://msdn.microsoft.com/en-us/library/aa717050.aspx
However, this approach (just like streaming) restricts the contract to limited method signitures:
[OperationContract(IsOneWay=true)]
[ChunkingBehavior(ChunkingAppliesTo.InMessage)]
void UploadStream(Stream stream);
The sample uses the rather convenient example of uploading a file from a fixed path and saving it to a fixed path on the server. Therefore, my question is how do I pass additional parameters to specify things like filename, filepath etc.
eg. I would like something like:
[OperationContract(IsOneWay=true)]
[ChunkingBehavior(ChunkingAppliesTo.InMessage)]
void UploadStream(Stream stream, String filePath);
Thanks in advance,
Mark.

This article explains how to use the MessageHeader attribute to force things to be passed in the header, and therefore not count as a parameter. So, instead of passing a stream and other meta data, create a class that has the attribute MessageContract and mark all of the meta data as a MessageHeader. Then, mark the stream as a MessageBodyMember (which the article incorrect calls "MessageBody"). Have your UploadStream method take a single parameter whose type is that of the MessageContract class you've just created. I've done this successfully, but I haven't done it in tandem with chunking. Good luck.

You could make your service session-ful and have an initialization method in the contract with the IsInitiating property set to true. Something like:
[OperationContract(IsInitiating = true)]
void InitializeUploadService(string filename);
[OperationContract(IsOneWay = true, IsInitiating = false)]
[ChunkingBehavior(ChunkingAppliesTo.InMessage)]
void UploadStream(Stream stream);
I have never tried it with streaming services but it should basically make WCF enforce that InitializeUploadService is always called before UploadStream.
More documentation can be found here:
http://msdn.microsoft.com/en-us/library/system.servicemodel.description.operationdescription.isinitiating.aspx

I would look at MessageContracts and add those values as message headers to your object. This should allow you to pass the stream and any values related to the stream as message headers.

Setting up the maxItemsInObjectGraph in the Client side and Server side worked for me.
(Dont forget the client side.) http://social.msdn.microsoft.com/Forums/en/wcf/thread/0af69654-2d89-44f3-857a-583b57844ca5

Related

WCF suppress deserialization of certain operation implementation parameters

I have a WCF service that has an operation that takes any .net serializable client data.
[OperationContract]
void SaveMyData(long id, string name, object serializableData);
[OperationContract]
object LoadMyData(long id, string name);
The server doesn't need to know what the data is, it just stores it or returns what is stored. And the server doesn't even know the types being serialized so of course this contract would result in deserialization exceptions.
I know that I could serialize/deserialize this independently of the WCF contract, for example:
[OperationContract]
void SaveMyData(long id, string name, byte[] serializedData);
[OperationContract]
byte[] LoadMyData(long id, string name);
But this requires additional code on the client to serialize and deserialize. I'd like to avoid that and have the client code as simple as possible.
I know that I could create a pre-build proxy in a client dll that would wrap the WCF calls and perform the additional serialization/deserialization. But I'd rather be able to rely on the clients generated from the WSDL.
Ideally, a RawAttribute could be placed on the parameters or return value which would suppress serialization/deserialization (of the universal root object type) and instead supply or expect an (object)byte[] (or (object)Stream?) from the operation.
[OperationBehavior]
public void SaveMyData(long id, string name, [Raw] object serializableData){ ... }
[OperationContract, Raw]
object LoadMyData(long id, string name);
I've looked at DataContractSurrogate and DataContractResolver but I'm not seeing how to achieve this. DataContractSurrogate seems too late in the deserialization pipeline as the type and deserialized object are already supplied. The resolver doesn't give the data, just the type info. Neither gives information about the parameter being deserialized for which to find the RawAttribute.
Does WCF offer an appropriate extensibility point for this? Or a built-in way?
I would also like to know what the declared type is, as extracted from the serialized data, but that isn't necessary.
Thanks!
Instead to fighting WCF's serialization mechanism, you should drop one level of abstraction and work at the message level of WCF. What you're looking for is a kind of "universal" service that can accept messages from any client. Read through this old but still applicable MSDN article on WCF Messaging. Toward the bottom of the article (figure 8) is sample code for a generic WCF service. That should at least give you a start in creating a service that bypasses serialization.

Adding REST methods to WCF Data Services?

I need to extend my WCF Data Service to include additional methods, not only the database tables..
But it doesn't seem to be working correctly.
Firstly i want to ask if this is legal? or frowned upon?
The reason i need to do it is i need add additional REST methods that will make a call to ASP.NET Membership services (the tables are in the db) to validate a login i.e.
public bool IsValidLogin(string username, string password)
{
return System.Web.Security.Membership.ValidateUser(username, password);
}
Here is what i have (i have simplied the IsValidLogin for testing).
[WebGet(UriTemplate = "TestMe")]
public bool IsValidLogin()
{
return true;
}
// This method is called only once to initialize service-wide policies.
public static void InitializeService(DataServiceConfiguration config)
{
// TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
// Examples:
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.SetServiceOperationAccessRule("IsValidLogin", ServiceOperationRights.All);
Now when i go to
http://localhost/MyDataAccess/MyService.svc/IsValidLogin
It seems to work i get an true back in the form of XML. But i have set a URI so i thought i could do this
http://localhost/MyDataAccess/MyService.svc/TestMe
But it fails? I am really confused, any ideas?
Also for it to work I needed to add this line, but a little but confused here
config.SetServiceOperationAccessRule("IsValidLogin", ServiceOperationRights.All);
Any help really appreciated
Not commenting on the REST dicsussion above, just posting a link on documentation on how to do so called "service operations": http://msdn.microsoft.com/en-us/library/cc668788.aspx
The ServiceOperation notion is a tacked on capability to provide exactly the escape you needed when you wanted to do something other than read data from a table.
Unfortunately, the default path in WCF REST has lead you to misunderstand how RESTful systems are supposed to work. REST is not just about exposing some data at URLs.
You really have two choices, either stick with RPC style of distributed computing that WS-*/SOAP based WCF provides or spend some time learning what REST is really all about. There are some links here to get you started.
I can't tell you what is the right approach for your scenario. What I can tell you is that you are not going to learn how to do REST from using the current WCF REST implementation. I'm not saying it is impossible to do, you just will be doing a lot of swimming upstream.

ServiceKnownTypeAttribute doesn't pop up in WSDL

I have an service Interface:
[ServiceContract]
[ServiceKnownType(typeof(Models.ArticleImage))]
public interface IPhotoManagementService
{
[OperationContract]
bool Login(string username, string password);
[OperationContract]
bool IsLoggedIn();
[OperationContract]
void UpdateImage(string articleID, string selectedImage);
}
As you can see I specify a typeof(Models.ArticleImage) on my ServiceContract.
So building the WSDL of this service should cause ArticleImage to pop up in the WSDL. Unfortunarly this doesn't happen at all. Why is that?
ArticleImage has DataContract on it. And when I return an ArticleImage in my interface, then the WSDL does pick up ArticleImage.
Edit: it doesn't even pop up in the service reference in the consuming project!
This is the result of a lot of testing:
The model I'm trying to add is a LINQ to SQL model.
When I add a normal model with ServiceKnownType it works.
When I use my LINQ to SQL entities in my Interface it works.
When I add my LINQ to SQL entity through ServiceKnownType it doesn't pop up.
Only types used as input/output parameters of service contract operations are published in the WSDL.
Why would it need to? Where does your service expose something that could possibly be an ArticleImage?
Re your comment; when using [ServiceKnownType], the extra trype is still exposed in the "mex" (consumed via "svcutil") - but not by the WSDL. Are you using a WCF client? It should appear (I've just checked... it did). In general, though, returning vague data from a web-service isn't a great idea... sub-types, sure! Dictionary<string,ArticleImage> or even Dictionary<string,SomeBaseType> (with [KnownType] etc), fine! But object, HashTable, etc - aren't a good idea (IMO).
You might also just return a list of your type (List<ArticleImage>) which will work in all scenarios (and be easy for WSDL etc); and let the client make the dictionary at their end.
With regards to LINQ-to-SQL; objects for "mex" need to be decorated with [DataContract] / [DataMember]. You can do this in the designed by toggling the "serialization" property for the dbml. With this set (Serialization Mode = Unidirectional), it should work. To be honest, though, I think you be better-off just adding a dummy method that makes the type explicit on the API.

Type 'System.Web.HttpRequest' cannot be serialized

I am trying to design an Picture Upload feature into a web site.
I am using ASP.NET 3.5, C#, and WCF.
I have been asked to accomplish the following:
1) Make the Uploader a Web Service
2) Return progress updates to the user as files are uploaded.
3) Log other relevant user-selected options in the database.
So, I have started off by creating a WCF web client with the
below service contract:
IService.UploadPictures(HttpRequest request);
private UploadServiceClient upload;
protected void Page_Load(object sender, EventArgs e)
{
upload = new UploadServiceClient();
upload.UploadPictures(Request.Files);
}
When I compile, I get the below error:
Type 'System.Web.HttpRequest' cannot
be serialized. Consider marking it
with the DataContractAttribute, and
marking all of its members you want
serialized with the
DataMemberAttribute attribute.
So, I went back into my service contract and
changed [OperationContract] to [DataContract]
but the change produced the same error.
Can somebody kindly tell me what I am doing wrong
and provide examples as to how to best move forward?
Thanks for your time.
You cannot use something like a HttpRequest as a WCF parameter. The error messages says it all - the HttpRequest is not serializable, and in order to work with WCF, types have to be serializable.
Also, you need to remember: you're not just passing an object instance to a method here - what you're really doing is having the WCF runtime serialize your request (the method name to call plus all the parameters passed in) into a message (think: e-mail or xml file), sending it to the server, deserialising there and building up a new copy of the given datatype (as defined in your DataContract), and doing something with it.
Your WCF service could well be self-hosted, e.g. running in a NT Service or console app - no HttpRequest available in those circumstances!
You need to definitely rearchitect your solution - you need to either check into WCF streaming to upload files to WCF (google for it - you'll find plenty of hits) or you'll need to find another way to pass the relevant info (e.g. list of filenames) to the WCF service without use of a HttpRequest object.
Marc
You are submitting a request as a parameter to a request. This is not what you want to do. I'm assuming that "Request.Files" is an array of files. This is what you want to upload. So something like:
IService.UploadPictures(List<SomeFileType> request);

Adding a new parameter to a WCF operation: choices?

What's the best way to handle adding a new (optional) parameter to an existing operation without requiring the client to update their WSDL? I do not want to update the namespace to describe a new version of the service contracts, since this should be backwards compatible with older clients.
Should I add a new operation with a new parameter, as an overload? Or should I just add the parameter to the existing operation?
Here is my operation:
[OperationContract]
MyResponse GetData();
Should it be:
[OperationContract]
MyResponse GetData();
[OperationContract]
MyResponse GetData(string filter);
Or more simply, just change it to this:
[OperationContract]
MyResponse GetData(string filter);
The latter option seems best, and according to my reference book, "The impact on client is none. New parameters are initialized to default values at the service." Is WCF initializing it to the the so-called default value? If so, what is the default value?
One thing to take into consideration is that you can't have two OperationContracts with the same name. The way it's serialized it will throw an error.
The best approach is to go with Option 3 (just adding the new parameter) and within the method logic account for it being a null value for those clients that haven't updated yet. If it's a breaking change that the clients will need to update for, make sure to not have the entire application die because of the exception.
Well, changing an existing contract after it's been used is really against all rules of service orientation; you should never ever break an existing contract.
In reality, this happens quite frequently, and WCF is pretty good about handling that for you. As long as you only introduce non-breaking changes, existing clients will continue to work.
This can be:
a new operation contract on an existing service contract
a new non-required field on a DataContract
What you're trying to do is not going to work, though
you cannot have two method with the same name in WCF - WCF is not .NET and you cannot have two methods by the same name being different only by their signature. Doesn't work. You'll need to use two separate, distinct names. Remember: your WCF method calls will be translated into a WSDL (web service description language) document to describe the service - and WSDL simply does not support having two operations with the same name - just a difference in signature is not supported and will not work.
you cannot change the existing contract, e.g. you cannot introduce a new parameter into a method call after the fact, without breaking the contract.
So what you really need to do is this:
[OperationContract]
MyResponse GetData();
[OperationContract]
MyResponse GetFilteredData(string filter);
Any other change you suggested will a) break the contract, or b) simply not work in WCF:
you can try this:
[OperationContract]
MyResponse GetData();
[OperationContract(Name = "GetDataByFilter")]
MyResponse GetData(string filter);