using System; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.Collections.Generic; using System.Data; using System.IO; using System.Reflection; using System.Text; using System.Xml.XPath; using GThinking.N_MISPROAS.Web.AjaxServices; using GThinking.N_MISPRO.Data; namespace GThinking.S_XLXSBJ { /// /// 服务方法管理相关服务。 /// public class ServiceMethod { /// /// 根据传入的 DLL 文件地址,反射获取其中标记了 AjaxMethod 的服务方法信息, /// 并写入数据库表:SRCMD、SRCMETHOD、SRCMETHODPARAMS、SRCTYPE、SRCPROP。 /// /// DLL 文件地址。 /// 操作结果。 [AjaxMethod(AuthRequried = false)] public static Dictionary SaveServiceMethodInfo(string filePath) { Dictionary result = new Dictionary(); TDatabase dberp = null; if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { result["success"] = false; result["msg"] = "文件地址不能为空或文件不存在"; return result; } try { dberp = TContextDatabases.GetDatabase("DBERP"); dberp.BeginTrans(); Assembly assembly = Assembly.LoadFrom(filePath); string macId = Truncate(assembly.GetName().Name, 20); // 先删除该部件旧数据 DeleteOldModuleData(dberp, macId); XmlDocHelper xmlDoc = new XmlDocHelper(filePath); int mdIndex = 0; Dictionary visitedTypes = new Dictionary(StringComparer.OrdinalIgnoreCase); Type[] types = null; try { types = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { types = ex.Types; } if (types == null) { dberp.CommitTrans(); result["success"] = true; result["msg"] = "未找到可扫描的类型"; result["macId"] = macId; return result; } foreach (Type type in types) { if (type == null) continue; if (!type.IsClass) continue; Dictionary methods = GetServiceMethods(type); if (methods == null || methods.Count == 0) continue; mdIndex++; double mdId = (double)mdIndex; InsertSRCMD(dberp, macId, mdId, Truncate(type.FullName, 40)); foreach (ServiceMethodInfo smi in methods.Values) { string methodDesc = xmlDoc.GetMethodSummary(smi.MethodInfo); string retDesc = xmlDoc.GetMethodReturns(smi.MethodInfo); InsertSRCMETHOD( dberp, macId, mdId, Truncate(smi.MethodName, 32), Truncate(GetMethodModifiers(smi.MethodInfo), 80), smi.AuthRequried ? "Y" : "N", smi.UseHttpGet ? "Y" : "N", Truncate(methodDesc, 4000), Truncate(GetTypeDisplayName(smi.ReturnType), 80), Truncate(retDesc, 1000)); if (smi.ParameterList != null) { int paramXh = 0; foreach (ServiceParameterInfo spi in smi.ParameterList) { string paramDesc = xmlDoc.GetParameterSummary(smi.MethodInfo, spi.Name); InsertSRCMETHODPARAMS( dberp, macId, mdId, Truncate(smi.MethodName, 32), (double)paramXh, Truncate(spi.Name, 32), Truncate(GetTypeDisplayName(spi.ParameterType), 80), Truncate(paramDesc, 1000)); ReflectType(spi.ParameterType, dberp, visitedTypes, xmlDoc); paramXh++; } } ReflectType(smi.ReturnType, dberp, visitedTypes, xmlDoc); } } dberp.CommitTrans(); result["success"] = true; result["msg"] = "扫描完成"; result["macId"] = macId; } catch (Exception ex) { if (dberp != null && dberp.InTransaction) dberp.RollbackTrans(); result["success"] = false; result["msg"] = ex.Message; } return result; } /// /// 根据 MACID 查询已扫描的服务方法数据。 /// /// 服务部件ID(程序集名称)。 /// 包含各表数据的结果。 [AjaxMethod(AuthRequried = false)] public static Dictionary GetServiceMethodData(string macId) { Dictionary result = new Dictionary(); TDatabase dberp = null; try { dberp = TContextDatabases.GetDatabase("DBERP"); result["SRCMD"] = QueryTableByMacId(dberp, "SRCMD", macId); result["SRCMETHOD"] = QueryTableByMacId(dberp, "SRCMETHOD", macId); result["SRCMETHODPARAMS"] = QueryTableByMacId(dberp, "SRCMETHODPARAMS", macId); result["SRCTYPE"] = QueryAll(dberp, "SRCTYPE"); result["SRCPROP"] = QueryAll(dberp, "SRCPROP"); result["success"] = true; result["msg"] = ""; } catch (Exception ex) { result["success"] = false; result["msg"] = ex.Message; } return result; } #region 数据库操作 private static void DeleteOldModuleData(TDatabase dberp, string macId) { string[] tables = new string[] { "SRCMETHODPARAMS", "SRCMETHOD", "SRCMD" }; TSQL sql = new TSQL(dberp); foreach (string table in tables) { sql.Clear(); sql.Add("delete from " + table + " where MACID=:pMACID"); sql.ParamByName("pMACID").Value = macId; dberp.ExecSQL(sql); } } private static void DeleteTypeData(TDatabase dberp, string typeFullName) { TSQL sql = new TSQL(dberp); sql.Clear(); sql.Add("delete from SRCPROP where TYPEFULLNAME=:pTYPEFULLNAME"); sql.ParamByName("pTYPEFULLNAME").Value = typeFullName; dberp.ExecSQL(sql); sql.Clear(); sql.Add("delete from SRCTYPE where TYPEFULLNAME=:pTYPEFULLNAME"); sql.ParamByName("pTYPEFULLNAME").Value = typeFullName; dberp.ExecSQL(sql); } private static List> QueryTableByMacId(TDatabase dberp, string tableName, string macId) { TSQL sql = new TSQL(dberp); sql.Add("select " + GetTableColumns(tableName) + " from " + tableName + " where MACID=:pMACID"); sql.ParamByName("pMACID").Value = macId; DataSet ds = dberp.OpenDataSet(sql); return DataTableToList(ds.Tables[0]); } private static List> QueryAll(TDatabase dberp, string tableName) { TSQL sql = new TSQL(dberp); sql.Add("select " + GetTableColumns(tableName) + " from " + tableName); DataSet ds = dberp.OpenDataSet(sql); return DataTableToList(ds.Tables[0]); } private static string GetTableColumns(string tableName) { switch (tableName) { case "SRCMD": return "MACID, MDID, MDNAME"; case "SRCMETHOD": return "MACID, MDID, METHODNAME, MODIFIERS, AUTHREQUIRED, USEHTTPGET, METHODDESC, RETTYPE, RETDESC"; case "SRCMETHODPARAMS": return "MACID, MDID, METHODNAME, PARAMXH, PARAMNAME, PARAMTYPE, PARAMDESC"; case "SRCTYPE": return "TYPEFULLNAME, NAMESPACE, TYPENAME, TYPEDESC"; case "SRCPROP": return "TYPEFULLNAME, PROPNAME, PROPTYPEFULLNAME, NAMESPACE, PROPTYPE, PROPDESC"; default: return "*"; } } private static List> DataTableToList(DataTable dt) { List> list = new List>(); foreach (DataRow row in dt.Rows) { Dictionary item = new Dictionary(); foreach (DataColumn col in dt.Columns) { object val = row[col]; item[col.ColumnName] = val == DBNull.Value ? null : val; } list.Add(item); } return list; } private static void InsertSRCMD(TDatabase dberp, string macId, double mdId, string mdName) { TSQL sql = new TSQL(dberp); sql.Clear(); sql.Add("insert into SRCMD(MACID, MDID, MDNAME) values(:pMACID, :pMDID, :pMDNAME)"); sql.ParamByName("pMACID").Value = macId; sql.ParamByName("pMDID").Value = mdId; sql.ParamByName("pMDNAME").Value = mdName; dberp.ExecSQL(sql); } private static void InsertSRCMETHOD(TDatabase dberp, string macId, double mdId, string methodName, string modifiers, string authRequired, string useHttpGet, string methodDesc, string retType, string retDesc) { TSQL sql = new TSQL(dberp); sql.Clear(); sql.Add("insert into SRCMETHOD(MACID, MDID, METHODNAME, MODIFIERS, AUTHREQUIRED, USEHTTPGET, METHODDESC, RETTYPE, RETDESC) "); sql.Add("values(:pMACID, :pMDID, :pMETHODNAME, :pMODIFIERS, :pAUTHREQUIRED, :pUSEHTTPGET, :pMETHODDESC, :pRETTYPE, :pRETDESC)"); sql.ParamByName("pMACID").Value = macId; sql.ParamByName("pMDID").Value = mdId; sql.ParamByName("pMETHODNAME").Value = methodName; sql.ParamByName("pMODIFIERS").Value = modifiers; sql.ParamByName("pAUTHREQUIRED").Value = authRequired; sql.ParamByName("pUSEHTTPGET").Value = useHttpGet; sql.ParamByName("pMETHODDESC").Value = methodDesc; sql.ParamByName("pRETTYPE").Value = retType; sql.ParamByName("pRETDESC").Value = retDesc; dberp.ExecSQL(sql); } private static void InsertSRCMETHODPARAMS(TDatabase dberp, string macId, double mdId, string methodName, double paramXh, string paramName, string paramType, string paramDesc) { TSQL sql = new TSQL(dberp); sql.Clear(); sql.Add("insert into SRCMETHODPARAMS(MACID, MDID, METHODNAME, PARAMXH, PARAMNAME, PARAMTYPE, PARAMDESC) "); sql.Add("values(:pMACID, :pMDID, :pMETHODNAME, :pPARAMXH, :pPARAMNAME, :pPARAMTYPE, :pPARAMDESC)"); sql.ParamByName("pMACID").Value = macId; sql.ParamByName("pMDID").Value = mdId; sql.ParamByName("pMETHODNAME").Value = methodName; sql.ParamByName("pPARAMXH").Value = paramXh; sql.ParamByName("pPARAMNAME").Value = paramName; sql.ParamByName("pPARAMTYPE").Value = paramType; sql.ParamByName("pPARAMDESC").Value = paramDesc; dberp.ExecSQL(sql); } private static void InsertSRCTYPE(TDatabase dberp, string typeFullName, string nameSpace, string typeName, string typeDesc) { TSQL sql = new TSQL(dberp); sql.Clear(); sql.Add("insert into SRCTYPE(TYPEFULLNAME, NAMESPACE, TYPENAME, TYPEDESC) "); sql.Add("values(:pTYPEFULLNAME, :pNAMESPACE, :pTYPENAME, :pTYPEDESC)"); sql.ParamByName("pTYPEFULLNAME").Value = typeFullName; sql.ParamByName("pNAMESPACE").Value = nameSpace; sql.ParamByName("pTYPENAME").Value = typeName; sql.ParamByName("pTYPEDESC").Value = typeDesc; dberp.ExecSQL(sql); } private static void InsertSRCPROP(TDatabase dberp, string typeFullName, string propName, string propTypeFullName, string nameSpace, string propType, string propDesc) { TSQL sql = new TSQL(dberp); sql.Clear(); sql.Add("insert into SRCPROP(TYPEFULLNAME, PROPNAME, PROPTYPEFULLNAME, NAMESPACE, PROPTYPE, PROPDESC) "); sql.Add("values(:pTYPEFULLNAME, :pPROPNAME, :pPROPTYPEFULLNAME, :pNAMESPACE, :pPROPTYPE, :pPROPDESC)"); sql.ParamByName("pTYPEFULLNAME").Value = typeFullName; sql.ParamByName("pPROPNAME").Value = propName; sql.ParamByName("pPROPTYPEFULLNAME").Value = propTypeFullName; sql.ParamByName("pNAMESPACE").Value = nameSpace; sql.ParamByName("pPROPTYPE").Value = propType; sql.ParamByName("pPROPDESC").Value = propDesc; dberp.ExecSQL(sql); } #endregion #region 反射与类型辅助 private static Dictionary GetServiceMethods(Type classType) { if (classType == null) return null; List typeList = new List(); Type current = classType; typeList.Add(current); while (current.BaseType != null) { current = current.BaseType; if (current != typeof(object)) typeList.Add(current); } Dictionary methods = new Dictionary(StringComparer.OrdinalIgnoreCase); BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Instance; for (int i = typeList.Count - 1; i >= 0; --i) { MethodInfo[] methodInfos = typeList[i].GetMethods(flags); foreach (MethodInfo method in methodInfos) { object[] attribs = method.GetCustomAttributes(typeof(AjaxMethodAttribute), true); if (attribs.Length > 0) { AjaxMethodAttribute ajaxAttr = (AjaxMethodAttribute)attribs[0]; ServiceMethodInfo smi = new ServiceMethodInfo(method, ajaxAttr); smi.LoadMethodParameters(); methods[smi.MethodName] = smi; } } } return methods; } private static string GetMethodModifiers(MethodInfo method) { StringBuilder sb = new StringBuilder(); if (method.IsPublic) sb.Append("public "); else if (method.IsPrivate) sb.Append("private "); else if (method.IsFamily) sb.Append("protected "); else if (method.IsAssembly) sb.Append("internal "); if (method.IsStatic) sb.Append("static "); return sb.ToString().Trim(); } private static void ReflectType(Type type, TDatabase dberp, Dictionary visitedTypes, XmlDocHelper xmlDoc) { if (type == null) return; if (type.IsByRef || type.IsPointer) { ReflectType(type.GetElementType(), dberp, visitedTypes, xmlDoc); return; } if (type.IsArray) { ReflectType(type.GetElementType(), dberp, visitedTypes, xmlDoc); return; } Type underlying = Nullable.GetUnderlyingType(type); if (underlying != null) { ReflectType(underlying, dberp, visitedTypes, xmlDoc); return; } if (type.IsGenericType) { Type[] args = type.GetGenericArguments(); foreach (Type arg in args) ReflectType(arg, dberp, visitedTypes, xmlDoc); Type def = type.GetGenericTypeDefinition(); if (!IsCustomType(def)) return; type = def; } if (!IsCustomType(type)) return; string key = type.FullName; if (string.IsNullOrEmpty(key)) return; if (visitedTypes.ContainsKey(key)) return; visitedTypes[key] = key; string typeFullName = type.FullName.Replace('+', '.'); // 先清理该类型旧数据,再重新写入 DeleteTypeData(dberp, typeFullName); InsertSRCTYPE( dberp, Truncate(typeFullName, 80), Truncate(type.Namespace, 80), Truncate(GetTypeShortName(type), 80), Truncate(xmlDoc.GetSummary(type), 1000)); List members = new List(); members.AddRange(type.GetProperties(BindingFlags.Public | BindingFlags.Instance)); members.AddRange(type.GetFields(BindingFlags.Public | BindingFlags.Instance)); members.Sort(new MemberNameComparer()); foreach (MemberInfo mi in members) { Type memberType = null; if (mi is PropertyInfo) memberType = ((PropertyInfo)mi).PropertyType; else if (mi is FieldInfo) memberType = ((FieldInfo)mi).FieldType; else continue; string propTypeFullName = memberType.FullName == null ? memberType.Name : memberType.FullName.Replace('+', '.'); InsertSRCPROP( dberp, Truncate(typeFullName, 80), Truncate(mi.Name, 32), Truncate(propTypeFullName, 80), Truncate(memberType.Namespace, 80), Truncate(GetTypeDisplayName(memberType), 80), Truncate(xmlDoc.GetSummary(mi), 250)); ReflectType(memberType, dberp, visitedTypes, xmlDoc); } } private static bool IsCustomType(Type type) { if (type == null) return false; if (type.IsGenericParameter) return false; if (type.IsPrimitive) return false; if (type == typeof(string) || type == typeof(object) || type == typeof(decimal) || type == typeof(DateTime) || type == typeof(TimeSpan) || type == typeof(Guid)) return false; if (type.Namespace != null && (type.Namespace.StartsWith("System") || type.Namespace.StartsWith("Microsoft"))) return false; return true; } private static string GetTypeDisplayName(Type type) { if (type == null) return ""; if (type.IsByRef) return GetTypeDisplayName(type.GetElementType()) + "&"; if (type.IsPointer) return GetTypeDisplayName(type.GetElementType()) + "*"; if (type.IsArray) return GetTypeDisplayName(type.GetElementType()) + "[]"; Type underlying = Nullable.GetUnderlyingType(type); if (underlying != null) return GetTypeDisplayName(underlying) + "?"; if (type.IsGenericType) { Type def = type.GetGenericTypeDefinition(); string baseName = def.FullName; if (string.IsNullOrEmpty(baseName)) baseName = type.Name; baseName = baseName.Replace('+', '.'); int backtick = baseName.IndexOf('`'); string cleanName = backtick > 0 ? baseName.Substring(0, backtick) : baseName; Type[] genericArgs = type.GetGenericArguments(); StringBuilder sb = new StringBuilder(); sb.Append(cleanName); sb.Append("<"); for (int i = 0; i < genericArgs.Length; i++) { sb.Append(GetTypeDisplayName(genericArgs[i])); if (i < genericArgs.Length - 1) sb.Append(", "); } sb.Append(">"); return sb.ToString(); } if (type.FullName == null) return type.Name; return type.FullName.Replace('+', '.'); } private static string GetTypeShortName(Type type) { if (type == null) return ""; string name = type.Name; int backtick = name.IndexOf('`'); if (backtick > 0) name = name.Substring(0, backtick); return name; } private static string Truncate(string value, int maxLength) { if (string.IsNullOrEmpty(value)) return string.Empty; if (value.Length > maxLength) return value.Substring(0, maxLength); return value; } #endregion #region 嵌套辅助类 /// /// 服务方法信息。 /// private class ServiceMethodInfo { internal ServiceMethodInfo(MethodInfo methodInfo, AjaxMethodAttribute methodAttribute) { _methodInfo = methodInfo; _methodName = methodInfo.Name; _isPublic = methodInfo.IsPublic; _isStatic = methodInfo.IsStatic; _returnType = methodInfo.ReturnType; _authRequried = methodAttribute.AuthRequried; _useHttpGet = methodAttribute.UseHttpGet; } private MethodInfo _methodInfo; internal MethodInfo MethodInfo { get { return _methodInfo; } } private string _methodName; public string MethodName { get { return _methodName == null ? string.Empty : _methodName; } set { _methodName = value; } } private bool _isPublic; public bool IsPublic { get { return _isPublic; } set { _isPublic = value; } } private bool _isStatic; public bool IsStatic { get { return _isStatic; } set { _isStatic = value; } } private Type _returnType; public Type ReturnType { get { return _returnType; } set { _returnType = value; } } private bool _authRequried; public bool AuthRequried { get { return _authRequried; } set { _authRequried = value; } } private bool _useHttpGet; public bool UseHttpGet { get { return _useHttpGet; } set { _useHttpGet = value; } } private List _parameterList; public List ParameterList { get { return _parameterList; } set { _parameterList = value; } } internal void LoadMethodParameters() { if (_methodInfo == null) return; List list = new List(); int index = 0; foreach (ParameterInfo param in _methodInfo.GetParameters()) { list.Add(new ServiceParameterInfo(param, index)); index++; } _parameterList = list; } } /// /// 服务方法参数信息。 /// private class ServiceParameterInfo { private ParameterInfo _param; private int _index; internal ServiceParameterInfo(ParameterInfo param, int index) { _param = param; _index = index; } internal ParameterInfo ParameterInfo { get { return _param; } } internal int Index { get { return _index; } } public string Name { get { return _param.Name; } } public Type ParameterType { get { return _param.ParameterType; } } } /// /// XML 文档注释读取辅助类。 /// private class XmlDocHelper { private XPathDocument _doc; private XPathNavigator _nav; public XmlDocHelper(string dllPath) { if (string.IsNullOrEmpty(dllPath)) return; string fullPath = Path.GetFullPath(dllPath); string dir = Path.GetDirectoryName(fullPath); string xmlPath = Path.Combine(dir, Path.GetFileNameWithoutExtension(fullPath) + ".xml"); if (!File.Exists(xmlPath)) return; try { _doc = new XPathDocument(xmlPath); _nav = _doc.CreateNavigator(); } catch { _doc = null; _nav = null; } } public string GetSummary(Type type) { if (_nav == null || type == null) return ""; string id = GetTypeDocId(type); return GetChildText("T", id, "summary"); } public string GetSummary(MemberInfo member) { if (_nav == null || member == null) return ""; string prefix = "P"; if (member is FieldInfo) prefix = "F"; else if (member is PropertyInfo) prefix = "P"; else if (member is MethodInfo) prefix = "M"; string id = GetMemberDocId(member); return GetChildText(prefix, id, "summary"); } public string GetMethodSummary(MethodInfo method) { XPathNavigator node = FindMethodNode(method); if (node == null) return ""; XPathNavigator sum = node.SelectSingleNode("summary"); return sum == null ? "" : sum.Value.Trim(); } public string GetMethodReturns(MethodInfo method) { XPathNavigator node = FindMethodNode(method); if (node == null) return ""; XPathNavigator ret = node.SelectSingleNode("returns"); return ret == null ? "" : ret.Value.Trim(); } public string GetParameterSummary(MethodInfo method, string paramName) { XPathNavigator node = FindMethodNode(method); if (node == null || string.IsNullOrEmpty(paramName)) return ""; string expr = "param[@name='" + Escape(paramName) + "']"; XPathNavigator pn = node.SelectSingleNode(expr); return pn == null ? "" : pn.Value.Trim(); } private string GetChildText(string prefix, string id, string childName) { if (_nav == null) return ""; string expr = "//member[@name='" + prefix + ":" + Escape(id) + "']"; XPathNavigator node = _nav.SelectSingleNode(expr); if (node == null) return ""; XPathNavigator child = node.SelectSingleNode(childName); return child == null ? "" : child.Value.Trim(); } private XPathNavigator FindMethodNode(MethodInfo method) { if (_nav == null || method == null) return null; string exactId = GetMethodDocId(method); string expr = "//member[@name='M:" + Escape(exactId) + "']"; XPathNavigator node = _nav.SelectSingleNode(expr); if (node != null) return node; string typeId = GetTypeDocId(method.DeclaringType); string prefix = "M:" + Escape(typeId) + "." + method.Name; XPathNodeIterator iter = _nav.Select("//member[starts-with(@name,'" + prefix + "')]"); int pCount = method.GetParameters().Length; while (iter.MoveNext()) { string name = iter.Current.GetAttribute("name", ""); int open = name.IndexOf('('); int close = name.LastIndexOf(')'); if (open > 0 && close > open) { string sig = name.Substring(open + 1, close - open - 1); int count = string.IsNullOrEmpty(sig) ? 0 : CountChar(sig, ',') + 1; if (count == pCount) return iter.Current.Clone(); } } return null; } private static string GetTypeDocId(Type type) { if (type == null) return ""; string fullName = type.FullName; if (string.IsNullOrEmpty(fullName)) return type.Name; return fullName.Replace('+', '.'); } private static string GetMemberDocId(MemberInfo member) { Type type = member.DeclaringType; string typeId = GetTypeDocId(type); if (member is MethodInfo) return GetMethodDocId((MethodInfo)member); return typeId + "." + member.Name; } private static string GetMethodDocId(MethodInfo method) { StringBuilder sb = new StringBuilder(); sb.Append(GetTypeDocId(method.DeclaringType)); sb.Append("."); sb.Append(method.Name); if (method.IsGenericMethod) sb.Append("``").Append(method.GetGenericArguments().Length); sb.Append("("); ParameterInfo[] ps = method.GetParameters(); for (int i = 0; i < ps.Length; i++) { if (i > 0) sb.Append(","); sb.Append(GetParameterDocTypeName(ps[i].ParameterType)); } sb.Append(")"); return sb.ToString(); } private static string GetParameterDocTypeName(Type type) { if (type == null) return ""; if (type.IsGenericParameter) return type.Name; if (type.IsByRef) return GetParameterDocTypeName(type.GetElementType()) + "@"; if (type.IsPointer) return GetParameterDocTypeName(type.GetElementType()) + "*"; if (type.IsArray) return GetParameterDocTypeName(type.GetElementType()) + "[]"; Type underlying = Nullable.GetUnderlyingType(type); if (underlying != null) return GetParameterDocTypeName(underlying) + "?"; if (type.IsGenericType) { Type def = type.GetGenericTypeDefinition(); string baseName = def.FullName; if (string.IsNullOrEmpty(baseName)) baseName = type.Name; baseName = baseName.Replace('+', '.'); Type[] args = type.GetGenericArguments(); StringBuilder sb = new StringBuilder(); sb.Append(baseName); sb.Append("{"); for (int i = 0; i < args.Length; i++) { if (i > 0) sb.Append(","); sb.Append(GetParameterDocTypeName(args[i])); } sb.Append("}"); return sb.ToString(); } string name = type.FullName; if (string.IsNullOrEmpty(name)) name = type.Name; return name.Replace('+', '.'); } private static string Escape(string s) { if (s == null) return ""; return s.Replace("'", "''"); } private static int CountChar(string s, char c) { int count = 0; for (int i = 0; i < s.Length; i++) { if (s[i] == c) count++; } return count; } } /// /// 按成员名称排序的比较器。 /// private class MemberNameComparer : IComparer { public int Compare(MemberInfo x, MemberInfo y) { return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); } } #endregion } }