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.
<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>
<form id="form1" runat="server">
<asp:ScriptManager ID="Scriptmanager1" runat="server">
<Services>
<asp:ServiceReference Path="~/MyService.svc/ajax" />
</Services>
</asp:ScriptManager>
</form>
<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;
}