Class shared properties values are different when called from Project A then B, how so? - vb.net

I have three projects, let's call them Client, Service and Execute.
Both Client and Service references Execute.
Client is a windows forms application. While first loading, it calls a shared method in a class called ParametersManager which fills four parameters in a class called Parameters.
Public Class Parameters
Public Shared Property FirstProperty As Integer
Public Shared Property SecondProperty As Integer
End Class
Public Class ParametersManager
Public Shared Sub FillParameters()
'In real scenario, I am just sending the parameters as arguments to the method.
Parameters.FirstProperty = 1
Parameters.SecondProperty = 2
End Sub
End Class
So when the application starts, it will call FillParameters from Execute.
I did debug the application, and when this method finish executing, the properties has the exact values that I gave them.
In one of the forms of the Client application, I am calling the Service which is an asmx Web Service. Service now calls a method from Execute project, let's call it UsePropertiesDoingSomething.
Public Class UseProperties
Public Shared Function UsePropertiesDoingSomething() as Integer
Return Parameters.FirstProperty + Parameters.SecondProperty
End Function
End Class
My problem is that when I call this function from the service, the properties are both equal to 0, not the values I have previously filled when the Client application first run.
How did I lose the the values? Shouldn't the values be saved in the Execute scope? How can I make sure not to lose them?
For testing purposes, I filled the parameters at the start of the Service, now when the Service calls the UseProperties the values are kept, but when called from the Client, the values are lost again.

For this purpose you have explore AppDomain and Execution context.
Shared does not means that it is shared between application or project. It is shared for specific application domain.
If same application domain load same library then it will be shared otherwise not as other application domain has it's own copy. (At least in .net it will not cross AppDomain boundry).
In your case Window Form has different Application Domain and ASMX in Web Service so it has different Application Domain so it will not be shared.

Related

Accessing functions of added web service

I have created my own WebService in VS 2010. My project is called sampleWebService and inside my project I have successfully added/connected to another WebService this is called practiceService.
sampleWebService has just the basic Hello World auto generated code in it, however, practiceService has Web Method Functions that handle database calls such as getFirstName, getLastName,...
My question isn't how to extract the data really since I know you have to use either JSON or SOAP. I'm just wondering what I have to type into my code to be able to see the functions and methods that are in my added web reference practiceService so I can connect to them.
Or maybe this is accomplished by using JSON or SOAP
Right now my code for my sampleWebService page is just as is:
<ToolboxItem(False)> _
Public Class Service1
Inherits System.Web.Services.WebService
<WebMethod()> _
Public Function HelloWorld() As String
Return "Hello World"
End Function
End Class
You can have a look at the proxy class that was generated when you added the web reference. Also when you create the object of practice service it will give you access to all the web methods exposed in that web service.
To access the web method you have to do something like
ServerName.WebServiceName CallWebService =
new ServerName.WebServiceName();
String sGetValue = CallWebService.MethodName();
Label1.Text = sGetValue;

Accessing web services uniformly

I have three web services, which were developed by three different vendors and have different URLs; input parameters and output parameters. They are all ASMX web services. They are used to delete records from third party relational databases e.g. I supply a personID and a person is deleted from one system and everything linked to the person. In another system I supply an order ID and everything linked to the order is deleted.
I have several options:
Create a single wrapper class, which is responsible for accessing the web services; supplying common input parameters and accepting common output parameters. This class would have lots of responsibilities.
Create three wrapper classes i.e. one for each web service
Modify the proxies generated by Visual Studio
Which way is best?
I would recommend allowing Visual Studio to automatically generate the appropriate proxy classes. I would then implement a wrapper class for each web service so that all of the wrapper classes could implement the same interface. For instance, you may make a common interface that looks like this:
Public Interface IPersonBusiness
Sub DeletePerson(personId As String)
End Interface
Then, lets say you had two web services. The first, we'll call it WebService1, has a Delete method which takes a person ID followed by the deletion time. The second web service, we'll call it WebService2, has a DeletePeople method which takes an array of person ID's. We could wrap both of these web services using the above common interface, like this:
Public Class WebService1Wrapper
Implements IPersonBusiness
Public Sub New(proxy As WebService1)
_proxy = proxy
End Sub
Private _proxy As WebService1
Public Sub DeletePerson(personId As String) Implements IPersonBusiness.DeletePerson
_proxy.Delete(personId, Date.Now)
End Sub
End Class
Public Class WebService2Wrapper
Implements IPersonBusiness
Public Sub New(proxy As WebService2)
_proxy = proxy
End Sub
Private _proxy As WebService2
Public Sub DeletePerson(personId As String) Implements IPersonBusiness.DeletePerson
_proxy.DeletePeople({personId})
End Sub
End Classs
I would avoid writing your own proxy code unless you really need to. For instance, if you needed to dynamically call any web service based on some external settings which tell you the method name and parameters to pass, or something like that, then it would be worth looking into.
I would also avoid putting all of the logic to call any of the web services into a single wrapper class. Doing so will make the code unnecessarily ugly and confusing, especially if you need to add additional web services in the future.

Running function of one application from within another application

I've got two standalone applications:
First one:
Namespace FirstApplication
Class MainWindow
Public Sub New()
InitializeComponent()
End Sub
Public Function RunBatch(Parameter as String) as Double
'Do some work
Return SomeValue
End Function
End Class
End Namespace
Second application:
Namespace SecondApplication
Class MainWindow
Public Sub New()
InitializeComponent()
End Sub
Public Sub RunBatch()
'Call RunBatch() from first Application, get show the result
Msgbox(RunBatch)
End Function
End Class
End Namespace
Both are WPF, .Net 4.0 based. The goal is to have second application call on the first one and execute a function in it.
The key part is that both applications are used primarily independently and only occasionally second calls on the first. Because both applications need to exist as executable, I don't want to solve the problem by creating a dll of the first application - I would need to maintain both the executable and dll update to date with potentially disastrous consequences if they fall out of sync.
So the question is whether it's possible, to create an instance of first application within AppDomain of the second one and, crucially, execute functions of that instance.
I don't believe you can create one AppDomain inside of another.
Any other options you have (using WCF, or old style .NET Remoting, or crossing AppDomains) are all going to be more complicated than just making a single DLL that both applications can reference. So long as you don't change the assembly number of the shared DLL you won't have to recompile each exe if you make changes to the DLL (assuming you don't make breaking changes like changing method signatures).
Does FirstApplication have to do something to SecondApplication? Are you trying to control a feature of one application from another? If so you will need something like WCF (using Named Pipes or just a self-hosted web service). Or just trying to not have to write the same code twice? Then the simplest approach is probably to create a single DLL both applications reference.
Apparently, this can be done via reflection. The process is straightforward, though not nearly as convenient as using dll.
Public Class CalltoExternallApp
'this is the declaration of the external application you want to run within your application
Dim newAssembly As System.Reflection.Assembly = System.Reflection.Assembly.LoadFrom("Mydirectory\Myfile.exe")
Public Sub Main()
'Loads the main entry point of the application i.e. calls default constructor and assigns handle for it to MyApplication
Dim MyApplication = newAssembly.CreateInstance("MyApplication.RootClass")
'Get the type which will allow for calls to methods within application
Dim MyApplicationType as Type = newAssembly.GetType("MyApplication.RootClass")
'If calling a function, the call will return value as normal.
Dim Result As Object = LunaMain.InvokeMember("MyFunction", Reflection.BindingFlags.InvokeMethod, Nothing, MyApplication, MyParameters)
End Sub
End Class
Check also here for adding the event handlers to instances created via Reflection:
http://msdn.microsoft.com/en-us/library/ms228976.aspx

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.

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