You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,455 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ClientProxyGenerator.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Script.Serialization;
|
||||
|
||||
internal abstract class ClientProxyGenerator {
|
||||
private static string DebugXmlComments = @"/// <param name=""succeededCallback"" type=""Function"" optional=""true"" mayBeNull=""true""></param>
|
||||
/// <param name=""failedCallback"" type=""Function"" optional=""true"" mayBeNull=""true""></param>
|
||||
/// <param name=""userContext"" optional=""true"" mayBeNull=""true""></param>
|
||||
";
|
||||
private Hashtable _registeredNamespaces = new Hashtable();
|
||||
private Hashtable _ensuredObjectParts = new Hashtable();
|
||||
protected StringBuilder _builder;
|
||||
protected bool _debugMode;
|
||||
// comments are the same in the instance methods as they are in the static methods
|
||||
// this cache is used when calculating comments for instance methods, then re-used when
|
||||
// writing out static methods.
|
||||
private Dictionary<string, string> _docCommentCache;
|
||||
|
||||
internal string GetClientProxyScript(WebServiceData webServiceData) {
|
||||
if (webServiceData.MethodDatas.Count == 0) return null;
|
||||
_builder = new StringBuilder();
|
||||
|
||||
if (_debugMode) {
|
||||
_docCommentCache = new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
// Constructor
|
||||
GenerateConstructor(webServiceData);
|
||||
|
||||
// Prototype functions
|
||||
GeneratePrototype(webServiceData);
|
||||
|
||||
GenerateRegisterClass(webServiceData);
|
||||
GenerateStaticInstance(webServiceData);
|
||||
GenerateStaticMethods(webServiceData);
|
||||
|
||||
// Generate some client proxy to make some types instantiatable on the client
|
||||
GenerateClientTypeProxies(webServiceData);
|
||||
GenerateEnumTypeProxies(webServiceData.EnumTypes);
|
||||
return _builder.ToString();
|
||||
}
|
||||
|
||||
protected void GenerateRegisterClass(WebServiceData webServiceData) {
|
||||
// Generate registerClass: Foo.NS.WebService.registerClass('Foo.NS.WebService', Sys.Net.WebServiceProxy);
|
||||
string typeName = GetProxyTypeName(webServiceData);
|
||||
_builder.Append(typeName).Append(".registerClass('").Append(typeName).Append("',Sys.Net.WebServiceProxy);\r\n");
|
||||
}
|
||||
|
||||
protected virtual void GenerateConstructor(WebServiceData webServiceData) {
|
||||
GenerateTypeDeclaration(webServiceData, false);
|
||||
_builder.Append("function() {\r\n");
|
||||
_builder.Append(GetProxyTypeName(webServiceData)).Append(".initializeBase(this);\r\n");
|
||||
GenerateFields();
|
||||
_builder.Append("}\r\n");
|
||||
}
|
||||
|
||||
protected virtual void GeneratePrototype(WebServiceData webServiceData) {
|
||||
GenerateTypeDeclaration(webServiceData, true);
|
||||
_builder.Append("{\r\n");
|
||||
// private method to return the path to be used , returns _path from current instance if set, otherwise returns _path from static instance.
|
||||
_builder.Append("_get_path:function() {\r\n var p = this.get_path();\r\n if (p) return p;\r\n else return ");
|
||||
_builder.Append(GetProxyTypeName(webServiceData)).Append("._staticInstance.get_path();},\r\n");
|
||||
bool first = true;
|
||||
foreach (WebServiceMethodData methodData in webServiceData.MethodDatas) {
|
||||
if (!first) {
|
||||
_builder.Append(",\r\n");
|
||||
}
|
||||
first = false;
|
||||
GenerateWebMethodProxy(methodData);
|
||||
}
|
||||
_builder.Append("}\r\n");
|
||||
}
|
||||
|
||||
protected virtual void GenerateTypeDeclaration(WebServiceData webServiceData, bool genClass) {
|
||||
AppendClientTypeDeclaration(webServiceData.TypeData.TypeNamespace, webServiceData.TypeData.TypeName, genClass, true);
|
||||
}
|
||||
|
||||
protected void GenerateFields() {
|
||||
_builder.Append("this._timeout = 0;\r\n");
|
||||
_builder.Append("this._userContext = null;\r\n");
|
||||
_builder.Append("this._succeeded = null;\r\n");
|
||||
_builder.Append("this._failed = null;\r\n");
|
||||
}
|
||||
|
||||
protected virtual void GenerateMethods() {
|
||||
}
|
||||
|
||||
protected void GenerateStaticMethods(WebServiceData webServiceData) {
|
||||
string className = GetProxyTypeName(webServiceData);
|
||||
// Now generate static methods NS.Service.MyMethod = function()
|
||||
foreach (WebServiceMethodData methodData in webServiceData.MethodDatas) {
|
||||
string methodName = methodData.MethodName;
|
||||
_builder.Append(className).Append('.').Append(methodName).Append("= function(");
|
||||
StringBuilder argBuilder = new StringBuilder();
|
||||
bool first = true;
|
||||
foreach (WebServiceParameterData paramData in methodData.ParameterDatas) {
|
||||
if (!first) argBuilder.Append(',');
|
||||
else first = false;
|
||||
argBuilder.Append(paramData.ParameterName);
|
||||
}
|
||||
if (!first) argBuilder.Append(',');
|
||||
argBuilder.Append("onSuccess,onFailed,userContext");
|
||||
|
||||
_builder.Append(argBuilder.ToString()).Append(") {");
|
||||
|
||||
if (_debugMode) {
|
||||
// doc comments should have been computed already
|
||||
_builder.Append("\r\n");
|
||||
_builder.Append(_docCommentCache[methodName]);
|
||||
}
|
||||
|
||||
_builder.Append(className).Append("._staticInstance.").Append(methodName).Append('(');
|
||||
_builder.Append(argBuilder.ToString()).Append("); }\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract string GetProxyPath();
|
||||
|
||||
protected virtual string GetJsonpCallbackParameterName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual bool GetSupportsJsonp() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void GenerateStaticInstance(WebServiceData data) {
|
||||
string typeName = GetProxyTypeName(data);
|
||||
_builder.Append(typeName).Append("._staticInstance = new ").Append(typeName).Append("();\r\n");
|
||||
|
||||
// Generate the static properties
|
||||
if (_debugMode) {
|
||||
_builder.Append(typeName).Append(".set_path = function(value) {\r\n");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_path(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_path = function() { \r\n/// <value type=\"String\" mayBeNull=\"true\">The service url.</value>\r\nreturn ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_path();}\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_timeout = function(value) {\r\n");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_timeout(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_timeout = function() { \r\n/// <value type=\"Number\">The service timeout.</value>\r\nreturn ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_timeout(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_defaultUserContext = function(value) { \r\n");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_defaultUserContext(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_defaultUserContext = function() { \r\n/// <value mayBeNull=\"true\">The service default user context.</value>\r\nreturn ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_defaultUserContext(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_defaultSucceededCallback = function(value) { \r\n ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_defaultSucceededCallback(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_defaultSucceededCallback = function() { \r\n/// <value type=\"Function\" mayBeNull=\"true\">The service default succeeded callback.</value>\r\nreturn ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_defaultSucceededCallback(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_defaultFailedCallback = function(value) { \r\n");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_defaultFailedCallback(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_defaultFailedCallback = function() { \r\n/// <value type=\"Function\" mayBeNull=\"true\">The service default failed callback.</value>\r\nreturn ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_defaultFailedCallback(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_enableJsonp = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_enableJsonp(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_enableJsonp = function() { \r\n/// <value type=\"Boolean\">Specifies whether the service supports JSONP for cross domain calling.</value>\r\nreturn ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_enableJsonp(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_jsonpCallbackParameter = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_jsonpCallbackParameter(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_jsonpCallbackParameter = function() { \r\n/// <value type=\"String\">Specifies the parameter name that contains the callback function name for a JSONP request.</value>\r\nreturn ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_jsonpCallbackParameter(); }\r\n");
|
||||
}
|
||||
else {
|
||||
_builder.Append(typeName).Append(".set_path = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_path(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_path = function() { return ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_path(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_timeout = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_timeout(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_timeout = function() { return ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_timeout(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_defaultUserContext = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_defaultUserContext(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_defaultUserContext = function() { return ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_defaultUserContext(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_defaultSucceededCallback = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_defaultSucceededCallback(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_defaultSucceededCallback = function() { return ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_defaultSucceededCallback(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_defaultFailedCallback = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_defaultFailedCallback(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_defaultFailedCallback = function() { return ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_defaultFailedCallback(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_enableJsonp = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_enableJsonp(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_enableJsonp = function() { return ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_enableJsonp(); }\r\n");
|
||||
|
||||
_builder.Append(typeName).Append(".set_jsonpCallbackParameter = function(value) { ");
|
||||
_builder.Append(typeName).Append("._staticInstance.set_jsonpCallbackParameter(value); }\r\n");
|
||||
_builder.Append(typeName).Append(".get_jsonpCallbackParameter = function() { return ");
|
||||
_builder.Append(typeName).Append("._staticInstance.get_jsonpCallbackParameter(); }\r\n");
|
||||
}
|
||||
|
||||
// the path has to be the full absolete path if this is a JSONP enabled service. But it is the responsibility
|
||||
// of the caller to GetClientProxyScript to pass the full path if appropriate since determining it may be
|
||||
// dependant on the specific technology.
|
||||
string proxyPath = GetProxyPath();
|
||||
if (!String.IsNullOrEmpty(proxyPath) && (proxyPath.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || proxyPath.StartsWith("https://", StringComparison.OrdinalIgnoreCase))) {
|
||||
// DevDiv 91322: avoid url encoding the domain portion of an IDN url
|
||||
// find the first "/" after the scheme, and only encode after that.
|
||||
int domainStart = proxyPath.IndexOf("://", StringComparison.OrdinalIgnoreCase) + "://".Length;
|
||||
int domainEnd = proxyPath.IndexOf("/", domainStart, StringComparison.OrdinalIgnoreCase);
|
||||
// if no slash after :// was found, it could be a domain only url, http://[some service].com, don't encode any of it
|
||||
if (domainEnd != -1) {
|
||||
proxyPath = proxyPath.Substring(0, domainEnd) + HttpUtility.UrlPathEncode(proxyPath.Substring(domainEnd));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// it doesn't appear to be an absolute url, at least not an http or https one. All relative paths
|
||||
// and other oddities are safely encoded with UrlPathEncode.
|
||||
proxyPath = HttpUtility.UrlPathEncode(proxyPath);
|
||||
}
|
||||
_builder.Append(typeName).Append(".set_path(\"").Append(proxyPath).Append("\");\r\n");
|
||||
if (GetSupportsJsonp()) {
|
||||
_builder.Append(typeName).Append(".set_enableJsonp(true);\r\n");
|
||||
string jsonpParameterName = GetJsonpCallbackParameterName();
|
||||
if (!String.IsNullOrEmpty(jsonpParameterName) && !jsonpParameterName.Equals("callback", StringComparison.Ordinal)) {
|
||||
_builder.Append(typeName).Append(".set_jsonpCallbackParameter(").Append(JavaScriptSerializer.SerializeInternal(jsonpParameterName)).Append(");\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildArgsDictionary(WebServiceMethodData methodData, StringBuilder args, StringBuilder argsDict, StringBuilder docComments) {
|
||||
argsDict.Append('{');
|
||||
foreach (WebServiceParameterData paramData in methodData.ParameterDatas) {
|
||||
string name = paramData.ParameterName;
|
||||
if (docComments != null) {
|
||||
// looks like: /// <param name="foo" type="ClientType">Namespace.ServerType</param>
|
||||
// client type may not match server type for built in js types like date, number, etc.
|
||||
// client type may be omitted for type Object.
|
||||
docComments.Append("/// <param name=\"").Append(name).Append("\"");
|
||||
Type serverType = ServicesUtilities.UnwrapNullableType(paramData.ParameterType);
|
||||
string clientType = GetClientTypeNamespace(ServicesUtilities.GetClientTypeFromServerType(methodData.Owner, serverType));
|
||||
if (!String.IsNullOrEmpty(clientType)) {
|
||||
docComments.Append(" type=\"").Append(clientType).Append("\"");
|
||||
}
|
||||
docComments.Append(">").Append(serverType.FullName).Append("</param>\r\n");
|
||||
}
|
||||
if (args.Length > 0) {
|
||||
args.Append(',');
|
||||
argsDict.Append(',');
|
||||
}
|
||||
args.Append(name);
|
||||
argsDict.Append(name).Append(':').Append(name);
|
||||
}
|
||||
if (docComments != null) {
|
||||
// append the built-in comments that all methods have (success, failed, usercontext parameters)
|
||||
docComments.Append(DebugXmlComments);
|
||||
}
|
||||
argsDict.Append("}");
|
||||
if (args.Length > 0) {
|
||||
args.Append(',');
|
||||
}
|
||||
args.Append("succeededCallback, failedCallback, userContext");
|
||||
}
|
||||
|
||||
private void GenerateWebMethodProxy(WebServiceMethodData methodData) {
|
||||
string methodName = methodData.MethodName;
|
||||
string typeName = GetProxyTypeName(methodData.Owner);
|
||||
string useGet = methodData.UseGet ? "true" : "false";
|
||||
|
||||
_builder.Append(methodName).Append(':');
|
||||
// e.g. MyMethod : function(param1, param2, ..., OnSuccess, OnFailure)
|
||||
StringBuilder args = new StringBuilder();
|
||||
StringBuilder argsDict = new StringBuilder();
|
||||
StringBuilder docComments = null;
|
||||
string docCommentsString = null;
|
||||
if (_debugMode) {
|
||||
docComments = new StringBuilder();
|
||||
}
|
||||
BuildArgsDictionary(methodData, args, argsDict, docComments);
|
||||
|
||||
if (_debugMode) {
|
||||
// Remember the doc comments for the static instance case
|
||||
docCommentsString = docComments.ToString();
|
||||
_docCommentCache[methodName] = docCommentsString;
|
||||
}
|
||||
|
||||
// Method calls look like this.invoke(FooNS.Sub.Method.get_path(), 'MethodName', true[useGet], {'arg1':'val1', 'arg2':'val2' }, onComplete, onError, userContext, 'FooNS.Sub.Method')
|
||||
_builder.Append("function(").Append(args.ToString()).Append(") {\r\n");
|
||||
if (_debugMode) {
|
||||
// docCommentsString always end in \r\n
|
||||
_builder.Append(docCommentsString);
|
||||
}
|
||||
_builder.Append("return this._invoke(this._get_path(), ");
|
||||
_builder.Append("'").Append(methodName).Append("',");
|
||||
_builder.Append(useGet).Append(',');
|
||||
_builder.Append(argsDict.ToString()).Append(",succeededCallback,failedCallback,userContext); }");
|
||||
}
|
||||
|
||||
/* e.g
|
||||
var Qqq = function() { this.__type = "Qqq"; }
|
||||
*/
|
||||
private void GenerateClientTypeProxies(WebServiceData data) {
|
||||
bool first = true;
|
||||
foreach (WebServiceTypeData t in data.ClientTypes) {
|
||||
if (first) {
|
||||
_builder.Append("var gtc = Sys.Net.WebServiceProxy._generateTypedConstructor;\r\n");
|
||||
first = false;
|
||||
}
|
||||
|
||||
string typeID = data.GetTypeStringRepresentation(t);
|
||||
string typeNameWithClientNamespace = GetClientTypeNamespace(t.TypeName);
|
||||
string typeName = ServicesUtilities.GetClientTypeName(typeNameWithClientNamespace);
|
||||
string clientTypeNamespace = GetClientTypeNamespace(t.TypeNamespace);
|
||||
|
||||
EnsureNamespace(t.TypeNamespace);
|
||||
EnsureObjectGraph(clientTypeNamespace, typeName);
|
||||
_builder.Append("if (typeof(").Append(typeName).Append(") === 'undefined') {\r\n");
|
||||
AppendClientTypeDeclaration(clientTypeNamespace, typeNameWithClientNamespace, false, false);
|
||||
// Need to use the _type id, which isn't necessarly the real name
|
||||
_builder.Append("gtc(\"");
|
||||
_builder.Append(typeID);
|
||||
_builder.Append("\");\r\n");
|
||||
_builder.Append(typeName).Append(".registerClass('").Append(typeName).Append("');\r\n}\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Create client stubs for all the enums
|
||||
private void GenerateEnumTypeProxies(IEnumerable<WebServiceEnumData> enumTypes) {
|
||||
foreach (WebServiceEnumData t in enumTypes) {
|
||||
EnsureNamespace(t.TypeNamespace);
|
||||
string typeNameWithClientNamespace = GetClientTypeNamespace(t.TypeName);
|
||||
string typeName = ServicesUtilities.GetClientTypeName(typeNameWithClientNamespace);
|
||||
string[] enumNames = t.Names;
|
||||
long[] enumValues = t.Values;
|
||||
Debug.Assert(enumNames.Length == enumValues.Length);
|
||||
EnsureObjectGraph(GetClientTypeNamespace(t.TypeNamespace), typeName);
|
||||
_builder.Append("if (typeof(").Append(typeName).Append(") === 'undefined') {\r\n");
|
||||
if (typeName.IndexOf('.') == -1) {
|
||||
_builder.Append("var ");
|
||||
}
|
||||
_builder.Append(typeName).Append(" = function() { throw Error.invalidOperation(); }\r\n");
|
||||
_builder.Append(typeName).Append(".prototype = {");
|
||||
for (int i = 0; i < enumNames.Length; i++) {
|
||||
if (i > 0) _builder.Append(',');
|
||||
_builder.Append(enumNames[i]);
|
||||
_builder.Append(": ");
|
||||
if (t.IsULong) {
|
||||
_builder.Append((ulong)enumValues[i]);
|
||||
}
|
||||
else {
|
||||
_builder.Append(enumValues[i]);
|
||||
}
|
||||
}
|
||||
_builder.Append("}\r\n");
|
||||
_builder.Append(typeName).Append(".registerEnum('").Append(typeName).Append('\'');
|
||||
_builder.Append(", true);\r\n}\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual string GetClientTypeNamespace(string ns) {
|
||||
return ns;
|
||||
}
|
||||
|
||||
private void AppendClientTypeDeclaration(string ns, string typeName, bool genClass, bool ensureNS) {
|
||||
// Register the namespace if any
|
||||
// e.g. registerNamespace('MyNS.MySubNS');
|
||||
string name = GetClientTypeNamespace(ServicesUtilities.GetClientTypeName(typeName));
|
||||
if (!String.IsNullOrEmpty(ns)) {
|
||||
if (ensureNS) EnsureNamespace(ns);
|
||||
}
|
||||
else if (!genClass) {
|
||||
// If there is no namespace, we need a var to declare the variable
|
||||
if (!name.Contains(".")) {
|
||||
// if name contains '.', an object graph was already ensured and we dont need 'var'.
|
||||
_builder.Append("var ");
|
||||
}
|
||||
}
|
||||
_builder.Append(name);
|
||||
if (genClass) {
|
||||
_builder.Append(".prototype");
|
||||
}
|
||||
_builder.Append('=');
|
||||
_ensuredObjectParts[name] = null;
|
||||
}
|
||||
|
||||
// Normally returns MyNS.MySubNS.MyWebService OR var MyWebService, PageMethods will return PageMethods
|
||||
protected virtual string GetProxyTypeName(WebServiceData data) {
|
||||
return ServicesUtilities.GetClientTypeName(data.TypeData.TypeName);
|
||||
}
|
||||
|
||||
private void EnsureNamespace(string ns) {
|
||||
//Give derived proxy generator a chance to transform namespace ( used by WCF)
|
||||
ns = GetClientTypeNamespace(ns);
|
||||
|
||||
if (String.IsNullOrEmpty(ns)) return;
|
||||
|
||||
// Don't register a given namespace more than once
|
||||
if (!_registeredNamespaces.Contains(ns)) {
|
||||
_builder.Append("Type.registerNamespace('").Append(ns).Append("');\r\n");
|
||||
_registeredNamespaces[ns] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureObjectGraph(string namespacePart, string typeName) {
|
||||
// When a type name includes dots, such as 'MyNamespace.MyClass.MyNestedClass',
|
||||
// this method writes code that ensures all the parts leading up to the actual class name
|
||||
// are either already namespaces or are at least Objects.
|
||||
// namespacePart is here so we dont unnecessarily ensure the first part that contains the
|
||||
// namespace is checked for. For example, if we have NS1.NS2.NS3.TYPE, the check for
|
||||
// _registeredNamespaces will find NS1.NS2.NS3 but not NS1 and NS1.NS2, so we'd insert
|
||||
// checks that NS1 and NS1.NS2 are objects, unnecessarily.
|
||||
int startFrom = 0;
|
||||
bool first = true;
|
||||
if (!String.IsNullOrEmpty(namespacePart)) {
|
||||
int nsIndex = typeName.IndexOf(namespacePart + ".", StringComparison.Ordinal);
|
||||
// in wcf services, the typeName starts with the namespace,
|
||||
// in asmx services, it doesnt.
|
||||
if (nsIndex > -1) {
|
||||
startFrom = nsIndex + namespacePart.Length + 1;
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
int dotIndex = typeName.IndexOf('.', startFrom);
|
||||
while (dotIndex > -1) {
|
||||
string fullPath = typeName.Substring(0, dotIndex);
|
||||
if (!_registeredNamespaces.Contains(fullPath) && !_ensuredObjectParts.Contains(fullPath)) {
|
||||
_ensuredObjectParts[fullPath] = null;
|
||||
_builder.Append("if (typeof(" + fullPath + ") === \"undefined\") {\r\n ");
|
||||
if (first) {
|
||||
// var foo = {};
|
||||
_builder.Append("var ");
|
||||
first = false;
|
||||
}
|
||||
// foo.bar = {};
|
||||
_builder.Append(fullPath + " = {};\r\n}\r\n");
|
||||
}
|
||||
dotIndex = typeName.IndexOf('.', dotIndex + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="GenerateScriptTypeAttribute.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Web;
|
||||
|
||||
[
|
||||
AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = true)
|
||||
]
|
||||
public sealed class GenerateScriptTypeAttribute : Attribute {
|
||||
|
||||
// Constructors
|
||||
public GenerateScriptTypeAttribute(Type type) {
|
||||
if (type == null) {
|
||||
throw new ArgumentNullException("type");
|
||||
}
|
||||
_type = type;
|
||||
}
|
||||
|
||||
// Instance Properties
|
||||
private Type _type;
|
||||
[SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
|
||||
Justification = "Distinguishable from Object.GetType()")]
|
||||
public Type Type {
|
||||
get {
|
||||
return _type;
|
||||
}
|
||||
}
|
||||
|
||||
private string _typeId;
|
||||
public string ScriptTypeId {
|
||||
get {
|
||||
return _typeId ?? String.Empty;
|
||||
}
|
||||
set {
|
||||
_typeId = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="PageClientProxyGenerator.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System.Web;
|
||||
using System.Web.UI;
|
||||
|
||||
internal class PageClientProxyGenerator : ClientProxyGenerator {
|
||||
private string _path;
|
||||
|
||||
internal PageClientProxyGenerator(IPage page, bool debug)
|
||||
: this(VirtualPathUtility.MakeRelative(page.Request.Path, page.Request.FilePath), debug) {
|
||||
// Dev10 Bug 597146: Use VirtualPathUtility to build a relative path from the path to the file.
|
||||
// Previously just Page.Request.FilePath was used, which was for example, /app/foo/page.aspx,
|
||||
// but this breaks with cookieless sessions since the url is /app/foo/(sessionid)/page.aspx.
|
||||
// We need to make a relative path from page.Request.Path (e.g. /app/foo) to page.Request.FilePath
|
||||
// (e.g. /app/foo/page.aspx) rather than just strip off 'page.aspx' with Path.GetFileName, because
|
||||
// the url may include PathInfo, such as "/app/foo/page.aspx/pathinfo1/pathinfo2", and in that case
|
||||
// we need the path to be ../../page.aspx
|
||||
}
|
||||
|
||||
internal PageClientProxyGenerator(string path, bool debug) {
|
||||
_path = path;
|
||||
_debugMode = debug;
|
||||
}
|
||||
|
||||
internal static string GetClientProxyScript(HttpContext context, IPage page, bool debug) {
|
||||
// Do nothing during unit tests which have no context or page
|
||||
if (context == null || page == null) return null;
|
||||
|
||||
WebServiceData webServiceData = WebServiceData.GetWebServiceData(context,
|
||||
page.AppRelativeVirtualPath,
|
||||
false /*failIfNoData*/,
|
||||
true /*pageMethods */);
|
||||
if (webServiceData == null)
|
||||
return null;
|
||||
|
||||
PageClientProxyGenerator proxyGenerator = new PageClientProxyGenerator(page, debug);
|
||||
return proxyGenerator.GetClientProxyScript(webServiceData);
|
||||
}
|
||||
|
||||
protected override void GenerateTypeDeclaration(WebServiceData webServiceData, bool genClass) {
|
||||
if (genClass) {
|
||||
_builder.Append("PageMethods.prototype = ");
|
||||
}
|
||||
else {
|
||||
_builder.Append("var PageMethods = ");
|
||||
}
|
||||
}
|
||||
|
||||
protected override string GetProxyTypeName(WebServiceData data) {
|
||||
return "PageMethods";
|
||||
}
|
||||
|
||||
protected override string GetProxyPath() {
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ProxyGenerator.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.ServiceModel;
|
||||
using System.ServiceModel.Description;
|
||||
using System.Web.Resources;
|
||||
|
||||
public static class ProxyGenerator {
|
||||
|
||||
const string WCFProxyTypeName = "System.ServiceModel.Description.WCFServiceClientProxyGenerator";
|
||||
const string WCFProxyMethodName = "GetClientProxyScript";
|
||||
|
||||
public static string GetClientProxyScript(Type type, string path, bool debug) {
|
||||
return GetClientProxyScript(type, path, debug, null);
|
||||
}
|
||||
|
||||
public static string GetClientProxyScript(Type type, string path, bool debug, ServiceEndpoint serviceEndpoint) {
|
||||
if (type == null) {
|
||||
throw new ArgumentNullException("type");
|
||||
}
|
||||
if (path == null) {
|
||||
throw new ArgumentNullException("path");
|
||||
}
|
||||
WebServiceData webServiceData = null;
|
||||
ClientProxyGenerator proxyGenerator = null;
|
||||
if (IsWebServiceType(type)) {
|
||||
proxyGenerator = new WebServiceClientProxyGenerator(path, debug);
|
||||
webServiceData = new WebServiceData(type, false);
|
||||
}
|
||||
else if (IsPageType(type)) {
|
||||
proxyGenerator = new PageClientProxyGenerator(path, debug);
|
||||
webServiceData = new WebServiceData(type, true);
|
||||
}
|
||||
else if(IsWCFServiceType(type)) {
|
||||
// invoke the WCFServiceClientProxyGenerator.GetClientProxyScript method using reflection
|
||||
Assembly wcfWebAssembly = Assembly.Load(AssemblyRef.SystemServiceModelWeb);
|
||||
if (wcfWebAssembly != null) {
|
||||
Type wcfProxyType = wcfWebAssembly.GetType(WCFProxyTypeName);
|
||||
if (wcfProxyType != null) {
|
||||
MethodInfo getClientProxyMethod = wcfProxyType.GetMethod(WCFProxyMethodName, BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (getClientProxyMethod != null) {
|
||||
return getClientProxyMethod.Invoke(null, new object[] { type, path, debug, serviceEndpoint }) as string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// in case the reflection fails, we should throw unsupported exception
|
||||
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
|
||||
AtlasWeb.ProxyGenerator_UnsupportedType,
|
||||
type.FullName));
|
||||
}
|
||||
else {
|
||||
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
|
||||
AtlasWeb.ProxyGenerator_UnsupportedType,
|
||||
type.FullName));
|
||||
}
|
||||
return proxyGenerator.GetClientProxyScript(webServiceData);
|
||||
}
|
||||
|
||||
private static bool IsPageType(Type type) {
|
||||
return typeof(System.Web.UI.Page).IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
private static bool IsWCFServiceType(Type type) {
|
||||
object[] attribs = type.GetCustomAttributes(typeof(ServiceContractAttribute), true);
|
||||
return (attribs.Length != 0);
|
||||
}
|
||||
|
||||
private static bool IsWebServiceType(Type type) {
|
||||
object[] attribs = type.GetCustomAttributes(typeof(ScriptServiceAttribute), true);
|
||||
return (attribs.Length != 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ResponseFormat.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
|
||||
public enum ResponseFormat {
|
||||
Json = 0,
|
||||
Xml = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="RestClientProxyHandler.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
internal class RestClientProxyHandler : IHttpHandler {
|
||||
public void ProcessRequest(HttpContext context) {
|
||||
if (context == null) {
|
||||
throw new ArgumentNullException("context");
|
||||
}
|
||||
|
||||
string clientProxyString = WebServiceClientProxyGenerator.GetClientProxyScript(context);
|
||||
if (clientProxyString != null) {
|
||||
context.Response.ContentType = "application/x-javascript";
|
||||
context.Response.Write(clientProxyString);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReusable {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="RestHandler.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Resources;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Web.SessionState;
|
||||
|
||||
internal class RestHandler : IHttpHandler {
|
||||
private WebServiceMethodData _webServiceMethodData;
|
||||
|
||||
internal static IHttpHandler CreateHandler(HttpContext context) {
|
||||
// Expectation is that we got a PathInfo of form /MethodName
|
||||
if (context.Request.PathInfo.Length < 2 || context.Request.PathInfo[0] != '/') {
|
||||
throw new InvalidOperationException(AtlasWeb.WebService_InvalidWebServiceCall);
|
||||
}
|
||||
|
||||
// Get the data about the web service being invoked
|
||||
WebServiceData webServiceData = WebServiceData.GetWebServiceData(context, context.Request.FilePath);
|
||||
string methodName = context.Request.PathInfo.Substring(1);
|
||||
return CreateHandler(webServiceData, methodName);
|
||||
}
|
||||
|
||||
private static IHttpHandler CreateHandler(WebServiceData webServiceData, string methodName) {
|
||||
|
||||
// Get the data about the method being called
|
||||
WebServiceMethodData methodData = webServiceData.GetMethodData(methodName);
|
||||
|
||||
// Create the proper handler, depending on whether we need session state
|
||||
RestHandler handler;
|
||||
if (methodData.RequiresSession)
|
||||
handler = new RestHandlerWithSession();
|
||||
else
|
||||
handler = new RestHandler();
|
||||
|
||||
// Save the method data in the handler
|
||||
handler._webServiceMethodData = methodData;
|
||||
return handler;
|
||||
}
|
||||
|
||||
// This is very similar to WebService caching, the differences are
|
||||
// 1) Here we explicitely SetValidUntilExpires(true) because in an XmlHttp there is
|
||||
// "pragma:no-cache" in header which would result in cache miss on the server.
|
||||
// 2) Here we don't vary on header "Content-type" or "SOAPAction" because the former
|
||||
// is specific to soap 1.2, which puts action in the content-type param; and the
|
||||
// later is used by soap calls.
|
||||
private static void InitializeCachePolicy(WebServiceMethodData methodData, HttpContext context) {
|
||||
int cacheDuration = methodData.CacheDuration;
|
||||
if (cacheDuration > 0) {
|
||||
context.Response.Cache.SetCacheability(HttpCacheability.Server);
|
||||
context.Response.Cache.SetExpires(DateTime.Now.AddSeconds(cacheDuration));
|
||||
context.Response.Cache.SetSlidingExpiration(false);
|
||||
context.Response.Cache.SetValidUntilExpires(true);
|
||||
|
||||
// DevDiv 23596: Don't set VaryBy* if the method takes no parameters
|
||||
if (methodData.ParameterDatas.Count > 0) {
|
||||
context.Response.Cache.VaryByParams["*"] = true;
|
||||
}
|
||||
else {
|
||||
context.Response.Cache.VaryByParams.IgnoreParams = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
context.Response.Cache.SetNoServerCaching();
|
||||
context.Response.Cache.SetMaxAge(TimeSpan.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> GetRawParamsFromGetRequest(HttpContext context, JavaScriptSerializer serializer, WebServiceMethodData methodData) {
|
||||
// Get all the parameters from the query string
|
||||
NameValueCollection queryString = context.Request.QueryString;
|
||||
Dictionary<string, object> rawParams = new Dictionary<string, object>();
|
||||
foreach (WebServiceParameterData param in methodData.ParameterDatas) {
|
||||
string name = param.ParameterInfo.Name;
|
||||
string val = queryString[name];
|
||||
if (val != null) {
|
||||
rawParams.Add(name, serializer.DeserializeObject(val));
|
||||
}
|
||||
}
|
||||
return rawParams;
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> GetRawParamsFromPostRequest(HttpContext context, JavaScriptSerializer serializer) {
|
||||
// Read the entire body as a string
|
||||
TextReader reader = new StreamReader(context.Request.InputStream);
|
||||
string bodyString = reader.ReadToEnd();
|
||||
|
||||
// If there is no body, treat it as an empty object
|
||||
if (String.IsNullOrEmpty(bodyString)) {
|
||||
return new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
// Deserialize the javascript request body
|
||||
return serializer.Deserialize<IDictionary<string, object>>(bodyString);
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> GetRawParams(WebServiceMethodData methodData, HttpContext context) {
|
||||
if (methodData.UseGet) {
|
||||
if (context.Request.HttpMethod == "GET") {
|
||||
return GetRawParamsFromGetRequest(context, methodData.Owner.Serializer, methodData);
|
||||
}
|
||||
else {
|
||||
throw new InvalidOperationException(
|
||||
String.Format(CultureInfo.CurrentCulture, AtlasWeb.WebService_InvalidVerbRequest,
|
||||
methodData.MethodName, "POST"));
|
||||
}
|
||||
}
|
||||
else if (context.Request.HttpMethod == "POST") {
|
||||
return GetRawParamsFromPostRequest(context, methodData.Owner.Serializer);
|
||||
} else {
|
||||
throw new InvalidOperationException(
|
||||
String.Format(CultureInfo.CurrentCulture, AtlasWeb.WebService_InvalidVerbRequest,
|
||||
methodData.MethodName, "GET"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void InvokeMethod(HttpContext context, WebServiceMethodData methodData, IDictionary<string, object> rawParams) {
|
||||
// Initialize HttpCachePolicy
|
||||
InitializeCachePolicy(methodData, context);
|
||||
|
||||
// Create an new instance of the class
|
||||
object target = null;
|
||||
if (!methodData.IsStatic) target = Activator.CreateInstance(methodData.Owner.TypeData.Type);
|
||||
|
||||
// Make the actual method call on it
|
||||
object retVal = methodData.CallMethodFromRawParams(target, rawParams);
|
||||
|
||||
string contentType;
|
||||
string responseString = null;
|
||||
if (methodData.UseXmlResponse) {
|
||||
responseString = retVal as string;
|
||||
|
||||
// If it's a string, output it as is unless XmlSerializeString is set
|
||||
if (responseString == null || methodData.XmlSerializeString) {
|
||||
// Use the Xml Serializer
|
||||
try {
|
||||
responseString = ServicesUtilities.XmlSerializeObjectToString(retVal);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Throw a better error if Xml serialization fails
|
||||
throw new InvalidOperationException(
|
||||
String.Format(CultureInfo.CurrentCulture, AtlasWeb.WebService_InvalidXmlReturnType,
|
||||
methodData.MethodName, retVal.GetType().FullName, e.Message));
|
||||
}
|
||||
}
|
||||
|
||||
contentType = "text/xml";
|
||||
}
|
||||
else {
|
||||
|
||||
// Convert the result to a JSON string
|
||||
// DevDiv 88409:Change JSON wire format to prevent CSRF attack
|
||||
// We wrap the returned value inside an object , and assign the returned value
|
||||
// to member "d" of the object. We do so as JSOM for object will never be parsed
|
||||
// as valid Javascript , unlike arrays.
|
||||
responseString =@"{""d"":" + methodData.Owner.Serializer.Serialize(retVal) + "}";
|
||||
contentType = "application/json";
|
||||
}
|
||||
|
||||
// Set the response content-type
|
||||
context.Response.ContentType = contentType;
|
||||
|
||||
// Write the string to the response
|
||||
if (responseString != null)
|
||||
context.Response.Write(responseString);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
|
||||
Justification="All exceptions need to be reported to the client")]
|
||||
[SuppressMessage("Microsoft.Security", "CA2107:ReviewDenyAndPermitOnlyUsage",
|
||||
Justification = "Fix for DevDiv 39162 GAC'd non-APTCA types can instantiate in networking stack in Medium trust")]
|
||||
internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData) {
|
||||
try {
|
||||
NamedPermissionSet s_permissionSet = HttpRuntime.NamedPermissionSet;
|
||||
if (s_permissionSet != null) {
|
||||
s_permissionSet.PermitOnly();
|
||||
}
|
||||
|
||||
// Deserialize the javascript request body
|
||||
IDictionary<string, object> rawParams = GetRawParams(methodData, context);
|
||||
InvokeMethod(context, methodData, rawParams);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
WriteExceptionJsonString(context, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static object BuildWebServiceError(string msg, string stack, string type) {
|
||||
var result = new OrderedDictionary();
|
||||
result["Message"] = msg;
|
||||
result["StackTrace"] = stack;
|
||||
result["ExceptionType"] = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static void WriteExceptionJsonString(HttpContext context, Exception ex) {
|
||||
WriteExceptionJsonString(context, ex, (int)HttpStatusCode.InternalServerError);
|
||||
}
|
||||
|
||||
internal static void WriteExceptionJsonString(HttpContext context, Exception ex, int statusCode) {
|
||||
// Record the charset before we call ClearHeaders(). (DevDiv Bugs 158401)
|
||||
string charset = context.Response.Charset;
|
||||
context.Response.ClearHeaders();
|
||||
context.Response.ClearContent();
|
||||
context.Response.Clear();
|
||||
context.Response.StatusCode = statusCode;
|
||||
context.Response.StatusDescription = HttpWorkerRequest.GetStatusDescription(statusCode);
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.AddHeader("jsonerror", "true");
|
||||
// Maintain the Charset from before. (DevDiv Bugs 158401)
|
||||
context.Response.Charset = charset;
|
||||
//Devdiv Bug: 118619:When accessed remotely, an Ajax web service that throws an error doesn't return the error string in the proper format on IIS7
|
||||
//For IIS 7.0 integrated mode we need to set TrySkipIisCustomErrors to override IIS custom error handling. This has no functional/perf impact on
|
||||
//IIS 7.0 classic mode or earlier versions.
|
||||
context.Response.TrySkipIisCustomErrors = true;
|
||||
using (StreamWriter writer = new StreamWriter(context.Response.OutputStream, new UTF8Encoding(false))) {
|
||||
if (ex is TargetInvocationException) {
|
||||
ex = ex.InnerException;
|
||||
}
|
||||
|
||||
// Don't show any error stack or sensitive info when custom error is enabled.
|
||||
if (context.IsCustomErrorEnabled) {
|
||||
writer.Write(JavaScriptSerializer.SerializeInternal(BuildWebServiceError(AtlasWeb.WebService_Error, String.Empty, String.Empty)));
|
||||
}
|
||||
else {
|
||||
writer.Write(JavaScriptSerializer.SerializeInternal(BuildWebServiceError(ex.Message, ex.StackTrace, ex.GetType().FullName)));
|
||||
}
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessRequest(HttpContext context) {
|
||||
ExecuteWebServiceCall(context, _webServiceMethodData);
|
||||
}
|
||||
|
||||
public bool IsReusable {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same handler, but implementing IRequiresSessionState to allow session state use
|
||||
internal class RestHandlerWithSession: RestHandler, IRequiresSessionState {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="RestHandlerFactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
|
||||
internal class RestHandlerFactory : IHttpHandlerFactory {
|
||||
internal const string ClientProxyRequestPathInfo = "/js";
|
||||
internal const string ClientDebugProxyRequestPathInfo = "/jsdebug";
|
||||
|
||||
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) {
|
||||
if (context == null) {
|
||||
throw new ArgumentNullException("context");
|
||||
}
|
||||
|
||||
if (IsClientProxyRequest(context.Request.PathInfo)) {
|
||||
// It's a request for client side proxies
|
||||
return new RestClientProxyHandler();
|
||||
}
|
||||
else {
|
||||
// The request is an actual call to a server method
|
||||
return RestHandler.CreateHandler(context);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ReleaseHandler(IHttpHandler handler) {
|
||||
}
|
||||
|
||||
// Detects if this is a request we want to intercept, i.e. invocation or proxy request
|
||||
internal static bool IsRestRequest(HttpContext context) {
|
||||
return IsRestMethodCall(context.Request) || IsClientProxyRequest(context.Request.PathInfo);
|
||||
}
|
||||
|
||||
// Detects if this is a method invocation, i.e. webservice call or page method call
|
||||
internal static bool IsRestMethodCall(HttpRequest request) {
|
||||
return
|
||||
!String.IsNullOrEmpty(request.PathInfo) &&
|
||||
(request.ContentType.StartsWith("application/json;", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(request.ContentType, "application/json", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
internal static bool IsClientProxyDebugRequest(string pathInfo) {
|
||||
return string.Equals(pathInfo, ClientDebugProxyRequestPathInfo, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
internal static bool IsClientProxyRequest(string pathInfo) {
|
||||
return (string.Equals(pathInfo, ClientProxyRequestPathInfo, StringComparison.OrdinalIgnoreCase) ||
|
||||
IsClientProxyDebugRequest(pathInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ScriptHandlerFactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Web.SessionState;
|
||||
using System.Security.Permissions;
|
||||
using System.Security;
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
internal class ScriptHandlerFactory : IHttpHandlerFactory {
|
||||
IHttpHandlerFactory _restHandlerFactory;
|
||||
IHttpHandlerFactory _webServiceHandlerFactory;
|
||||
|
||||
internal class HandlerWrapper : IHttpHandler {
|
||||
protected IHttpHandler _originalHandler;
|
||||
private IHttpHandlerFactory _originalFactory;
|
||||
|
||||
internal HandlerWrapper(IHttpHandler originalHandler, IHttpHandlerFactory originalFactory) {
|
||||
_originalFactory = originalFactory;
|
||||
_originalHandler = originalHandler;
|
||||
}
|
||||
|
||||
internal void ReleaseHandler() {
|
||||
_originalFactory.ReleaseHandler(_originalHandler);
|
||||
}
|
||||
|
||||
public bool IsReusable {
|
||||
get {
|
||||
return _originalHandler.IsReusable;
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessRequest(HttpContext context) {
|
||||
_originalHandler.ProcessRequest(context);
|
||||
}
|
||||
}
|
||||
|
||||
internal class HandlerWrapperWithSession : HandlerWrapper, IRequiresSessionState {
|
||||
internal HandlerWrapperWithSession(IHttpHandler originalHandler, IHttpHandlerFactory originalFactory)
|
||||
: base(originalHandler, originalFactory) {}
|
||||
}
|
||||
|
||||
private class AsyncHandlerWrapper : HandlerWrapper, IHttpAsyncHandler {
|
||||
internal AsyncHandlerWrapper(IHttpHandler originalHandler, IHttpHandlerFactory originalFactory)
|
||||
:
|
||||
base(originalHandler, originalFactory) { }
|
||||
|
||||
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) {
|
||||
return ((IHttpAsyncHandler)_originalHandler).BeginProcessRequest(context, cb, extraData);
|
||||
}
|
||||
|
||||
public void EndProcessRequest(IAsyncResult result) {
|
||||
((IHttpAsyncHandler)_originalHandler).EndProcessRequest(result);
|
||||
}
|
||||
}
|
||||
|
||||
private class AsyncHandlerWrapperWithSession : AsyncHandlerWrapper, IRequiresSessionState {
|
||||
internal AsyncHandlerWrapperWithSession(IHttpHandler originalHandler, IHttpHandlerFactory originalFactory)
|
||||
: base(originalHandler, originalFactory) { }
|
||||
}
|
||||
|
||||
public ScriptHandlerFactory() {
|
||||
_restHandlerFactory = new RestHandlerFactory();
|
||||
_webServiceHandlerFactory = new System.Web.Services.Protocols.WebServiceHandlerFactory();
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) {
|
||||
IHttpHandler handler;
|
||||
IHttpHandlerFactory factory;
|
||||
if (RestHandlerFactory.IsRestRequest(context)) {
|
||||
// It's a REST request
|
||||
factory = _restHandlerFactory;
|
||||
}
|
||||
else {
|
||||
// It's a regular asmx web request, so delegate to the WebServiceHandlerFactory
|
||||
factory = _webServiceHandlerFactory;
|
||||
}
|
||||
|
||||
handler = factory.GetHandler(context, requestType, url, pathTranslated);
|
||||
|
||||
bool requiresSession = handler is IRequiresSessionState;
|
||||
|
||||
if (handler is IHttpAsyncHandler) {
|
||||
if (requiresSession)
|
||||
return new AsyncHandlerWrapperWithSession(handler, factory);
|
||||
else
|
||||
return new AsyncHandlerWrapper(handler, factory);
|
||||
}
|
||||
|
||||
if (requiresSession)
|
||||
return new HandlerWrapperWithSession(handler, factory);
|
||||
else
|
||||
return new HandlerWrapper(handler, factory);
|
||||
}
|
||||
|
||||
public virtual void ReleaseHandler(IHttpHandler handler) {
|
||||
if (handler == null) {
|
||||
throw new ArgumentNullException("handler");
|
||||
}
|
||||
((HandlerWrapper)handler).ReleaseHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ScriptMethodAttribute.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
[
|
||||
AttributeUsage(AttributeTargets.Method)
|
||||
]
|
||||
public sealed class ScriptMethodAttribute : Attribute {
|
||||
private ResponseFormat _responseFormat;
|
||||
private bool _useHttpGet;
|
||||
private bool _xmlSerializeString;
|
||||
|
||||
public ResponseFormat ResponseFormat {
|
||||
get {
|
||||
return _responseFormat;
|
||||
}
|
||||
set {
|
||||
_responseFormat = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseHttpGet {
|
||||
get {
|
||||
return _useHttpGet;
|
||||
}
|
||||
set {
|
||||
_useHttpGet = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool XmlSerializeString {
|
||||
get {
|
||||
return _xmlSerializeString;
|
||||
}
|
||||
set {
|
||||
_xmlSerializeString = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ScriptServiceAttribute.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
using System.Web;
|
||||
|
||||
[
|
||||
AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)
|
||||
]
|
||||
public sealed class ScriptServiceAttribute : Attribute {
|
||||
public ScriptServiceAttribute() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ServicesUtilities.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
internal static class ServicesUtilities {
|
||||
|
||||
internal static string GetClientTypeName(string name) {
|
||||
// e.g. MyNS.MySubNS.MyWebService OR var MyWebService
|
||||
return name.Replace('+', '_');
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA2301:EmbeddableTypesInContainersRule", MessageId = "System.Collections.Generic.Dictionary`2<System.Type,System.String>", Justification = "This is used by ASP.Net web services which is a legacy technology.")]
|
||||
internal static string GetClientTypeFromServerType(WebServiceData webServiceData, Type type)
|
||||
{
|
||||
// For intellisense purposes, returns a best estimate of what the appropriate client-side type is for a given server type.
|
||||
// Takes generated client proxies and enum proxies into consideration.
|
||||
// The rest is a best guess.
|
||||
// If all else fails we use "", to indicate "any" client side type. "Object" is not the same as any type on the client since
|
||||
// string, for example, is not considered an object. "Object" is equiv to a .net dictionary.
|
||||
|
||||
|
||||
|
||||
if (webServiceData.ClientTypeNameDictionary.ContainsKey(type)) {
|
||||
// if it exists in the client type dictionary, it will have a proxy generated for it
|
||||
//get the client based on type.FullName for ASMX, and schema qualified name and namespace for WCF
|
||||
return webServiceData.ClientTypeNameDictionary[type];
|
||||
}
|
||||
|
||||
if (type.IsEnum) {
|
||||
// there will be a proxy for this enum
|
||||
return GetClientTypeName(type.FullName);
|
||||
}
|
||||
|
||||
// there is no client proxy for it, so it either maps to a built-in js type or it could be "anything"
|
||||
|
||||
// take care of the most common types
|
||||
if (type == typeof(string) || type == typeof(char)) {
|
||||
return "String";
|
||||
}
|
||||
else if (type.IsPrimitive) {
|
||||
// The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64 (long), UInt64, IntPtr, Char, Double, and Single (float).
|
||||
if (type == typeof(bool)) {
|
||||
// bool is the only primitive we shouldnt treat as a number
|
||||
return "Boolean";
|
||||
}
|
||||
else {
|
||||
// takes care of all ints, float, double, but not decimal since it isnt a primitive
|
||||
// we also consider byte, sbyte, and intptr to be numbers
|
||||
return "Number";
|
||||
}
|
||||
}
|
||||
|
||||
if (type.IsValueType) {
|
||||
if (type == typeof(DateTime)) {
|
||||
return "Date";
|
||||
}
|
||||
else if (type == typeof(Guid)) {
|
||||
return "String";
|
||||
}
|
||||
else if (type == typeof(Decimal)) {
|
||||
return "Number";
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(IDictionary).IsAssignableFrom(type)) {
|
||||
return "Object";
|
||||
}
|
||||
// might still be IDictionary<K,T>
|
||||
if (type.IsGenericType) {
|
||||
Type gtd = type;
|
||||
if (!type.IsGenericTypeDefinition) {
|
||||
gtd = type.GetGenericTypeDefinition();
|
||||
}
|
||||
if (gtd == typeof(IDictionary<,>)) {
|
||||
return "Object";
|
||||
}
|
||||
}
|
||||
|
||||
if (type.IsArray || typeof(IEnumerable).IsAssignableFrom(type)) {
|
||||
return "Array";
|
||||
}
|
||||
|
||||
// dont know what it is (e.g., TimeSpan), or it is type Object, so allow any client type.
|
||||
return "";
|
||||
}
|
||||
|
||||
internal static Type UnwrapNullableType(Type type) {
|
||||
// check for nullable<t> and pull out <t>
|
||||
if (type.IsGenericType && !type.IsGenericTypeDefinition) {
|
||||
Type genericTypeDefinition = type.GetGenericTypeDefinition();
|
||||
if (genericTypeDefinition == typeof(Nullable<>)) {
|
||||
return type.GetGenericArguments()[0];
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
// Serialize an object to an XML string
|
||||
internal static string XmlSerializeObjectToString(object obj) {
|
||||
//
|
||||
XmlSerializer xs = new XmlSerializer(obj.GetType());
|
||||
MemoryStream ms = new MemoryStream();
|
||||
using (XmlTextWriter writer = new XmlTextWriter(ms, Encoding.UTF8)) {
|
||||
xs.Serialize(writer, obj);
|
||||
ms.Position = 0;
|
||||
using (StreamReader reader = new StreamReader(ms)) {
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebServiceClientProxyGenerator.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Web;
|
||||
using System.Security.Permissions;
|
||||
using System.Security;
|
||||
|
||||
internal class WebServiceClientProxyGenerator : ClientProxyGenerator {
|
||||
private string _path;
|
||||
|
||||
// Called by ScriptManager to generate the proxy inline
|
||||
internal static string GetInlineClientProxyScript(string path, HttpContext context, bool debug) {
|
||||
WebServiceData webServiceData = WebServiceData.GetWebServiceData(context, path, true, false, true);
|
||||
WebServiceClientProxyGenerator proxyGenerator = new WebServiceClientProxyGenerator(path, debug);
|
||||
return proxyGenerator.GetClientProxyScript(webServiceData);
|
||||
}
|
||||
|
||||
private static DateTime GetAssemblyModifiedTime(Assembly assembly) {
|
||||
AssemblyName assemblyName = assembly.GetName();
|
||||
DateTime writeTime = File.GetLastWriteTime(new Uri(assemblyName.CodeBase).LocalPath);
|
||||
// DevDiv 52056: include writeTime.Second in the date, otherwise if you modify it within the same minute it we'd still respond with http status 304 not modified
|
||||
return new DateTime(writeTime.Year, writeTime.Month, writeTime.Day, writeTime.Hour, writeTime.Minute, writeTime.Second);
|
||||
}
|
||||
|
||||
// This is called thru the RestClientProxyHandler
|
||||
[SecuritySafeCritical]
|
||||
[FileIOPermission(SecurityAction.Assert, Unrestricted = true)]
|
||||
internal static string GetClientProxyScript(HttpContext context) {
|
||||
WebServiceData webServiceData = WebServiceData.GetWebServiceData(context, context.Request.FilePath);
|
||||
DateTime lastModifiedDate = GetAssemblyModifiedTime(webServiceData.TypeData.Type.Assembly);
|
||||
|
||||
// If the browser sent this header, we can check if we need to resend
|
||||
string modifiedSince = context.Request.Headers["If-Modified-Since"];
|
||||
if (modifiedSince != null) {
|
||||
DateTime header;
|
||||
if (DateTime.TryParse(modifiedSince, out header)) {
|
||||
// We are done if the assembly hasn't been modified
|
||||
if (header >= lastModifiedDate) {
|
||||
context.Response.StatusCode = 304;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool debug = RestHandlerFactory.IsClientProxyDebugRequest(context.Request.PathInfo);
|
||||
// Only cache for release proxy script (/js)
|
||||
if (!debug) {
|
||||
// Only cache if we get a reasonable last modified date
|
||||
if (lastModifiedDate.ToUniversalTime() < DateTime.UtcNow) {
|
||||
// Cache the resource so we don't keep processing the same requests
|
||||
HttpCachePolicy cachePolicy = context.Response.Cache;
|
||||
cachePolicy.SetCacheability(HttpCacheability.Public);
|
||||
cachePolicy.SetLastModified(lastModifiedDate);
|
||||
// expires is necessary so that the browser at least does an If-Modified-Since request on every request.
|
||||
// without that, the browser wouldn't request a new proxy until the user hits refresh.
|
||||
// Use one year ago to reasonably ensure "past" interpretation
|
||||
cachePolicy.SetExpires(lastModifiedDate.AddYears(-1));
|
||||
}
|
||||
}
|
||||
|
||||
WebServiceClientProxyGenerator proxyGenerator = new WebServiceClientProxyGenerator(context.Request.FilePath, debug);
|
||||
return proxyGenerator.GetClientProxyScript(webServiceData);
|
||||
}
|
||||
|
||||
internal WebServiceClientProxyGenerator(string path, bool debug) {
|
||||
// internal because ExtensionsTest needs this path to bypass httpcontext
|
||||
_path = path;
|
||||
_debugMode = debug;
|
||||
}
|
||||
|
||||
protected override string GetProxyPath() {
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,83 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebServiceEnumData.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
|
||||
internal class WebServiceEnumData : WebServiceTypeData {
|
||||
bool isULong;
|
||||
string[] names;
|
||||
long[] values;
|
||||
|
||||
internal WebServiceEnumData(string typeName, string typeNamespace, string[] names, long[] values, bool isULong)
|
||||
: base(typeName, typeNamespace) {
|
||||
InitWebServiceEnumData(names, values, isULong);
|
||||
}
|
||||
|
||||
internal WebServiceEnumData(string typeName, string typeNamespace, Type t, string[] names, long[] values, bool isULong)
|
||||
: base(typeName, typeNamespace, t) {
|
||||
InitWebServiceEnumData(names, values, isULong);
|
||||
}
|
||||
|
||||
internal WebServiceEnumData(string typeName, string typeNamespace, string[] names, Array values, bool isULong)
|
||||
: base(typeName, typeNamespace) {
|
||||
InitWebServiceEnumData(names, values, isULong);
|
||||
}
|
||||
|
||||
internal WebServiceEnumData(string typeName, string typeNamespace, Type t, string[] names, Array values, bool isULong)
|
||||
: base(typeName, typeNamespace) {
|
||||
InitWebServiceEnumData(names, values, isULong);
|
||||
}
|
||||
|
||||
internal bool IsULong {
|
||||
get {
|
||||
return isULong;
|
||||
}
|
||||
}
|
||||
|
||||
internal string[] Names {
|
||||
get {
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
internal long[] Values {
|
||||
get {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitWebServiceEnumData(string[] names, long[] values, bool isULong) {
|
||||
System.Diagnostics.Debug.Assert(names != null);
|
||||
System.Diagnostics.Debug.Assert(values != null);
|
||||
System.Diagnostics.Debug.Assert(names.Length == values.Length);
|
||||
this.names = names;
|
||||
this.values = values;
|
||||
this.isULong = isULong;
|
||||
}
|
||||
|
||||
private void InitWebServiceEnumData(string[] names, Array values, bool isULong) {
|
||||
System.Diagnostics.Debug.Assert(names != null);
|
||||
System.Diagnostics.Debug.Assert(values != null);
|
||||
System.Diagnostics.Debug.Assert(names.Length == values.Length);
|
||||
this.names = names;
|
||||
this.values = new long[values.Length];
|
||||
for (int i = 0; i < values.Length; i++) {
|
||||
object enumValue = values.GetValue(i);
|
||||
if (isULong) {
|
||||
this.values[i] = (long)((IConvertible)enumValue).ToUInt64(CultureInfo.InvariantCulture);
|
||||
}
|
||||
else {
|
||||
this.values[i] = ((IConvertible)enumValue).ToInt64(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
this.isULong = isULong;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebServiceMethodData.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Web.Resources;
|
||||
using System.Web.Script.Serialization;
|
||||
using System.Web.Services;
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
|
||||
internal class WebServiceMethodData {
|
||||
private MethodInfo _methodInfo;
|
||||
private WebMethodAttribute _webMethodAttribute;
|
||||
private ScriptMethodAttribute _scriptMethodAttribute;
|
||||
private string _methodName;
|
||||
private Dictionary<string, WebServiceParameterData> _parameterData;
|
||||
private WebServiceData _owner;
|
||||
private bool? _useHttpGet;
|
||||
|
||||
internal WebServiceMethodData(WebServiceData owner, MethodInfo methodInfo, WebMethodAttribute webMethodAttribute, ScriptMethodAttribute scriptMethodAttribute) {
|
||||
_owner = owner;
|
||||
_methodInfo = methodInfo;
|
||||
_webMethodAttribute = webMethodAttribute;
|
||||
_methodName = _webMethodAttribute.MessageName;
|
||||
_scriptMethodAttribute = scriptMethodAttribute;
|
||||
if (String.IsNullOrEmpty(_methodName)) {
|
||||
_methodName = methodInfo.Name;
|
||||
}
|
||||
}
|
||||
|
||||
// This constructor is only used by WCF. Owener, MethodName, ParameterDataDictionary, ParamterData
|
||||
// are the only valid properties in WCF case.
|
||||
internal WebServiceMethodData(WebServiceData owner, string methodName, Dictionary<string, WebServiceParameterData> parameterData, bool useHttpGet) {
|
||||
_owner = owner;
|
||||
_methodName = methodName;
|
||||
_parameterData = parameterData;
|
||||
_useHttpGet = useHttpGet;
|
||||
}
|
||||
|
||||
internal WebServiceData Owner {
|
||||
get {
|
||||
return _owner;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureParameters() {
|
||||
|
||||
// Build the parameters collection on demand
|
||||
if (_parameterData != null)
|
||||
return;
|
||||
|
||||
lock (this) {
|
||||
Dictionary<string, WebServiceParameterData> parameterData = new Dictionary<string, WebServiceParameterData>();
|
||||
int index = 0;
|
||||
foreach (ParameterInfo param in _methodInfo.GetParameters()) {
|
||||
parameterData[param.Name] = new WebServiceParameterData(param, index);
|
||||
index++;
|
||||
}
|
||||
_parameterData = parameterData;
|
||||
}
|
||||
}
|
||||
|
||||
internal string MethodName {
|
||||
get {
|
||||
return _methodName;
|
||||
}
|
||||
}
|
||||
|
||||
internal MethodInfo MethodInfo {
|
||||
get {
|
||||
return _methodInfo;
|
||||
}
|
||||
}
|
||||
|
||||
internal IDictionary<string, WebServiceParameterData> ParameterDataDictionary {
|
||||
get {
|
||||
EnsureParameters();
|
||||
return _parameterData;
|
||||
}
|
||||
}
|
||||
|
||||
internal ICollection<WebServiceParameterData> ParameterDatas {
|
||||
get {
|
||||
return ParameterDataDictionary.Values;
|
||||
}
|
||||
}
|
||||
|
||||
internal int CacheDuration {
|
||||
get {
|
||||
Debug.Assert(_webMethodAttribute != null); // If fails: WebserviceMethodData corrosponding to WCF is being used by ASMX JSON handler
|
||||
return _webMethodAttribute.CacheDuration;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool RequiresSession {
|
||||
get {
|
||||
Debug.Assert(_webMethodAttribute != null); // If fails: WebserviceMethodData corrosponding to WCF is being used by ASMX JSON handler
|
||||
return _webMethodAttribute.EnableSession;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsStatic {
|
||||
get {
|
||||
Debug.Assert(_methodInfo != null); // If fails: WebserviceMethodData corrosponding to WCF is being used by ASMX JSON handler
|
||||
return _methodInfo.IsStatic;
|
||||
}
|
||||
}
|
||||
|
||||
internal Type ReturnType {
|
||||
get {
|
||||
Debug.Assert(_methodInfo != null); // If fails: WebserviceMethodData corrosponding to WCF is being used by ASMX JSON handler
|
||||
return (_methodInfo == null) ? null : _methodInfo.ReturnType;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool UseXmlResponse {
|
||||
get {
|
||||
if (_scriptMethodAttribute != null) {
|
||||
return _scriptMethodAttribute.ResponseFormat == ResponseFormat.Xml;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool XmlSerializeString {
|
||||
get {
|
||||
if (_scriptMethodAttribute != null) {
|
||||
return _scriptMethodAttribute.XmlSerializeString;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool UseGet {
|
||||
get {
|
||||
if (_useHttpGet != null) {
|
||||
return _useHttpGet.Value;
|
||||
}
|
||||
if (_scriptMethodAttribute != null) {
|
||||
return _scriptMethodAttribute.UseHttpGet;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal object CallMethodFromRawParams(object target, IDictionary<string, object> parameters) {
|
||||
// Process the 'raw' parameters so that we use strongly typed objects when possible
|
||||
parameters = StrongTypeParameters(parameters);
|
||||
return CallMethod(target, parameters);
|
||||
}
|
||||
|
||||
private object CallMethod(object target, IDictionary<string, object> parameters) {
|
||||
// Make sure we have all the data about this method's parameters
|
||||
EnsureParameters();
|
||||
|
||||
// Allocate an object array for all the formal parameters (whether passed in or not)
|
||||
object[] actualParams = new object[_parameterData.Count];
|
||||
|
||||
// Go through all the actual parameters
|
||||
foreach (WebServiceParameterData paramData in _parameterData.Values) {
|
||||
object value;
|
||||
if (parameters.TryGetValue(paramData.ParameterInfo.Name, out value)) {
|
||||
actualParams[paramData.Index] = value;
|
||||
}
|
||||
else {
|
||||
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.WebService_MissingArg, paramData.ParameterInfo.Name));
|
||||
}
|
||||
}
|
||||
|
||||
// Make the actual method call
|
||||
return _methodInfo.Invoke(target, actualParams);
|
||||
}
|
||||
|
||||
private IDictionary<string, object> StrongTypeParameters(IDictionary<string, object> rawParams) {
|
||||
Debug.Assert(ParameterDataDictionary != null);
|
||||
IDictionary<string, WebServiceParameterData> paramDataDictionary = ParameterDataDictionary;
|
||||
|
||||
// Allocate a dictionary to hold the processed parameters.
|
||||
IDictionary<string, object> result = new Dictionary<string, object>(rawParams.Count);
|
||||
|
||||
// Go through all the raw parameters
|
||||
foreach (KeyValuePair<string, object> pair in rawParams) {
|
||||
string memberName = pair.Key;
|
||||
if (paramDataDictionary.ContainsKey(memberName)) {
|
||||
// Get the type of the formal parameter
|
||||
Type paramType = paramDataDictionary[memberName].ParameterInfo.ParameterType;
|
||||
|
||||
// Convert the raw parameter based on that type
|
||||
result[memberName] = ObjectConverter.ConvertObjectToType(pair.Value, paramType, Owner.Serializer);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebServiceParameterData.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
internal class WebServiceParameterData {
|
||||
private ParameterInfo _param;
|
||||
private int _index; // Index of the parameter in the method
|
||||
private string _paramName;
|
||||
private Type _paramType;
|
||||
|
||||
internal WebServiceParameterData(ParameterInfo param, int index) {
|
||||
_param = param;
|
||||
_index = index;
|
||||
}
|
||||
|
||||
// This constructor is only used by indigo
|
||||
internal WebServiceParameterData(string paramName, Type paramType, int index) {
|
||||
_paramName = paramName;
|
||||
_paramType = paramType;
|
||||
_index = index;
|
||||
|
||||
}
|
||||
|
||||
internal int Index {
|
||||
get { return _index; }
|
||||
}
|
||||
|
||||
internal ParameterInfo ParameterInfo {
|
||||
get { return _param; }
|
||||
}
|
||||
|
||||
internal string ParameterName {
|
||||
get {
|
||||
if (_param != null) {
|
||||
return _param.Name;
|
||||
}
|
||||
else {
|
||||
return _paramName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal Type ParameterType {
|
||||
get {
|
||||
if (_param != null) {
|
||||
return _param.ParameterType;
|
||||
}
|
||||
else {
|
||||
return _paramType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="WebServiceTypeData.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Script.Services {
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
|
||||
|
||||
internal class WebServiceTypeData {
|
||||
// Actual type is needed for ResolveType in WebServiceData (not relevant in Indigo)
|
||||
private Type _actualType;
|
||||
// The custom string reprensenation used for WCF case
|
||||
private string _stringRepresentation;
|
||||
private string _typeName;
|
||||
private string _typeNamespace;
|
||||
static Dictionary<XmlQualifiedName, Type> _nameToType = new Dictionary<XmlQualifiedName, Type>();
|
||||
|
||||
// constants carried over for internal System.Runtime.Seriliazation.Globals class.
|
||||
private const string SerializationNamespace = "http://schemas.microsoft.com/2003/10/Serialization/";
|
||||
private const string StringLocalName = "string";
|
||||
private const string SchemaNamespace = XmlSchema.Namespace;
|
||||
private const string ActualTypeLocalName = "ActualType";
|
||||
private const string ActualTypeNameAttribute = "Name";
|
||||
private const string ActualTypeNamespaceAttribute = "Namespace";
|
||||
private const string EnumerationValueLocalName = "EnumerationValue";
|
||||
private const string OccursUnbounded = "unbounded";
|
||||
|
||||
static WebServiceTypeData() {
|
||||
Add(typeof(sbyte), "byte");
|
||||
Add(typeof(byte), "unsignedByte");
|
||||
Add(typeof(short), "short");
|
||||
Add(typeof(ushort), "unsignedShort");
|
||||
Add(typeof(int), "int");
|
||||
Add(typeof(uint), "unsignedInt");
|
||||
Add(typeof(long), "long");
|
||||
Add(typeof(ulong), "unsignedLong");
|
||||
}
|
||||
|
||||
|
||||
// type is null for WCF
|
||||
internal WebServiceTypeData(string name, string ns, Type type) {
|
||||
if (String.IsNullOrEmpty(ns)) {
|
||||
_typeName = name;
|
||||
if (type == null) { // for WCF known types
|
||||
_stringRepresentation = name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_typeName = ns + "." + name;
|
||||
if (type == null) { // for WCF known types
|
||||
_stringRepresentation = string.Format(CultureInfo.InvariantCulture, "{0}:{1}", name, ns);
|
||||
}
|
||||
}
|
||||
_typeNamespace = ns;
|
||||
_actualType = type;
|
||||
}
|
||||
|
||||
internal WebServiceTypeData(string name, string ns)
|
||||
: this(name, ns, null) {
|
||||
}
|
||||
|
||||
private static XmlQualifiedName actualTypeAnnotationName;
|
||||
private static XmlQualifiedName ActualTypeAnnotationName {
|
||||
get {
|
||||
if (actualTypeAnnotationName == null)
|
||||
actualTypeAnnotationName = new XmlQualifiedName(ActualTypeLocalName, SerializationNamespace);
|
||||
return actualTypeAnnotationName;
|
||||
}
|
||||
}
|
||||
|
||||
static XmlQualifiedName enumerationValueAnnotationName;
|
||||
private static XmlQualifiedName EnumerationValueAnnotationName {
|
||||
get {
|
||||
if (enumerationValueAnnotationName == null)
|
||||
enumerationValueAnnotationName = new XmlQualifiedName(EnumerationValueLocalName, SerializationNamespace);
|
||||
return enumerationValueAnnotationName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal string StringRepresentation {
|
||||
get { return _stringRepresentation; }
|
||||
}
|
||||
|
||||
internal string TypeName {
|
||||
get { return _typeName; }
|
||||
}
|
||||
|
||||
internal string TypeNamespace {
|
||||
get { return _typeNamespace; }
|
||||
}
|
||||
|
||||
internal Type Type {
|
||||
get { return _actualType; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void Add(Type type, string localName) {
|
||||
XmlQualifiedName stableName = new XmlQualifiedName(localName, XmlSchema.Namespace);
|
||||
_nameToType.Add(stableName, type);
|
||||
}
|
||||
|
||||
private static bool CheckIfCollection(XmlSchemaComplexType type) {
|
||||
if (type == null) {
|
||||
return false;
|
||||
}
|
||||
bool isCollection = false;
|
||||
if (type.ContentModel == null) {
|
||||
isCollection = CheckIfCollectionSequence(type.Particle as XmlSchemaSequence);
|
||||
}
|
||||
return isCollection;
|
||||
}
|
||||
|
||||
private static bool CheckIfCollectionSequence(XmlSchemaSequence rootSequence) {
|
||||
// No support for versioning since schema is not persisted; unknown serialization elements are not removed
|
||||
if (rootSequence.Items == null || rootSequence.Items.Count == 0)
|
||||
return false;
|
||||
if (rootSequence.Items.Count != 1)
|
||||
return false;
|
||||
|
||||
XmlSchemaObject o = rootSequence.Items[0];
|
||||
if (!(o is XmlSchemaElement))
|
||||
return false;
|
||||
|
||||
XmlSchemaElement localElement = (XmlSchemaElement)o;
|
||||
return (localElement.MaxOccursString == OccursUnbounded || localElement.MaxOccurs > 1);
|
||||
}
|
||||
|
||||
private static bool CheckIfEnum(XmlSchemaSimpleType simpleType, out XmlSchemaSimpleTypeRestriction simpleTypeRestriction) {
|
||||
simpleTypeRestriction = null;
|
||||
if (simpleType == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check enum
|
||||
XmlSchemaSimpleTypeRestriction restriction = simpleType.Content as XmlSchemaSimpleTypeRestriction;
|
||||
if (restriction != null) {
|
||||
simpleTypeRestriction = restriction;
|
||||
return CheckIfEnumRestriction(restriction);
|
||||
}
|
||||
|
||||
// check flags enum
|
||||
XmlSchemaSimpleTypeList list = simpleType.Content as XmlSchemaSimpleTypeList;
|
||||
XmlSchemaSimpleType anonymousType = list.ItemType;
|
||||
if (anonymousType != null) {
|
||||
restriction = anonymousType.Content as XmlSchemaSimpleTypeRestriction;
|
||||
if (restriction != null) {
|
||||
simpleTypeRestriction = restriction;
|
||||
return CheckIfEnumRestriction(restriction);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool CheckIfEnumRestriction(XmlSchemaSimpleTypeRestriction restriction) {
|
||||
foreach (XmlSchemaFacet facet in restriction.Facets) {
|
||||
if (!(facet is XmlSchemaEnumerationFacet)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Does not check for non-string name or anonymous base type
|
||||
// because these are exported types and do not use those constructs.
|
||||
if (restriction.BaseTypeName != XmlQualifiedName.Empty) {
|
||||
return (restriction.BaseTypeName.Name == StringLocalName
|
||||
&& restriction.BaseTypeName.Namespace == SchemaNamespace
|
||||
&& restriction.Facets.Count > 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetInnerText(XmlQualifiedName typeName, XmlElement xmlElement) {
|
||||
if (xmlElement != null) {
|
||||
XmlNode child = xmlElement.FirstChild;
|
||||
while (child != null) {
|
||||
if (child.NodeType == XmlNodeType.Element) {
|
||||
throw new InvalidOperationException();
|
||||
//
|
||||
}
|
||||
child = child.NextSibling;
|
||||
}
|
||||
return xmlElement.InnerText;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static List<WebServiceTypeData> GetKnownTypes(Type type, WebServiceTypeData typeData) {
|
||||
List<WebServiceTypeData> knownTypes = new List<WebServiceTypeData>();
|
||||
XsdDataContractExporter exporter = new XsdDataContractExporter();
|
||||
exporter.Export(type);
|
||||
ICollection schemas = exporter.Schemas.Schemas();
|
||||
foreach (XmlSchema schema in schemas) {
|
||||
// DataContractSerializer always exports built-in types into a fixed schema that can be ignored.
|
||||
if (schema.TargetNamespace == SerializationNamespace) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (XmlSchemaObject schemaObj in schema.Items) {
|
||||
XmlSchemaType schemaType = schemaObj as XmlSchemaType;
|
||||
string schemaTargetNamespace = XmlConvert.DecodeName(schema.TargetNamespace);
|
||||
if (schemaType != null
|
||||
&& !(schemaType.Name == typeData.TypeName && schemaTargetNamespace == typeData.TypeNamespace)
|
||||
&& !String.IsNullOrEmpty(schemaType.Name)) {
|
||||
WebServiceTypeData knownTypeData = null;
|
||||
XmlSchemaSimpleTypeRestriction simpleTypeRestriction;
|
||||
if (CheckIfEnum(schemaType as XmlSchemaSimpleType, out simpleTypeRestriction)) {
|
||||
knownTypeData = ImportEnum(XmlConvert.DecodeName(schemaType.Name), schemaTargetNamespace, schemaType.QualifiedName, simpleTypeRestriction, schemaType.Annotation);
|
||||
}
|
||||
else if (CheckIfCollection(schemaType as XmlSchemaComplexType)) {
|
||||
continue;
|
||||
}
|
||||
else if (!(schemaType is XmlSchemaSimpleType)) {
|
||||
knownTypeData = new WebServiceTypeData(XmlConvert.DecodeName(schemaType.Name), schemaTargetNamespace);
|
||||
}
|
||||
if (knownTypeData != null) {
|
||||
knownTypes.Add(knownTypeData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return knownTypes;
|
||||
}
|
||||
|
||||
//used for WCF known types
|
||||
internal static WebServiceTypeData GetWebServiceTypeData(Type type) {
|
||||
|
||||
WebServiceTypeData typeData = null;
|
||||
XsdDataContractExporter exporter = new XsdDataContractExporter();
|
||||
XmlQualifiedName qname = exporter.GetSchemaTypeName(type);
|
||||
if (!qname.IsEmpty) {
|
||||
if (type.IsEnum) {
|
||||
bool isUlong = (Enum.GetUnderlyingType(type) == typeof(ulong));
|
||||
typeData = new WebServiceEnumData(XmlConvert.DecodeName(qname.Name), XmlConvert.DecodeName(qname.Namespace), Enum.GetNames(type), Enum.GetValues(type), isUlong);
|
||||
}
|
||||
else {
|
||||
typeData = new WebServiceTypeData(XmlConvert.DecodeName(qname.Name), XmlConvert.DecodeName(qname.Namespace));
|
||||
}
|
||||
}
|
||||
return typeData;
|
||||
}
|
||||
|
||||
internal static XmlQualifiedName ImportActualType(XmlSchemaAnnotation annotation, XmlQualifiedName defaultTypeName, XmlQualifiedName typeName) {
|
||||
XmlElement actualTypeElement = ImportAnnotation(annotation, ActualTypeAnnotationName);
|
||||
if (actualTypeElement == null) {
|
||||
return defaultTypeName;
|
||||
}
|
||||
|
||||
XmlNode nameAttribute = actualTypeElement.Attributes.GetNamedItem(ActualTypeNameAttribute);
|
||||
Debug.Assert(nameAttribute != null);
|
||||
Debug.Assert(nameAttribute.Value != null);
|
||||
string name = nameAttribute.Value;
|
||||
|
||||
XmlNode nsAttribute = actualTypeElement.Attributes.GetNamedItem(ActualTypeNamespaceAttribute);
|
||||
Debug.Assert(nsAttribute != null);
|
||||
Debug.Assert(nsAttribute.Value != null);
|
||||
string ns = nsAttribute.Value;
|
||||
|
||||
return new XmlQualifiedName(name, ns);
|
||||
}
|
||||
|
||||
|
||||
static XmlElement ImportAnnotation(XmlSchemaAnnotation annotation, XmlQualifiedName annotationQualifiedName) {
|
||||
if (annotation != null && annotation.Items != null && annotation.Items.Count > 0 && annotation.Items[0] is XmlSchemaAppInfo) {
|
||||
XmlSchemaAppInfo appInfo = (XmlSchemaAppInfo)annotation.Items[0];
|
||||
XmlNode[] markup = appInfo.Markup;
|
||||
if (markup != null) {
|
||||
for (int i = 0; i < markup.Length; i++) {
|
||||
XmlElement annotationElement = markup[i] as XmlElement;
|
||||
if (annotationElement != null && annotationElement.LocalName == annotationQualifiedName.Name && annotationElement.NamespaceURI == annotationQualifiedName.Namespace) {
|
||||
return annotationElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
static WebServiceEnumData ImportEnum(string typeName, string typeNamespace, XmlQualifiedName typeQualifiedName, XmlSchemaSimpleTypeRestriction restriction, XmlSchemaAnnotation annotation) {
|
||||
// CheckIfEnum has already checked if baseType of restriction is string
|
||||
XmlQualifiedName baseTypeName = ImportActualType(annotation, new XmlQualifiedName("int", XmlSchema.Namespace), typeQualifiedName);
|
||||
Type baseEnumType = _nameToType[baseTypeName];
|
||||
bool isULong = (baseEnumType == typeof(ulong));
|
||||
List<string> enumNames = new List<string>();
|
||||
List<long> enumValues = new List<long>();
|
||||
foreach (XmlSchemaFacet facet in restriction.Facets) {
|
||||
XmlSchemaEnumerationFacet enumFacet = facet as XmlSchemaEnumerationFacet;
|
||||
Debug.Assert(enumFacet != null);
|
||||
Debug.Assert(enumFacet.Value != null);
|
||||
|
||||
string valueInnerText = GetInnerText(typeQualifiedName, ImportAnnotation(enumFacet.Annotation, EnumerationValueAnnotationName));
|
||||
long value;
|
||||
if (valueInnerText == null) {
|
||||
// ASP .NET AJAX doesn't honor the Flags nature of Flags enums
|
||||
// If it were to, we would assign Math.Pow(2, nameValues.Count) for Flags enums instead.
|
||||
value = enumNames.Count;
|
||||
}
|
||||
else {
|
||||
if (isULong) {
|
||||
value = (long)ulong.Parse(valueInnerText, NumberFormatInfo.InvariantInfo);
|
||||
}
|
||||
else {
|
||||
value = long.Parse(valueInnerText, NumberFormatInfo.InvariantInfo);
|
||||
}
|
||||
}
|
||||
|
||||
enumNames.Add(enumFacet.Value);
|
||||
enumValues.Add(value);
|
||||
}
|
||||
|
||||
return new WebServiceEnumData(typeName, typeNamespace, enumNames.ToArray(), enumValues.ToArray(), isULong);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user