How to Allow Client-Service to Build Complex Object in WCF Service? - wcf

I made a simple PasteBin demo example of what my code looks like: http://pastebin.com/GpDhPRVm
My actual Process object is extremely complex that includes adding collections of Tasks, Documents, Workflows, etc into a Process object and setting properties through methods, etc.
My PasteBin example is as simple as I can make it to show where things break down. Including the Process object in the CreateNewProcess method in my service (shown in code below), allows the service user to automatically "see" the Process object, properties and enumerators in their instance on the client side.
<ServiceContract()>
Public Interface ICreateProcess
<OperationContract()>
Sub CreateNewProcess(ByVal newprocess As Process)
End Interface
However, it does not allow them to use any of the methods like the 'AddTask' method (shown in PasteBin example) and it also doesn't expose the Task or TaskCollection objects. Even if I decorate the methods with DataContract, OperationContract, etc they still are not visible to the client service.
This is my major issue: I need the client service to be able to fully "build" the Process object and then pass it in to the CreateNewProcess method.
Another small note: (to avoid comments asking me about it) is that I made sure that all of my properties are simple types that are interoperable since this needs to be able to work for Java - not just .NET clients.

Ok, I figured it out with a little help from a buddy of mine.
Can't use methods at all outside the .svc service class; only properties, enums and sub objects will translate down to the client. (I figured this)
Then, instead of using a Collection, you have to use a generic List type.
So, instead of this:
Public Tasks As New TaskCollection() 'where TaskCollection inherits from Collection
I needed to do this:
Public Tasks As List(Of Task)
And the client will just have to build their own array of Tasks and assign it to the t.Tasks property.
This works like a charm now: http://pastebin.com/rt8HwsXY

Related

WCF 4.0 Instance Management inside of Instanced Service Objects?

I have a WCF service using the per-instance call pattern. It is self-hosted, not using IIS at all.
The service class instances another class with the pattern
var myfoo = new Foo().GetResultFromDictionary(something);
The Foo class reads from an embedded resource, does some lookups, and returns an object of Bar that the service class then uses.
Creating a new instance for a single call on Foo() seems like a waste. Once it loads, all instances of the service should be able to make the call.
Does anyone have design recommendations for this? I do not want to fall into the trap of over-optimizing and pre-optimizing if there is no problem here (eg if .NET Framework will take care of instance management for Foo), but I'm not sure.
All ideas appreciated, thanks.
I am going to work through the System.Runtime.Caching bits, creating a static class that inserts into MemoryCache.Default when the static "constructor" is called, and reading from the cache on all subsequent calls.

Type being passed through web service not being recognized by consuming code

I am creating an XML web service that passes an array of custom types. In my consuming code I am referencing the code as a web reference which I have given the namespace MYWS. Now in code I am trying to assign the results of my web service call to an array of my type like so :
'instance to make a call to my web service
Dim srv As New MYDWS.ServiceNameWSSoapClient
'array to hold the results
Dim arr() As MyClass
'assign the web service call results
arr = srv.myWebMethod()
When I do this the complier complains, saying:
Value of 1 dimensional array of my.namespace.MyClass cannot be
converted to 1 dimensional array of my.namespace.MYWS.MyClass because
my.namespace.MYSW.MyClass is not derived from my.namespace.MyClass
Now I understand the message, the thing is they are the same class. The class is declared in my calling code by the web service references a dll from that project. How do I tell the compiler that these are the same type? Any help would be very much appreciated. Thanks!
The upshot is that you have a namespace mismatch. If you right-click on MyClass in your example and select Go To Definition, where does it take you? I suspect that you may end up in a locally defined class.
The solution is to change
Dim arr() As MyClass
to
Dim arr() As MYWS.MyClass
Update based on information in comments
The problem with using the web service is that you cannot cast it to a local class.
You have a couple of options depending on exactly what you need out of the local class.
If you only need methods to act on the data in the class or you need additional properties, you can create a partial class in your environment that extends the class created by the web service. For example:
Namespace MYWS
Public Partial Class MyClass
Public Property SomeAdditionalData As String
Public Sub SomeMethod
' Perform some operations on the class members
End Sub
End Class
End Namespace
However, if you have calculations or other work embedded in the class, then you will need to get the data using the web service class, then copy the data from that class into your local class. If the properties have the same names, you could ease this task using reflection.
As another option, if you have control over the web service, you could change it to a WCF service. This will allow you to reuse the exact same class code on both ends of the communication pipe.
Found a solution to the problem. In the web.config I found this:
<add key="net.mydom.mydom" value="http://localhost:7452/dir/mysvc.asmx"/>
which was what the system automatically entered when I registered the web service. I got the error messages on screen, but everything compiled and ran w/o problem.
When I manually changed to this:
<add key="net.mydom" value="http://localhost:7452/dir/mysvc.asmx"/>
The error messages went away and everything continued to function as expected.
(That only took my 7 years to figure out...)
UPDATE:
Well, not quite the fix, but it must be close. After awhile, the problem came back, when I switched back to to:
<add key="net.mydom.mydom" value="http://localhost:7452/dir/mysvc.asmx"/>
it went away again...sure to come back at any time...
UPDATE
If I explicitly add:
imports net.mydom
to the top of my code, the message goes away again (even though I was explicitly using the full net.mydom. when typing the variables.

How can I cleanly write abstractions for interacting with RESTful resources?

I have a simple REST client that works well. In my application code I do something like this:
restClient = new RestClient(configurationData)
restClient.get('/person/1') //Get Person
restClient.get('/equipment/auto/3') //Get an Auto
restClient.get('/house/7') //Get a House
That works well but things are getting more complicated and I would like to divorce the application code from the specific resource locations.
I'd like to be able to write a wrapper around the service, which will store the resource locations and not require me to put them in my application code. I would expect my code to start looking more like this:
restClient = new RestClient(configurationData)
restClient.getPerson(1) //Get Person
restClient.getAuto(3) //Get an Auto
restClient.getHouse(7) //Get a House
I started adding these wrappers inside of my RestClient class but it got very bloated very fast, and it felt that the abstraction should be at a higher level. Mixing Resource-specifics with my client also felt wrong.
So, instead I subclassed RestClient, and each resource has its own class. The problem is that now I have to instantiate a new client for every different resource type:
personRestClient = new PersonRestClient(configurationData)
personRestClient.get(1);
autoRestClient = new AutoRestClient(configurationData)
autoRestClient.get(3);
housesRestClient = new HousesRestClient(configurationData)
housesRestClient.get(7);
But now I've created a new Client class for each Resource and I am fairly certain that is a very bad thing to do. It's also a pain because I have to tie my connection configuration data to each one, when this should only happen once.
Is there a good example or pattern I should be following when I want to write abstractions for my Resources? My base RestClient works fine but I dislike having to put the server-side API locations in my application code. But I also don't want to have to instantiate one specialized client class for each Resource I want to interact with.
I am in a similar situation, and have what I consider to be a good implementation with the appropriate abstractions. Whether my solution is the best practice or not, I cannot guarantee it, but it is fairly lightweight. Here is how I have it architected:
My UI layer needs to make calls into my REST service, so I created an abstraction called ServiceManagers.Interfaces.IAccountManager. The interface has methods called GetAccounts(Int64 userId).
Then I created a Rest.AccountManager that implemented this Interface, and injected that into my AccountController. The Rest.AccountManager is what wraps the REST specifics (URL, get/post/put..., parameters, etc).
So, now my UI code only has to call accountManager.GetAccounts(userId). You can create an all-encompassing interface so that you only have a Get, but I feel that is less expressive. It is ok to have many different interfaces for each component(ie: PersonManager, HouseManager, AutoManager), because each are a separate concern returning different data. Do not be afraid of having a lot of interfaces and classes, as long as your names are expressive.
In my example, my UI has a different manager for each controller, and the calls made fit each controller appropriately (ie. GetAccounts for AccountController, GetPeople for PeopleController).
Also, as to the root configuration data, you can just use a configurationCreationFactory class or something. That way all implementations have the appropriate configuration with the core logic in one location.
This can be a hard thing to explain, and I know I did not do a perfect job, but hopefully this helps a little. I will try to go back through and clean it up later, especially if you do not get my point :)
I am thinking something like this, again some way of mapping your end points to the client. You can have the mapping as an xml or a properties file which can be loaded and cached during the app start. The file should have key value pairs
PERSON_ENDPOINT=/person/
AUTO_ENDPOINT=/equipment/auto/...
The client should pass this key to the factory may be ClientFactory which has this xml cache and retrieves the end point from the cached file. The parameters can be passed to the factory as custom object or a map. The factory gives back the complete end point say "/person/1" which you can pass to your client. This way you dont need to have different classes for the client. If you dont like the xml or a file you can have it as a static map with key value pairs. If its an xml or file you dont need a code change every time that is the advantage.
Hope this helps you.

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...