DCSIMG
Adding a Web reference dynamically at Runtime And Invoke the methods - שלמה גולדברג (הרב דוטנט)

שלמה גולדברג (הרב דוטנט)

מרצה בסלע ויועץ בעולם ה - net.

Adding a Web reference dynamically at Runtime And Invoke the methods

כיצד ניתן להפעיל מתודות של WebService שה - Proxy שלו נוצר בזמן ריצה.

 
 
בפוסט הקודם הראיתי שני דרכים לעבוד עם WebService שיש לנו את הכתובת שלו רק בזמן ריצה.
 
קבלתי שאלה במייל כיצד ניתן לקבל את רשימת המתודות שלו ולהפעיל אותם.
 
התשובה היא כמובן בעזרת reflection. ואני אדגים כאן איך עושים את זה.
 
 
את דוגמת הקוד ניתן להוריד כאן. הפתרון של בניית ה - WebService בצורה דינמית מתבסס על התשובה כאן
 
 
נתחיל:
נגדיר מחלקה בשם WsProxy. ויהיו בו המתודות הבאות:
 
  • GetWebService
  • Methods
  • GetPartameters
  • Inovke
 
נעבור על המתודות.
GetWebService תפקידה לקבל את הכתובת של ה - WevService וליצור proxy בזמן ריצה
 

[SecurityPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]

internal static object GetWebService(string webServiceAsmxUrl, string serviceName)

{

    WebClient client = new WebClient();

 

    // Connect To the web service

    Stream stream = client.OpenRead(webServiceAsmxUrl + "?wsdl");

 

    // Now read the WSDL file describing a service.

    ServiceDescription description = ServiceDescription.Read(stream);

 

    ///// LOAD THE DOM /////////

 

    // Initialize a service description importer.

    ServiceDescriptionImporter importer = new ServiceDescriptionImporter();

    importer.ProtocolName = "Soap12"; // Use SOAP 1.2.

    importer.AddServiceDescription(description, null, null);

 

    // Generate a proxy client.

    importer.Style = ServiceDescriptionImportStyle.Client;

 

    // Generate properties to represent primitive values.

    importer.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;

 

    // Initialize a Code-DOM tree into which we will import the service.

 

    CodeNamespace nmspace = new CodeNamespace();

    CodeCompileUnit unit1 = new CodeCompileUnit();

 

    unit1.Namespaces.Add(nmspace);

 

    // Import the service into the Code-DOM tree. This creates proxy code that uses the service.

    ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit1);

 

    if (warning == 0) // If zero then we are good to go

    {

        // Generate the proxy code

 

        CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");

 

        // Compile the assembly proxy with the appropriate references

        string[] assemblyReferences = new string[5]

                                                {

                                                    "System.dll",

                                                    "System.Web.Services.dll",

                                                    "System.Web.dll",

                                                    "System.Xml.dll",

                                                    "System.Data.dll"

                                                };

 

        CompilerParameters parms = new CompilerParameters(assemblyReferences);

        CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);

 

        // Check For Errors

        if (results.Errors.Count > 0)

        {

            throw new System.Exception("Compile Error Occured calling webservice. Check Debug ouput window.");

        }

 

        // Finally, Invoke the web service method

        object wsvcClass = results.CompiledAssembly.CreateInstance(serviceName);

 

        return wsvcClass;

    }

    else

    {

        return null;

    }

}

 
 
מה שהמתודה הזאת עושה זה לקבל את הכתובת של ה - WebService לקבל אותה בפורמט Soap12 ולהחזיר מופע של המחלקה. (בעזרת קומפילצייה בזמן ריצה)
 
 
Methods מקבלת כפרמטר את האובייקט ומחזירה מערך של מחרוזות כשכל מחרוזת מייצגת שם של מתודה שיש עליה attribute שזה מתודה שהיא בעצם WebMethod
 

internal static string[] Methods(object webService)

{

    var list = webService.GetType().GetMethods().

            Where(x => x.GetCustomAttributes(typeof(SoapDocumentMethodAttribute), false).Length > 0).

            Select(y => y.Name).ToArray();

 

    return list;

}

 
 
 
GetParameters מקבלת שם של מתודה ומחזירה אוסף שמייצג את שמות הפרמטרים והטיפוס שלהם
 

internal static List<KeyValuePair<string, Type>> GetParameters(object service, string methodeName)

{

    var list = new List<KeyValuePair<string, Type>>();

 

    MethodInfo mi = service.GetType().GetMethod(methodeName);

    foreach (var parameter in mi.GetParameters())

    {

        list.Add(new KeyValuePair<string, Type>(parameter.Name, parameter.ParameterType));

    }

 

 

    return list;

}

 
 
Invoke מקבלת את הפרמטרים ומפעילה את המתודה
 

internal static object Invoke(object service, string methodName, object[] parameters)

{

    return service.GetType().GetMethod(methodName).Invoke(service, parameters);

}

 
 
 
עד כאן המחלקה שתוכלו להשתמש איתה בכל צורה.
 
הדוגמא שאני אציג כאן היא שימוש במחלקה הזאת ב - Web Application.
 
 
נניח שיש לנו שני Web Serives שנראים כך:
 

public class MyWebService : System.Web.Services.WebService

{

    [WebMethod]

    public string HelloWorld()

    {

        return "Hello World";

    }

 

    [WebMethod]

    public int Add(int a, int b)

    {

        return a + b;

    }

}

 

public class YourWebService : System.Web.Services.WebService

{

 

    [WebMethod]

    public string HelloWorld()

    {

        return "Hello World";

    }

 

    [WebMethod]

    public int Random()

    {

        return new Random().Next(0, 100);

    }

}

 
 
אנחנו רוצים להפעיל את המתודות של ה - Web Services האלו בצורה  דינמית.
 
 צד לקוח:
נכתוב דף aspx שבו יש את הקוד הבא

<asp:DropDownList ID="ddlService" runat="server" AutoPostBack="true"

                    OnSelectedIndexChanged="ddlService_SelectedIndexChanged">

    <asp:ListItem Text="Select Service"></asp:ListItem>

    <asp:ListItem Text="http://localhost:51119/MyWebService.asmx"

                    Value="MyWebService"></asp:ListItem>

    <asp:ListItem Text="http://localhost:51119/YourWebService.asmx"

                    Value="YourWebService"></asp:ListItem>

</asp:DropDownList>

 
יש לנו הגדרה של DropDownList ובו רשימה של כתובות של webServices (כמובן שזה יכול להתמלא בצורה דינמית מבסיס נתונים כלשהו)
 
בנוסף לנו עוד DropDownList עבור המתודות

<asp:DropDownList ID="ddlMethod" runat="server" AutoPostBack="true"

            OnSelectedIndexChanged="ddlMethod_SelectedIndexChanged">

    <asp:ListItem Text="Select Method"></asp:ListItem>

</asp:DropDownList>

 
 
בנוסף יש לנו table שיבנה בזמן ריצה לפי הפרמטרים
 

<table id="tblParam" runat="server">

</table>

 
ולחצן שיפעיל את המתודה

<asp:Button ID="btnInvoke" runat="server" Text="Invoke" OnClick="btnInvoke_Click" />

 
 
 
צד שרת
 
בזמן בחירה של Service ב - DropDownList נבצע את הקוד הבא
 

protected void ddlService_SelectedIndexChanged(object sender, EventArgs e)

{

    ddlMethod.Items.Clear();

    ddlMethod.Items.Add("Select Method");

 

    if (Session[ddlService.SelectedValue] == null)

    {

        Session[ddlService.SelectedValue] =

                WsProxy.GetWebService(ddlService.SelectedItem.Text, ddlService.SelectedItem.Value);

    }

 

    ddlMethod.Items.AddRange(WsProxy.Methods(Session[ddlService.SelectedValue]).

                            Select(x => new ListItem(x)).ToArray());

}

 
אנחנו בודקים האם כבר יצרנו את האובייקט מה - Service (מכיון שאין טעם לייצר בזמן ריצה כל פעם את ה - Proxy מחדש - זה סיפור יקר מכיוון שאנחנו מקמפלים מחלקה בזמן ריצה)
 
לאחר שיש לנו את האובייקט (או שקמפלנו או שהוצאנו מה - Session)
 
אנחנו מפעילים את מתודת Methods ושולחים כפרמטר את האובייקט וממירים את מערך המחרוזות למערך של ListItem.
את מערך ה - ListItem אנחנו מוסיפים ל - DropDownList השני (של המתודות)
 
בזמן בחירה באחד מהמתודות נפעיל את הקוד הבא 
 
 
 

protected void ddlMethod_SelectedIndexChanged(object sender, EventArgs e)

{

    var parameters = WsProxy.GetParameters(Session[ddlService.SelectedValue], ddlMethod.Text);

    foreach (var item in parameters)

    {

        HtmlTableRow row = new HtmlTableRow();

        HtmlTableCell cell = new HtmlTableCell();

 

        cell.InnerHtml = string.Format("{0} ({1}): <input type='text' name='{2}'/>",

                                        item.Key, item.Value.Name, item.Key);

       

        // num1 (Int32) <input type='text' name ='num1'/>

 

        row.Cells.Add(cell);

        tblParam.Rows.Add(row);

    }

}

 
נקבל את הפרמטרים מהמחלקה WsProxy עבור כל אחד מהפרמטרים נוסיף לטבלה שורה ותיבת טקסט שהמאפיין name יהיה שם הפרמטר (כך שהערך יחזור ב - request)
 
 
נניח שהמשתמש בחר ב - WebService הראשון ובחר במתודה Name המסך שלו יראה כך
 
Adding a Web reference dynamically at Runtime And Invoke the methods
 
 
לחיצה על Invoke תפעיל את הקוד הבא:
 

protected void btnInvoke_Click(object sender, EventArgs e)

{

    object service = Session[ddlService.SelectedValue];

    var parameters = WsProxy.GetParameters(service, ddlMethod.Text);

    object[] @params = new object[parameters.Count];

 

    for (int i = 0; i < parameters.Count; i++)

    {

        var data = Request[parameters[i].Key];

        @params[i] = Convert.ChangeType(data, parameters[i].Value);

    }

 

    var res = WsProxy.Invoke(service, ddlMethod.Text, @params);

}

 
נקבל את האובייקט מה - Session
נקבל את כל הפרמטרים,
נמלא מערך של אובייקטים לפי ה - Type שלהם בעזרת Convert.ChangeType
ונפעיל את המתודה בעזרת מתודת Invoke שכתבנו.
 
 
 
כאמור זאת דוגמת Web ותוכלו להוריד את הקוד מכאן, אבל תוכלו להשתמש במחלקה גם בפלטפורמה אחרת.

תוכן התגובה

Shlomo כתב/ה:

אני לא חושב שצריך מכיון שאתה יכול לפנות ל - WCF SERVICE מקוד לפי כתובת כל עוד שיש לך את ה - interface

# May 24, 2010 6:37 PM
שלח תגובה

(שדה חובה)  

(שדה חובה)  

(אופציונלי)

(שדה חובה) 

Please add 3 and 6 and type the answer here:


Enter the numbers above: