Вопрос

I'm using asp:ScriptManager to call my webmethods from javascript. Now I migrate to TypeScript and would like to have my WebMethods and DTO definitions be usable from TypeScript. I searched, but couldn't not find anything (except a suggestion) that does this.

Now I'm implementing a code-generation tool that takes wsdl and generates typescript typings. I'll share it here when it is done, but meanwhile if you know any tool that does this, I'll be happy to hear.

Thanks in advance.


This is how I call webmethods from javascipt.

1) web.config

<system.serviceModel>
<services>
  <service name="Services.MyService" behaviorConfiguration="metadataBehavior">
    <endpoint binding="basicHttpBinding" contract="Services.MyService"/>
    <endpoint address="/ajax" behaviorConfiguration="Services.MyServiceAspNetAjaxBehavior" binding="webHttpBinding" contract="Services.MyService"/>
  </service>
</services>
<behaviors>
  <endpointBehaviors>
    <behavior name="Services.MyServiceAspNetAjaxBehavior">
      <enableWebScript />
    </behavior>
  </endpointBehaviors>
</behaviors>
</system.serviceModel>

2) Default.aspx

<form id="form1" runat="server">
    <asp:ScriptManager ID="Scriptmanager1" runat="server">
        <Services>
            <asp:ServiceReference Path="~/MyService.svc/ajax" />
        </Services>
    </asp:ScriptManager>
</form>

3) SomeFile.js

<script>
    MyService.SomeMethod(someParameter1, someParameter2, function(result) {
        alert("Success: " + result);
    }, function(err){
        alert("Error: " + err);
    });

</script>

I can see a js file that has functions to call my webservice on ~/MyService.svc/ajax/jsdebug

Это было полезно?

Решение

Well here is the code i write and use, but you need to change some parameters for your own wsdl. Feedbacks are appriciated.

Also if someone upgrades and makes this into a blog post, that would be great I guess (I don't have a blog) many people would need this I think, this saves a lot of time on our project and tidy things up.

By the way I run this code in Linqpad. That's why methods are not static and there is a object.Dump() call in the last line of Main()

void Main()
{
    var svcUrl = "http://localhost:12345/MyService.svc";
    var declaration = "declare var MyService: MyServiceClient;";
    var koRef = "/// <reference path=\"../../Scripts/typings/knockout.mapping/knockout.mapping.d.ts" +
    "\" />\r\n/// <reference path=\"../../Scripts/typings/knockout/knockout.d.ts\" />";


    TypeScriptWebServiceAndKnockOutTypings(svcUrl, declaration, koRef).Dump();
}


string TypeScriptWebServiceAndKnockOutTypings(string svcUrl, string declaration, string koRef)
{
    var outputPath = @"d:\output";

    File.Delete(outputPath + ".cs");
    File.Delete(outputPath + ".dll");
    Process.Start(@"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\x64\svcutil.exe",  " /language:cs /out:" + outputPath + ".cs " + svcUrl).WaitForExit();
    Process.Start(@"C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe", " /t:library /out:"+ outputPath + ".dll  "+ outputPath + ".cs").WaitForExit();

    var a = Assembly.LoadFile(outputPath + ".dll");

    var sbEnums = new StringBuilder();
    var sbClasses = new StringBuilder();

    #region types conversions

    var dictTypesNormal = new Dictionary<Type, string>();
    dictTypesNormal.Add(typeof(Int32), "number");
    dictTypesNormal.Add(typeof(UInt32), "number");
    dictTypesNormal.Add(typeof(Int64), "number");
    dictTypesNormal.Add(typeof(UInt64), "number");
    dictTypesNormal.Add(typeof(float), "number");
    dictTypesNormal.Add(typeof(double), "number");
    dictTypesNormal.Add(typeof(bool), "bool");
    dictTypesNormal.Add(typeof(string), "string");
    dictTypesNormal.Add(typeof(Object), "any");
    dictTypesNormal.Add(typeof(DateTime), "Date");

    var dictTypesKnockout = new Dictionary<Type, string>();
    dictTypesKnockout.Add(typeof(Int32), "KnockoutObservableNumber");
    dictTypesKnockout.Add(typeof(UInt32), "KnockoutObservableNumber");
    dictTypesKnockout.Add(typeof(Int64), "KnockoutObservableNumber");
    dictTypesKnockout.Add(typeof(UInt64), "KnockoutObservableNumber");
    dictTypesKnockout.Add(typeof(float), "KnockoutObservableNumber");
    dictTypesKnockout.Add(typeof(double), "KnockoutObservableNumber");
    dictTypesKnockout.Add(typeof(bool), "KnockoutObservableBool");
    dictTypesKnockout.Add(typeof(string), "KnockoutObservableString");
    dictTypesKnockout.Add(typeof(Object), "KnockoutObservableAny");
    dictTypesKnockout.Add(typeof(DateTime), "KnockoutObservableDate");

    var classNames = a.DefinedTypes.Where(t => !t.IsEnum).Select(t=>t.Name).ToList();
    var enumNames = a.DefinedTypes.Where(t => t.IsEnum).Select(t=>t.Name).ToList();

    #region get type name

    Func<Type, string> getTypeNameNormal = t => {
        if(dictTypesNormal.ContainsKey(t)) 
            return dictTypesNormal[t];

        if(classNames.Contains(t.Name.Replace("[]", ""))) 
            return t.Name;

        if(enumNames.Contains(t.Name.Replace("[]", ""))) 
            return "Enum" + t.Name;

        throw new Exception(string.Format("Unknown type [{0}]", t.Name));
    };

    Func<Type, string> getTypeNameKnockout = t => {
        if(dictTypesKnockout.ContainsKey(t)) 
            return dictTypesKnockout[t];

        if(classNames.Contains(t.Name.Replace("[]", ""))) 
            return t.Name + "_KnockoutMapped";

        if(enumNames.Contains(t.Name.Replace("[]", ""))) 
            return "KnockoutObservableEnum" + t.Name + "EnumMember";

        throw new Exception(string.Format("Unknown type [{0}]", t.Name));
    };

    #endregion

    #region convert field 

    Func<string, Type, string> convertFieldNormal = null; convertFieldNormal = (pName, f) => {

        // void
        if(f == typeof(void)) 
            return "";

        // array
        if(f.IsArray)
            return string.Format("{0}{1}[]", pName, convertFieldNormal("", f.GetElementType()));

        // property
        if(!f.IsGenericType) 
            return string.Format("{0}: {1}", pName, getTypeNameNormal(f));

        // nullable
        if(f.GetGenericTypeDefinition() == typeof(Nullable<>)) 
            return string.Format("{0}: {1}", pName, getTypeNameNormal(f.GetGenericArguments()[0]));

        // dictionaries
        if(f.GetGenericTypeDefinition().ToString().Contains("Dictionary")) 
            return string.Format("{0}: {{ {1}; {2}; }}[]", pName, convertFieldNormal("Key", f.GetGenericArguments()[0]), convertFieldNormal("Value", f.GetGenericArguments()[1]));

        throw new Exception(string.Format("Unknown generic type [{0}] for property {1}", f.Name, pName));
    };

    Func<string, Type, string> convertFieldKnockout = null; convertFieldKnockout = (pName, f) => {

        // void
        if(f == typeof(void)) 
            return "";

        // array
        if(f.IsArray)
            return string.Format("{0}{1}[]", pName, convertFieldKnockout("", f.GetElementType()));

        // property
        if(!f.IsGenericType) 
            return string.Format("{0}: {1}", pName, getTypeNameKnockout(f));

        // nullable
        if(f.GetGenericTypeDefinition() == typeof(Nullable<>)) 
            return string.Format("{0}: {1}", pName, getTypeNameKnockout(f.GetGenericArguments()[0]));

        // dictionaries
        if(f.GetGenericTypeDefinition().ToString().Contains("Dictionary")) 
            return string.Format("{0}: {{ {1}; {2}; }}[]", pName, convertFieldKnockout("Key", f.GetGenericArguments()[0]), convertFieldKnockout("Value", f.GetGenericArguments()[1]));

        throw new Exception(string.Format("Unknown generic type [{0}] for property {1}", f.Name, pName));
    };

    #endregion

    #endregion

    #region enums

    foreach (var e in a.DefinedTypes.Where(t => t.IsEnum))
    {
        #region enum definition
        sbEnums.AppendLine(string.Format("enum Enum{0} {{", e.Name));

        sbEnums.AppendLine(string.Format("\t{0} = {1},", "Tümü", -1)); 

        foreach (var v in Enum.GetValues(e))
        {
            sbEnums.AppendLine(string.Format("\t{0} = {1},", v.ToString(), (int)v)); 
        }

        sbEnums.AppendLine("}");
        #endregion

        #region knockout

        sbEnums.AppendLine(string.Format(
            @"interface KnockoutObservable{0} extends KnockoutObservableBase {{
                (): {0};
                (value: {0}): void;

                subscribe(callback: (newValue: {0}) => void , target?: any, topic?: string): KnockoutSubscription;
                notifySubscribers(valueToWrite: {0}, topic?: string);
            }}", "Enum" + e.Name + "EnumMember"));

        #endregion

        #region enum member

        sbEnums.AppendLine(string.Format(
            @"class Enum{0}EnumMember {{
                constructor(public Key: Enum{0}, public Value: string) {{ }};
                public toString() {{ return this.Value }};
              }}", e.Name));

        #endregion

        #region combobox definition
        sbEnums.AppendLine(string.Format("var {0}s: Enum{0}EnumMember[] = [", e.Name));

        sbEnums.AppendLine(string.Format("\t new Enum{0}EnumMember(Enum{0}.{1}, \"{1}\"),", e.Name, "Tümü")); 

        foreach (var v in Enum.GetValues(e))
        {
            sbEnums.AppendLine(string.Format("\t new Enum{0}EnumMember(Enum{0}.{1}, \"{1}\"),", e.Name, v.ToString())); 
        }

        sbEnums.AppendLine("];");
        #endregion

        //public OnayDurumlari = ko.observable([{ Key: 3, Value: "Tümü" }, { Key: 0, Value: "Onay Bekliyor" }, { Key: 1, Value: "Onaylı" }, { Key: 2, Value: "Red" }]); 

        sbEnums.AppendLine();
    }

    #endregion

    #region classes

    #region find classes & methods

    var classes =  (from t in a.DefinedTypes
                    where t.IsEnum == false
                    let methods = (from m in t.GetMethods()
                                    where !m.IsSpecialName 
                                    && m.IsFinal 
                                    && m.IsVirtual 
                                    && m.IsHideBySig 
                                    && m.IsSecurityCritical 
                                    && !m.Name.EndsWith("Async")
                                    select m)
                    where t.DeclaredProperties.Any()
                        || methods.Any()
                    select new
                    {
                        t.Name,
                        t.DeclaredProperties,
                        methods,
                    });

    #endregion

    foreach (var t in classes)
    {
        #region interface definition

        sbClasses.AppendLine(string.Format("{1} {0} {{", t.Name, t.DeclaredProperties.Any() ? "class" : "interface"));

        foreach (var p in t.DeclaredProperties)
        {
            if(p.Name == "ExtensionData") continue;
            var f = p.GetGetMethod().ReturnType;

            sbClasses.AppendLine(string.Format("\t{0};", convertFieldNormal(p.Name, f)));
        }

        #region methods

        foreach (var m in t.methods)
        {
            sbClasses.AppendLine(string.Format("\t{0}({1}Success: ({2}) => void, Fail?: (err: Sys$Net$WebServiceError) => void ) : void;", 
                m.Name, 
                string.Concat(m.GetParameters().Select(p => convertFieldNormal(p.Name, p.ParameterType) + ", ")),
                convertFieldNormal("result", m.ReturnType)));
        }

        #endregion

        sbClasses.AppendLine("}");

        #endregion

        #region knockout

        if(t.DeclaredProperties.Any())
        {
            sbClasses.AppendLine(string.Format(@"
                interface KnockoutObservable{0} extends KnockoutObservableBase {{
                    (): {0};
                    (value: {0}): void;

                    subscribe(callback: (newValue: {0}) => void , target?: any, topic?: string): KnockoutSubscription;
                    notifySubscribers(valueToWrite: {0}, topic?: string);
                }}", t.Name));
//  
//          sbClasses.AppendLine(string.Format(@"
//              interface KnockoutObservableStatic {{
//                  (value: {0}): KnockoutObservable{0};
//                  new (value: {0}): KnockoutObservable{0};
//              }}", t.Name));

            #region KnockoutObservableArray

            sbClasses.AppendLine(string.Format(
                @"interface {0}_KnockoutObservableArray extends KnockoutObservableArrayFunctions {{
                    (): {0}_KnockoutMapped[];
                    (value: {0}_KnockoutMapped[]): void;

                    subscribe(callback: (newValue: {0}_KnockoutMapped[]) => void, target?:any, topic?: string): KnockoutSubscription;
                    notifySubscribers(valueToWrite: {0}_KnockoutMapped[], topic?: string);
                }}", t.Name));

            #endregion

            #region _KnockoutMapped
            sbClasses.AppendLine(string.Format("interface {0}_KnockoutMapped {{", t.Name));

            foreach (var p in t.DeclaredProperties)
            {
                if(p.Name == "ExtensionData") continue;
                var f = p.GetGetMethod().ReturnType;

                sbClasses.AppendLine(string.Format("\t{0};", convertFieldKnockout(p.Name, f)));
            }               
            sbClasses.AppendLine("}");
            #endregion

        }
        #endregion

        sbClasses.AppendLine();
    }

    #endregion

    #region knockout

    var sbKnockout = new StringBuilder();

    #region mapper
    sbKnockout.AppendLine("module Mapper {");
    classes.Where(c=>c.DeclaredProperties.Any()).ToList().ForEach(c=>{
        sbKnockout.AppendLine(string.Format("\texport function fromJs_{0}(r : {0}) : {0}_KnockoutMapped {{return ko.mapping.fromJS(r);}}", c.Name));
        sbKnockout.AppendLine(string.Format("\texport function toJs_{0}(r : {0}_KnockoutMapped) : {0} {{return ko.mapping.toJS(r);}}", c.Name));
        sbKnockout.AppendLine(string.Format("\texport function fromJsArray_{0}(r : {0}[]) : {0}_KnockoutMapped[] {{return r.map(k => ko.mapping.fromJS(k));}}", c.Name));
        sbKnockout.AppendLine(string.Format("\texport function toJsArray_{0}(r : {0}_KnockoutMapped[]) : {0}[] {{return r.map(k => ko.mapping.toJS(k));}}", c.Name));
    });
    sbKnockout.AppendLine("}");
    sbKnockout.AppendLine();
    #endregion

    #region knockout definitions
    sbKnockout.AppendLine("declare var ko;");
    dictTypesKnockout.Select(k=>k.Value).Distinct().ToList().ForEach(k=>{
        sbKnockout.AppendLine(string.Format("interface {0} {{}}", k));
    });
    sbKnockout.AppendLine("interface KnockoutSubscription {}");
    sbKnockout.AppendLine("interface KnockoutObservableArrayFunctions {}");
    sbKnockout.AppendLine();
    #endregion

    #endregion

    return koRef 
         + "interface Sys$Net$WebServiceError {_timedOut: bool;_message: string;_stackTrace: string;_exceptionType: string;_errorObject: {ExceptionDetail: {HelpLink: any; InnerException: any; Message: string; StackTrace: string;Type: string;};ExceptionType: string;Message: string;StackTrace: string;};}\r\n\r\n"
         + sbEnums.ToString()
         + sbClasses.ToString()
         + sbKnockout
         + declaration;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top