public static void BuildCommand(OracleCommand cmd, Func<string, OracleParameter> paramUpdater = null, Func<XElement, bool> customTagResolver = null) { XElement elemSql; try { elemSql = XElement.Parse("<root>" + cmd.CommandText + "</root>", LoadOptions.PreserveWhitespace); } catch (XmlException ex) { var tokens = cmd.CommandText.Split(new[] {Environment.NewLine}, StringSplitOptions.None); string msg; if (ex.LineNumber >= 0 && ex.LineNumber < tokens.Length) { msg = tokens[ex.LineNumber - 1]; } else { msg = string.Format("Line Number: {0}; LinePosition: {1}", ex.LineNumber, ex.LinePosition); } throw new ArgumentException(msg, "cmd.CommandText", ex); } var nav = new XPathEvaluator(p => { OracleParameter param; if (cmd.Parameters.Contains(p)) { param = cmd.Parameters[p]; } else { param = paramUpdater(p); cmd.Parameters.Add(param); } return param.Value; }); var toRemove = new HashSet<XElement>(); foreach (var elem in elemSql.Descendants()) { Debug.Assert(elem != null, "elem != null"); if (toRemove.Contains(elem.Parent)) { // Optimization. // Do nothing because the parent has been removed and so this element is irrelevant continue; } bool bKeep; switch (elem.Name.LocalName) { case "if": bKeep = EvaluateCondition(nav, elem); break; case "a": //<a pre="(", sep=" OR ", post=")">mri.ia_id = :ia_id</a> var matches = _regexParameters.Matches(elem.Value); // Exception here if multiple parameters are found string varName = matches.Cast<Match>().Select(p => p.Groups["paramName"].Value) .Distinct().Single(); string condition = string.Format("${0}", varName); bKeep = nav.Matches(condition); if (bKeep) { bKeep = HandleArrayParamterXml(elem, varName, cmd); } break; case "else": // Keep the else only if all the previous else and if have been removed bKeep = elem.ElementsBeforeSelf().Reverse() .TakeUntil(p => p.Name.LocalName == "if") .All(toRemove.Contains); if (bKeep) { XAttribute attr = elem.Attribute("c"); if (attr != null) { bKeep = EvaluateCondition(nav, elem); } } break; default: if (customTagResolver == null) { throw new NotSupportedException(elem.Name.LocalName); } bKeep = customTagResolver(elem); break; } if (!bKeep) { toRemove.Add(elem); } } toRemove.Remove(); cmd.CommandText = elemSql.Value; string[] paramsUsed = _regexParameters.Matches(cmd.CommandText).Cast<Match>() .Select(p => p.Groups["paramName"].Value) .Distinct(StringComparer.InvariantCultureIgnoreCase) .ToArray(); // Remove excess parameters. Note that we are performing case insensitive comparisons string[] paramsUnused = cmd.Parameters.Cast<OracleParameter>() .Select(p => p.ParameterName) .Except(paramsUsed, StringComparer.InvariantCultureIgnoreCase).ToArray(); foreach (string name in paramsUnused) { cmd.Parameters.Remove(cmd.Parameters[name]); } // Create remaining used parameters string[] paramsUncreated = paramsUsed.Except(cmd.Parameters.Cast<OracleParameter>().Select(p => p.ParameterName), StringComparer.InvariantCultureIgnoreCase).ToArray(); foreach (string name in paramsUncreated) { cmd.Parameters.Add(paramUpdater(name)); } return; }
private static bool EvaluateCondition(XPathEvaluator nav, XElement elem) { XAttribute attr = elem.Attribute("c"); string condition; if (attr == null) { // Build the condition. This condition evaluates to true if all parameters found within the clause // are not null string[] paramsUsed = _regexParameters.Matches(elem.Value).Cast<Match>() .Select(p => "$" + p.Groups["paramName"].Value) .Distinct(StringComparer.InvariantCultureIgnoreCase) .ToArray(); if (paramsUsed.Length == 0) { string msg = string.Format("Cannot infer condition. No parameter within {0}", elem.Value); throw new Exception(msg); } condition = string.Join(" and ", paramsUsed); } else { condition = attr.Value; } bool bMatch = nav.Matches(condition); return bMatch; }