public void BuildAndCacheServerMethodJsAndTSD() { try { var registrations = ServerMethodManager.GetRegistrationsForApp(this); if (registrations.Count() > 0) { this.GenerateX(registrations); } else { this.ServerMethodJs = this.ServerMethodTSD = this.ServerMethodJsEtag = this.ServerMethodTSDEtag = null; } } catch (Exception ex) { SessionLog.Error($"Failed to generate ServerMethod output files for {this.Project.Name}/{this.Name}.See exception that follows."); SessionLog.Exception(ex); } }
public (plugin.ServerMethodPlugin, ServerMethodRegistrationMethod /*matched Method*/, string /*error*/) GetServerMethodPluginInstance(string nameSpace, string methodName, Dictionary <string, string> inputParameters) { // find all registered ServerMethods for this app var registrations = ServerMethodManager.GetRegistrationsForApp(this.Application); // TODO: To support overloading we need to match name + best fit parameter list var methodCandidates = registrations.SelectMany(reg => reg.Methods) .Where(m => ((nameSpace == null && m.Namespace == null) || (m.Namespace?.Equals(nameSpace, StringComparison.Ordinal) ?? false)) && m.Name.Equals(methodName, StringComparison.Ordinal)) .Select(m => m); if (methodCandidates.Count() == 0) { return(null, null, "Method name not found."); } var weightedMethodList = new List <(decimal /*weight*/, string /*error*/, ServerMethodRegistrationMethod)>(); // find the best matching overload (if any) foreach (var regMethod in methodCandidates) { var methodParameters = regMethod.AssemblyMethodInfo.GetParameters(); if (inputParameters.Count > methodParameters.Length) { weightedMethodList.Add((1M, "Too many parameters specified", regMethod)); continue; } var joined = from methodParam in methodParameters join inputParam in inputParameters on methodParam.Name equals inputParam.Key into grp from parm in grp.DefaultIfEmpty() select new { HasMatch = parm.Key != null, Param = methodParam }; var matched = joined.Where(e => e.HasMatch); var notmatched = joined.Where(e => !e.HasMatch); var expectedCnt = methodParameters.Count(); var matchedCnt = matched.Count(); // out/ref/optional parameters are added as extra credit below (but does not contribute to actual weight) var outRefSum = (from p in joined where (p.Param.IsOut || p.Param.IsOptional || p.Param.ParameterType.IsByRef) && !p.HasMatch select 1.0M).Sum(); if (matchedCnt == expectedCnt || matchedCnt + outRefSum == expectedCnt) { weightedMethodList.Add((matchedCnt, null, regMethod)); } else { //weightedMethodList.Add((matchedCnt, $"Following parameters not specified: {string.Join("\r\n", notmatched.Select(nm => nm.Param.Name))}", regMethod)); weightedMethodList.Add((matchedCnt, "Parameter mismatch", regMethod)); } } var bestMatch = weightedMethodList.OrderByDescending(k => k.Item1).FirstOrDefault(); if (!string.IsNullOrWhiteSpace(bestMatch.Item2)) { var parms = bestMatch.Item3.AssemblyMethodInfo.GetParameters(); var parmDesc = "(no parameters)"; if (parms.Length > 0) { parmDesc = string.Join("\r\n", parms.Select(p => $"{p.Name} ({p.ParameterType.ToString()})")); // TODO: Provide "easy to read" description for type, e.g. nullabe Int32 can be something like 'int?' and 'List<string>' just 'string[]' } return(null, bestMatch.Item3, $"Failed to find suitable overload.\r\nError: {bestMatch.Item2}\r\nBest match requires parameters:\r\n{parmDesc}"); } var matchedRegMethod = bestMatch.Item3; var cacheKey = $"{matchedRegMethod.Registration.PluginAssemblyInstanceId}; {matchedRegMethod.Registration.TypeInfo.FullName}"; plugin.ServerMethodPlugin pluginInstance = null; lock (ServerMethodInstanceCache) { if (ServerMethodInstanceCache.ContainsKey(cacheKey)) { pluginInstance = ServerMethodInstanceCache[cacheKey]; } else // instantiate a new instance { try { pluginInstance = (plugin.ServerMethodPlugin)matchedRegMethod.Registration.Assembly.CreateInstance(matchedRegMethod.Registration.TypeInfo.FullName); var initMethod = typeof(plugin.ServerMethodPlugin).GetMethod("InitSM", BindingFlags.Instance | BindingFlags.NonPublic); if (initMethod != null) { initMethod.Invoke(pluginInstance, new object[] { new Func <SqlConnection>(() => { if (this.ExecutionConnection != null) { var con = new SqlConnection(this.ExecutionConnection.ConnectionStringDecrypted); con.Open(); return(con); } return(new SqlConnection()); }) }); } else { SessionLog.Warning($"Failed to find InitSM method on plugin {matchedRegMethod.Registration.TypeInfo.FullName} from assembly {matchedRegMethod.Registration.Assembly.FullName}. Make sure the correct version of the jsdal plugin is used and that you derive from the correct base class (should be ServerMethodPlugin)."); } var setGetServicesFuncMethod = typeof(plugin.PluginBase).GetMethod("SetGetServicesFunc", BindingFlags.Instance | BindingFlags.NonPublic); if (setGetServicesFuncMethod != null) { setGetServicesFuncMethod.Invoke(pluginInstance, new object[] { new Func <Type, plugin.PluginService>(serviceType => { if (serviceType == typeof(plugin.BlobStoreBase)) { return(BlobStore.Instance); } return(null); }) }); } ServerMethodManager.RegisterInstanceUse(this, matchedRegMethod); ServerMethodInstanceCache.Add(cacheKey, pluginInstance); } catch (Exception ex) { SessionLog.Error($"Failed to instantiate plugin {matchedRegMethod.Registration.TypeInfo.FullName} from assembly {matchedRegMethod.Registration.Assembly.FullName}. See exception that follows."); SessionLog.Exception(ex); } } } // lock return(pluginInstance, matchedRegMethod, null); }