/// <summary> /// Creates a method plan from the parameters listed. /// </summary> /// <param name="methodElement">An XML node where the method and parameters are found.</param> bool ExecuteMethod(XElement methodElement) { // Indicates the success or failure of an individual method execution. bool isSuccessful = false; try { // The client channel on which this method is to be executed is described by the 'client' attribute. XAttribute clientAttribte = methodElement.Attribute("client"); String clientName = clientAttribte == null ? String.Empty : clientAttribte.Value; ClientChannel client; if (!this.clientTable.TryGetValue(clientName, out client)) { throw new Exception(String.Format("The client {0} hasn't been defined", clientName == String.Empty ? "<default>" : clientName)); } // Reflection is used here to find the method to be executed. XAttribute methodNameAttribute = methodElement.Attribute("name"); String methodName = methodNameAttribute == null ? String.Empty : methodNameAttribute.Value; MethodInfo methodInfo = client.ChannelType.GetMethod(methodNameAttribute.Value); if (methodInfo == null) { throw new Exception(String.Format("The method {0} isn't part of the library", methodNameAttribute.Value)); } // This will pull apart the XML that contains the parameters and construct a dictionary of method arguments. Dictionary <String, XElement> parameterDictionary = new Dictionary <String, XElement>(); foreach (XElement parameterElement in methodElement.Elements("parameter")) { XAttribute parameterNameAttribute = parameterElement.Attribute("name"); parameterDictionary.Add(parameterNameAttribute.Value, parameterElement); } // This will correlate the parameter in the XML with the parameter of the actual method found through reflection and convert the parameter into the // proper destination type. The end result is a parameter array that is compatible with a reflection call to the method specified in the XML. ParameterInfo[] parameterInfoArray = methodInfo.GetParameters(); Object[] parameterArray = new Object[parameterInfoArray.Length]; for (int index = 0; index < parameterInfoArray.Length; index++) { ParameterInfo parameterInfo = parameterInfoArray[index]; XElement parameterElement; if (parameterDictionary.TryGetValue(parameterInfo.Name, out parameterElement)) { parameterArray[index] = ScriptLoader.ConvertElement(parameterInfo.ParameterType, parameterElement, Path.GetDirectoryName(this.FileName)); } } try { // At this point, the only thing left to do is call the method using the parsed parameters. methodInfo.Invoke(client.ChannelObject, parameterArray); } catch (TargetInvocationException targetInvocationException) { // We use reflection to execute the method, so naturally we're going to get a reflection error. This will dig out the real reason that this // method failed and throw it. throw targetInvocationException.InnerException; } // The method invocation was successful at this point. isSuccessful = true; } catch (FaultException <IndexNotFoundFault> indexNotFoundException) { // The record wasn't found. Console.WriteLine(Teraque.Properties.Resources.IndexNotFoundError, indexNotFoundException.Detail.IndexName, indexNotFoundException.Detail.TableName); } catch (FaultException <RecordNotFoundFault> recordNotFoundException) { // The record wasn't found. Console.WriteLine(Teraque.Properties.Resources.RecordNotFoundError, CommonConversion.FromArray(recordNotFoundException.Detail.Key), recordNotFoundException.Detail.TableName); } catch (FaultException <ConstraintFault> constraintFaultException) { // The arguments weren't in the proper range. Console.WriteLine(constraintFaultException.Detail.Message); } catch (FaultException <ArgumentFault> argumentFaultException) { // The arguments weren't in the proper range. Console.WriteLine(argumentFaultException.Detail.Message); } catch (FaultException <FormatFault> formatFaultException) { // The arguments weren't in the proper range. Console.WriteLine(formatFaultException.Detail.Message); } catch (FaultException <ExceptionDetail> exceptionDetail) { // This is a general purpose exception for debugging. Console.WriteLine(exceptionDetail.Message); } // This is the final indication of whether the method was successful or not. return(isSuccessful); }
static Int32 Main(String[] args) { // This object is used to load the script and keeps track of loading statistics. ScriptLoader scriptLoader = new ScriptLoader(); // The endpoint is pulled from the application settings first. It can be overridden with the command line. scriptLoader.Endpoint = Properties.Settings.Default.DataModelEndpoint; // These are the parameters that are parsed out of the command line. Boolean forceLogin = false; String inputFilePath = String.Empty; Verbosity verbosity = Verbosity.Minimal; try { // The command line parser is driven by different states that are triggered by the flags read. Unless a flag has been parsed, the command line // parser assumes that it's reading the file name from the command line. ArgumentState argumentState = ArgumentState.InputFileName; // Parse the command line for arguments. foreach (String argument in args) { // Use the dictionary to transition from one state to the next based on the input parameters. ArgumentState nextArgumentState; if (Program.argumentStates.TryGetValue(argument, out nextArgumentState)) { argumentState = nextArgumentState; } // The parsing state will determine which variable is read next. switch (argumentState) { case ArgumentState.EndpointParam: // The next command line argument will be endpoint. argumentState = ArgumentState.Endpoint; break; case ArgumentState.Endpoint: // This will set the endpoint to use when communicating with the service. scriptLoader.Endpoint = argument; break; case ArgumentState.ForceLoginParam: // This will cause the script loader to prompt for login credentials. forceLogin = true; argumentState = ArgumentState.InputFileName; break; case ArgumentState.InputFileParam: // The next command line argument will be the input file name. argumentState = ArgumentState.InputFileName; break; case ArgumentState.InputFileName: // Expand the environment variables so that paths don't need to be absolute. inputFilePath = Environment.ExpandEnvironmentVariables(argument); break; case ArgumentState.VerbosityParam: // Verbose output. argumentState = ArgumentState.Verbosity; break; case ArgumentState.Verbosity: try { // Make sure any parsing errors are ignored. verbosity = (Verbosity)Enum.Parse(typeof(Verbosity), argument); } catch { } break; default: // The parser will revert back to looking for an input file when it doesn't recognized the switch. argumentState = ArgumentState.InputFileName; break; } } // Throw a usage message back at the user if no file name was given. if (inputFilePath == null) { throw new Exception("Usage: \"Script Loader\" [-b <Batch Size>] [-f] -i <FileName>"); } // Expand the environment variables and load the resulting file. scriptLoader.ForceLogin = forceLogin; scriptLoader.FileName = inputFilePath; scriptLoader.Verbosity = verbosity; scriptLoader.Load(); } catch (Exception exception) { // Dump the error. Console.WriteLine(exception.Message); // This will force an abnormal exit from the program. scriptLoader.HasErrors = true; } // Print the final status of the load. Console.WriteLine(String.Format("{0} {1}: {2} Methods Executed", DateTime.Now.ToString("u"), scriptLoader.ScriptName, scriptLoader.MethodCount)); // If an error happened anywhere, don't exit normally. if (scriptLoader.HasErrors) { return(1); } // After a successful load, the properties (specifically user name and password) can be saved for the next invocation of this application. Properties.Settings.Default.Save(); // If we reached here, the file was imported without issue. return(0); }
/// <summary> /// Converts an XElement describing a value to a native CLR value. /// </summary> /// <param name="type">The target datatype.</param> /// <param name="parameterElement">An XElement containing a String representation of a value.</param> /// <param name="directoryName">The directory where external files can be found.</param> /// <returns>The native CLR value of the described value.</returns> static Object ConvertElement(Type type, XElement parameterElement, String directoryName) { // If the target parameter is an array, then construct a vector parameter. if (type == typeof(Object[])) { // The values can be found in a single attribute of the 'parameter' element or be listed as children. This list collects both methods of describing // values and constructs a single array when all elements and attributes are parsed. List <Object> valueList = new List <Object>(); // An attribute can be used to desribe a value. An optional 'Type' attribute can specify what type of conversion is used to evaluate the CLR value. XAttribute valueAttribute = parameterElement.Attribute("value"); if (valueAttribute != null) { valueList.Add(ScriptLoader.ConvertValue(ScriptLoader.GetElementType(typeof(String), parameterElement), valueAttribute.Value)); } // It is possible to specify the value using the content of an XML element or through an "import" statement. This will cycle through any nodes of // the parameter looking for additional nodes containing the data for the parameter. foreach (XObject xObject in parameterElement.Nodes()) { // This uses the element content as the value for the parameter. if (xObject is XText) { XText xText = xObject as XText; valueList.Add(ConvertValue(typeof(String), xText.Value)); } // Elements can be nested inside the parameter element to greater detail to the parameter. if (xObject is XElement) { // This element holds special instructions for the parameter. XElement xElement = xObject as XElement; // Values for a key can be specified as child elements of the parameter. if (xElement.Name == "value") { valueList.Add(ConvertValue(GetElementType(typeof(String), xElement), xElement.Value)); } // This special instruction allows the value of a parameter to come from an external file. This is used primary to load XML content into a // record. if (xElement.Name == "import") { XAttribute xAttribute = xElement.Attribute("path"); String path = Path.IsPathRooted(xAttribute.Value) ? xAttribute.Value : Path.Combine(directoryName, xAttribute.Value); XDocument xDocument = XDocument.Load(path); valueList.Add(ConvertValue(type, xDocument.ToString())); } // A 'load' element will read a binary resource into a byte array. if (xElement.Name == "load") { XAttribute xAttribute = xElement.Attribute("path"); String path = Path.IsPathRooted(xAttribute.Value) ? xAttribute.Value : Path.Combine(directoryName, xAttribute.Value); Byte[] binaryData; using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)) { binaryData = new Byte[fileStream.Length]; fileStream.Read(binaryData, 0, Convert.ToInt32(fileStream.Length)); } return(ConvertValue(type, Convert.ToBase64String(binaryData))); } } } // This array is most often used as a key to find a record in a table. return(valueList.ToArray()); } else { // If the XML specifies an override for the native data type of the parameter, make sure that the new value's type is compatible with the parameter. Type originalType = type; type = ScriptLoader.GetElementType(type, parameterElement); if (type != originalType && !type.IsSubclassOf(originalType)) { throw new Exception(String.Format("Can't cast a parameter of type {0} to {1}.", type, originalType)); } // It is possible to specify the value using the content of an XML element or through an "import" statement. This will cycle through any nodes of // the parameter looking for additional nodes containing the data for the parameter. Of course, for a scalar, there is only a single value that // can be returned, so we'll take the first element we find that converts into a value. foreach (XObject xObject in parameterElement.DescendantNodes()) { return(ScriptLoader.ParseValue(type, xObject, directoryName)); } // If no elements are specified, then we'll simply convert the attribute into a value for this parameter. This is by far the most common method of // providing values for parameter. XAttribute valueAttribute = parameterElement.Attribute("value"); return(valueAttribute == null ? DBNull.Value : ScriptLoader.ConvertValue(type, valueAttribute.Value)); } }