My web service takes a list of Guids as an argument, but the generated reference wants an observable collection - vb.net

I'm kind of confused here. I have a WCF web service
<OperationContract()>
Function StartStep(ByVal Key As Guid, WorkOrders As List(Of Guid), UserUID As Guid, Comment As String) As Boolean
But when I generate my web reference on my client, WorkOrders comes out as an observableCollection. I created a few new web services and all of the lists are doing that. A list is a fairly common data type, I assumed it would be handled properly by WCF.. could this be because I have another function that returns an ObservableCollection and that's screwing something up? Would I have to use a KnownType or something?

In the "Advanced" tab of the "Add Service Reference" dialog, you get to say how you want collections handled on the client. I guess you have it set to default to ObservableCollection, but you can select List.
Neither "List", nor "ObservableCollection", nor any other .NET type are transferred over the wire. In all cases, XML or some other such data representation is sent. The setting I mentioned earlier is about deciding how the client will interpret this XML data.

Related

.NET XML web service returned collection as array

I am working with an XML web service using VB.NET, created using VS 2010. One of my web methods returns a collection(type that inherits from list) of custom objects. It's a simple return statement, it is my understanding that .NET handles most of the tricky protocol stuff as well as serializing/unserialzing of objects.
The issue is in my consuming application when I get the the return value of the web method that returns a custom collection I get an array of the custom objects. Is this normal behavior? It will be easy enough for me to take that array and insert it into a custom collection object but if I could I would like to skip this step. Googling hasn't returned anything that I found useful. Could anyone tell me if this is typical behavior? Thanks much!
Assuming you are using WCF, try adding CollectionDataContractAttribute to your custom collection, as per Customizing Collection Types section of Collection Types in Data Contracts.
In the case you are using your service by setting up a Service Reference in the consuming application, then an even easier method would be to right-click the service name, select Configure Service Reference..., and about 1/3rd the way down of the dialog that pops up there will be a dropdown with "Collection type:" label. Default is to use System.Array, but here you can easily change it to use whichever collection type you prefer.

wcf reference not being generated correctly

Ran across a weird problem with my WCF references this morning. In a nutshell, I have a duplex service set up so that a server can notify a client by sending objects in a data contract. When a client connects, it runs a function on the server to return a List(Of NewItem) based on what's in the shared queue of the service class. The trouble is, when I update my service reference in the client, it says the function returns a NewItem object, not a List(Of NewItem) object. I can go into the reference and manually change it to a List object and it'll transfer just fine. Any ideas why the service reference generator would arbitrarily change my return type?
Here's the relevant code:
<ServiceContract(
CallbackContract:=GetType(INotifyCallback),
SessionMode:=ServiceModel.SessionMode.Required)>
Public Interface INotifyService
<OperationContract()>
Function GetNewServerItems() As List(Of NewItem)
End Interface
<DataContract>
<Serializable>
Public Class NewItem
<DataMember()>
Public Property ItemNum As String
<DataMember()>
Public Property Timestamp As DateTime
End Class
<ServiceBehavior(
ConcurrencyMode:=ServiceModel.ConcurrencyMode.Single,
InstanceContextMode:=ServiceModel.InstanceContextMode.Single)>
Public Class NotifyService
Implements INotifyService
Shared _server_items As New List(Of NewItem)
Public Function GetNewServerItems() As List(Of NewItem)
Return _server_items
End Function
End Class
And in Reference.vb (simplified):
<System.ServiceModel.OperationContractAttribute(Action:="http://tempuri.org/INotifyService/GetNewServerItems", ReplyAction:="http://tempuri.org/INotifyService/GetNewServerItemsResponse")> _
Function GetNewServerItems() As NotifyGateway.NewItem()
You and I fell into the same trap: we both misread the VB.NET code generated for the proxy method as returning a single item. In fact, it is returning an array.
WSDL uses XML Schema to describe the shape of the request and response. XML Schema has no concept of a "list", only of a sequence. By default, when it sees a sequence of items, "Add Service Reference" generates an array. You can change this to generate a "List", simply by changing the collection type on the "Advanced" tab of the "Add Service Reference" or "Configure Service Reference" dialogs.
When adding a reference the way you did, you should change the "Collection type" in the Advanced Service Reference Settings (Add Service Reference dialog) to "system.Collections.Generic.List". By default it is "System.Array".
I would not recomend though using the Add Reference way because you lose control over the configuration of your WCF service, and VS adds a lot of garbage that you do not need, and that would make your client/service link harder to maintain.
Hope this helps.
There is another, less well-known, but more powerful mechanism for preserving the collection types without resorting to using the default collection type setting.
This mechanism is especially useful if you have CollectionDataContract classes that you use in your code on both sides of a WCF pipe.
The solution involves editing the svcmap that is generated as part of the WCF service reference and adding specific CollectionMapping entries for each of the collection types that you want to serialize. The primary thing to watch out for here is generics: if WCF finds more than one match for a given class, it will refuse to generate the code.
To actually make the changes:
1) Show all of the files in the project which contains your WCF service reference.
2) Expand your service reference
3) Double-click on the Reference.svmap file to edit it.
4) Add your entries in the CollectionMappings section (if it does not exist, you can add it).
For example, in order to transfer generic Dictionaries and Lists, and to transfer StringCollections, you can have the following entries:
<CollectionMappings>
<CollectionMapping TypeName="System.Collections.Generic.Dictionary`2" Category="Dictionary" />
<CollectionMapping TypeName="System.Collections.Generic.List`1" Category="List" />
<CollectionMapping TypeName="System.Collections.Specialized.StringCollection" Category="List" />
<CollectionMappings>
The first item with a category of Dictionary will serve as the default Dictionary collection type and the first item with a category of List will serve as the default Collection type.
We have over 200 collection classes that we use this way for Silverlight and Windows Forms clients and it is an extremely efficient way to reuse your own code on both sides of a WCF pipe.
One important note: to reuse your classes, you need to ensure that the "Reuse types in referenced assemblies" flag in the service reference configuration is checked or that that the GenerateInternalTypes flag in the reference.svmap is set to false (these are the same thing).

ObservableCollection turns into an Array after transported using WCF

I got a class called "Board" and one of its property's is an ObservableCollection. When i send the ObservableCollection through WCF (from server to client) end call it from my proxy, it's turned into an Array, which is no good for me.
Can i keep the ObservableCollection after being sent, or do i have to kick the Array till it becomes an ObservableCollection again?
Check out the 'Configure Service Reference' option in the context menu in VS for the reference. You can choose the collection type that is transmitted across the service. By default I think it is set to array but there are several choices (I believe list and observablecollection are options).
EDIT: I just checked, and unfortunately observable collection is not one of the choices. It looks like you'll have to pick from:
Array
ArrayList
LinkedList
List
Collection
BindingList
By default - no, you cannot do anything about it. WCF will serialize your structures into something that can be represented with XML schema. XML Schema has no knowledge of anything but raw, and fairly simplistic data structures. You can only transfer concrete, raw data - no "magic" behavioral addon.
There is one solution to the problem, IF you own both ends of the wire: you could put your service and data contracts into a separate class library assembly, and share those between server and client. In that case, you only ever have one single implementation of your data contract - your ObservableCollection.
If you share that assembly between your service (implementation) class, and the client (add the reference to that assembly before you "Add Service Reference" from Visual Studio!), then your client should pick up that ObservableCollection and continue to use that (instead of creating a XML schema compatible Array on the client side).
Thank you both for the answer.
I will look at both solutions when i continue the project, and will start with try and change the Collection send through the wcf service.
I'll let you know what works for me...

Can Server-side and Client-side WCF Share Validation Library?

Greetings!
I am using a WCF library on an application server, which is referenced by an IIS server (which is therefore the client). I would like to put my validation in a place so that I can just call .Validate() which returns a string array of errors (field too short, missing, etc). The problem is, such functions don't cross the WCF boundary and I really don't want to code the same logic in the WCF service and in IIS/WCF client. Is there a way to use extension methods or something similar so both side can use use a .Validat() method which calls the same code?
Many thanks for any ideas!
Steve
If you control both sides of the wire, i.e. the server-side (service) and the client-side, then you could do the following:
put all your service and data contracts into a shared assembly
reference that "Contracts" assembly from both the server and the client
manually create the client proxy (by deriving from ClientBase<T> or by creating it from a ChannelFactory<T>) - do not use "Add Service Reference" or svcutil.exe!
put all validation logic into a shared assembly
reference that shared validation assembly from both projects
If you want to use a shared validation assembly, you must make sure the data types used on your server and client are identical - this can only be accomplished if you also share service and data contracts. Unfortunately, that requires manual creation of the client proxy (which is really not a big deal!).
If you'd use "Add Service Reference", then Visual Studio will inspect the service based on its metadata, and create a new set of client-side objects, which look the same in terms of their fields and all, but they're a separate, distinct type, and thus you wouldn't be able to use your shared validation on both the server-side and the client-side objects.
Do you have a problem with sending the data over to the server to be validated? In other words, your service interface actually offers the "Validate" method and takes a data contract full of data, validates it and returns a List where T is some kind of custom ValidationResult data contract that contains all the info you need about validation warnings/errors.
In a service architecture, you can't trust the client, who could theoretically be some other company altogether, to have done proper data validation for you. You always need to do it at the service layer and design for communication of those validation issues back to your client. So if you're doing that work at the server anyway, why not open that logic up to the clients so they can use it directly? Certainly the clients can (should) still do some kind of basic input validation such as checking for null values, empty strings, values out of range, etc, but core business logic checks should be shipped off to the service.

.NET webservice using an instance of a parameter type?

I have a Windows forms project and a Web Service project in my solution, and I'm trying to call the web service and return a customer object as the result. The problem is that when I try to receive the return object, I get an error that it can't convert it. For example, here is the signature for my webservice:
Public Function GetDriverByID(ByVal DriverID As Integer) As Driver
And here is the code I'm using to call it:
Dim d As Driver = mywebserviceinstance.GetDriverByID(1)
But I receive this compile-time error (wsDrivers is the name of the web reference I've added to my form project): "Value of type ProjectNamespace.Common.wsDrivers.Driver cannot be converted to ProjectNamespace.Common.Driver"
This "Common" namespace contains the Driver class, and I'm not sure why the return class from the web service isn't just a generic "Driver", but is instead a "wsDrivers.Driver", and I can't convert it back. Anybody know how I can deal with this type mismatch?
EDIT: Thanks for the explanations - this actually makes it clear what it's doing. However, is there any way that I can force it to use the actual type instead of the proxy (or, rather, is there any way to convert between the "real" instance and the "proxy" instance), or do I have to serialize the properties before I send them over the wire, and then manually de-serialize the return values?
This is actually pretty common. What's happening is that the Web Service has defined in it the definitions of all the types used in the web service. When you add a reference to that web service, it auto-generates a proxy type in a sub namespace of your namespace. That is what is being returned by your web service when you call it.
However, you probably are also referencing the same library that the web service does seperately that contains the same type. That is the type that is expected when you Dim Driver. That's why there is a mismatch.
The web service reference in a VB.NET or C# project can reference any type of web service and is not limited to those provided by ASP.NET. That is why Visual Studio creates proxy classes for each object which can be retrieved from the web service.