Using reflection to call an ASP.NET web service

2020-05-24 05:43发布

问题:

Say I have an ASMX web service, MyService. The service has a method, MyMethod. I could execute MyMethod on the server side as follows:

MyService service = new MyService();
service.MyMethod();

I need to do similar, with service and method not known until runtime.

I'm assuming that reflection is the way to go about that. Unfortunately, I'm having a hard time making it work. When I execute this code:

Type.GetType("MyService", true);

It throws this error:

Could not load type 'MyService' from assembly 'App_Web__ktsp_r0, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.

Any guidance would be appreciated.

回答1:

I'm not sure if this would be the best way to go about it. The most obvious way to me, would be to make an HTTP Request, and call the webservice using an actual HTTP GET or POST. Using your method, I'm not entirely sure how you'd set up the data you are sending to the web service. I've added some sample code in VB.Net

Dim HTTPRequest As HttpWebRequest
Dim HTTPResponse As HttpWebResponse
Dim ResponseReader As StreamReader
Dim URL AS String
Dim ResponseText As String

URL = "http://www.example.com/MyWebSerivce/MyMethod?arg1=A&arg2=B"

HTTPRequest = HttpWebRequest.Create(URL)
HTTPRequest.Method = "GET"

HTTPResponse = HTTPRequest.GetResponse()

ResponseReader = New StreamReader(HTTPResponse.GetResponseStream())
ResponseText = ResponseReader.ReadToEnd()


回答2:

// Try this ->

    Type t = System.Web.Compilation.BuildManager.GetType("MyServiceClass", true);
    object act = Activator.CreateInstance(t);                
    object o = t.GetMethod("hello").Invoke(act, null);


回答3:

Although I don't know why Reflection is not working for you there (I assume the compiler might be creating a new class from your [WebService] annotations), here is some advice that might solve your problem:

Keep your WebService simple, shallow, in short: An implementation of the Facade Pattern.

Make your service delegate computation to an implementation class, which should easily be callable through Reflection. This way, your WebService class is just a front for your system - you can even add an email handler, XML-RPC frontend etc., since your logic is not coupled to the WebService, but to an actual business layer object.

Think of WebService classes as UI layer objects in your Architecture.



回答4:

Here's a quick answer someone can probably expand on.

When you use the WSDL templating app (WSDL.exe) to genereate service wrappers, it builds a class of type SoapHttpClientProtocol. You can do it manually, too:

public class MyService : SoapHttpClientProtocol
{
    public MyService(string url)
    {
        this.Url = url;
        // plus set credentials, etc.
    }

    [SoapDocumentMethod("{service url}", RequestNamespace="{namespace}", ResponseNamespace="{namespace}", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
    public int MyMethod(string arg1)
    {
        object[] results = this.Invoke("MyMethod", new object[] { arg1 });
        return ((int)(results[0]));
    }
}

I haven't tested this code but I imagine it should work stand-alone without having to run the WSDL tool.

The code I've provided is the caller code which hooks up to the web service via a remote call (even if for whatever reason, you don't actually want it to be remote.) The Invoke method takes care of packaging it as a Soap call. @Dave Ward's code is correct if you want to bypass the web service call via HTTP - as long as you are actually able to reference the class. Perhaps the internal type is not "MyService" - you'd have to inspect the control's code to know for sure.



回答5:

@Kibbee: I need to avoid the HTTP performance hit. It won't be a remote call, so all of that added overhead should be unnecessary.

@Daren: I definitely agree with that design philosophy. The issue here is that I'm not going to be in control of the service or its underlying business logic.

This is for a server control that will need to execute against an arbitrary service/method, orthogonally to how the web service itself is implemented.



回答6:

Although I cannot tell from your post:

One thing to keep in mind is that if you use reflection, you need to create an instance of the autogenerated webservice class(the one created from your webservice's WSDL). Do not create the class that is responsbile for the server-side of the service.

So if you have a webservice

    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    public class WebService1 : System.Web.Services.WebService
    {
     ...
    }

you cannot reference that assembly in your client and do something like:

WebService1  ws = new WebService1 ();
ws.SomeMethod();


回答7:

@Radu: I'm able to create an instance and call the method exactly like that. For example, if I have this ASMX:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MyService : System.Web.Services.WebService
{
  [WebMethod]
  public string HelloWorld()
  {
    return "Hello World";
  }
}

I'm able to call it from an ASPX page's codebehind like this:

MyService service = new MyService();
Response.Write(service.HelloWorld());

Are you saying that shouldn't work?



回答8:

I looked back at this question and I think what you're facing is that the ASMX code will be built into a DLL with a random name as part of the dynamic compilation of your site. Your code to look up the type will, by default, only search its own assembly (another App_Code DLL, by the looks of the error you received) and core libraries. You could provide a specific assembly reference "TypeName, AssemblyName" to GetType() but that's not possible in the case of the automatically generated assemblies, which have new names after each recompile.

Solution.... I haven't done this myself before but I believe that you should be able to use something like this:

System.Web.Compilation.BuildManager.GetType("MyService", true)

as the BuildManager is aware of the DLLs it has created and knows where to look.

I guess this really doesn't have to do with Web Services but if it were your own code, Daren's right about Facade patterns.