Type being passed through web service not being recognized by consuming code - vb.net

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.

Related

Using a Service Reference in VB.NET

i have added a service ref in my VB.NET application.
i can see the objects in the object browser. i need to log in first to obtain a session ID.
when I try
dim client as new serviceReference1.IGPSBulkData
I get an error
'New cannot be used on an interface'.
IGPSBulkdata is the only option that includes the login function so Im not sure how to make this call
any ideas?
When you add a service reference several classes are being generated from the service' WSDL. Take a closer look at the generated code 🤓 There will be something like GPSBulkDataClient.
This generated client class can be used to communicate with the service.
The error is correct; you cannot instantiate an Interface.
If you open Object Browser in Visual Studio and search for IGPSBulkData you should be presented with a list of classes which implement it. From there you can instantiate your client object (if that's what you need to do with that class).
So if there was a class called GPSBulkDataThing which implements IGPSBulkData your code might resemble:
Dim client as serviceReference1.IGPSBulkData = new GPSBulkDataThing(maybe with some arguments here)

Service Reference not reusing data types client side

This question has been asked many times but I can't find a solution.
I have a WCF service with a function that takes in a customer object. This customer object is in a separate project that both the client and server code reference. When I add the service reference to the client project, I choose the option to reuse data types. However, when I try to call the function on the client side and pass the customer object in, I get this error:
Error 39 Value of type 'Real.Namespace.Customer' cannot be converted to 'Service.Namespace.Customer'.
The "Real.Namespace" is the class it should be using. "Service.Namespace" is the auto generated class created by the service reference. I know this is supposed to work, so there must be some reason why it is unable to find the real class and reuse it.
I've tried this with very simple objects and it still won't work. Any ideas on why the auto generated code can't find the real class and use it?
Edit: I've tried using a very simple object just to see if I could get it to work. So right now I'm just using a test class that looks like this:
Namespace DTO
<DataContract>
Public Class Test
<DataMember>
Public Property Name As String
End Class
End Namespace

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

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

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

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