Я также надеялся найти фрагмент кода для преобразования CAML в SQL, чтобы создать собственный оператор SQL для доступа к данным.
Мой основной проект заключается в создании расширения ленты SharePoint для экспорта содержимого списков (внутреннего и внешнего) в CSV, а в случае внешних списков, чтобы иметь возможность обойти ограничение регулирования (2000), установленное для внешних типов содержимого (BCS) ).
Я использую информацию в хранилище метаданных и безопасном хранилище для построения строки подключения и прямого доступа к базе данных.
Когда мне нужно было усовершенствовать мой код, включив в него фильтры, я закончил тем, что собрал свои собственные методы, чтобы получить часть «Где» в представлении запроса и преобразовать ее в SQL-подобный оператор Where:
Ввод:
В моем случае это объект SPView, но его можно легко преобразовать для использования строки. Я извлекаю из него запрос типа «CAML».
"<Where>
<And>
<Or>
<Geq>
<FieldRef Name=\"Microfilm\" />
<Value Type=\"Text\">10</Value>
</Geq>
<Leq>
<FieldRef Name=\"Microfilm\" />
<Value Type=\"Text\">50</Value>
</Leq>
</Or>
<BeginsWith>
<FieldRef Name=\"Title\" />
<Value Type=\"Text\">Ice</Value>
</BeginsWith>
</And>
</Where>"
Выход:
"(Microfilm >= 10 OR Microfilm <= 50) AND Title LIKE 'Ice%'"
Вот методы:
Этот метод извлекает узел «Где» из представления Query и
передайте его методу для обработки и верните SQL-оператор.
private static string ViewQueryToSqlWhere(SPView v)
{
string sqlWhere = string.Empty;
XmlDocument xmlDoc = new XmlDocument();
XmlNodeList nodeList;
//Add <Query> around the SPView.Query since a valid XML document requires a single root element.
//and SPView.Query doesn't.
xmlDoc.LoadXml("<Query>" + v.Query + "</Query>");
nodeList = xmlDoc.GetElementsByTagName("Where");
if (nodeList.Count == 1)
{
XmlNode nodeWhere = nodeList[0];
if (nodeWhere.HasChildNodes) //Should Always be the case
{
StringBuilder sb = new StringBuilder();
bool isSuccess = ProcessWhereNode(nodeWhere, ref sb);
}
}
return sqlWhere;
}
Этот метод вызовет другой метод для рекурсивного обхода всех узлов, чтобы получить значения и операторы в узле View Query «Where». Он будет заключать в круглые скобки операторы «ИЛИ», чтобы сохранить приоритет операции.
private static bool ProcessWhereNode(XmlNode xmlNode, ref StringBuilder sb)
{
bool isSuccess = false;
Stack<string> operatorStack = new Stack<string>();
Queue<string> valueQueue = new Queue<string>();
string previousOp = string.Empty;
string strOperator = string.Empty;
try
{
//Call a method to iterate "recursively" throught the nodes to get the values and operators.
if (ProcessRecursiveWhereNode(xmlNode, "", "", ref operatorStack, ref valueQueue))
{
while (valueQueue.Count > 0)
{
if (operatorStack.Count > 0)
{
strOperator = operatorStack.Pop();
//Open bracket if it's an OR operator except if the previous one was also an OR.
if (strOperator == "OR" && previousOp != "OR")
sb.Append("(");
}
else
{
strOperator = string.Empty;
}
sb.Append(valueQueue.Dequeue());
//Close bracket if previous OP was an OR, and it's not followed by another one
if (previousOp == "OR" && strOperator != "OR")
sb.Append(")");
if (strOperator != string.Empty)
{
sb.Append(" " + strOperator + " ");
}
previousOp = strOperator;
}
}
}
catch (Exception ex)
{ }
return isSuccess;
}
Этот метод выполняет большую часть работы для итерации каждого узла:
private static bool ProcessRecursiveWhereNode(XmlNode xmlNode, string strOperatorValue, string strOperatorType, ref Stack<string> operatorStack, ref Queue<string> valueQueue)
{
bool isSuccess = false;
string fieldName = string.Empty;
string value = string.Empty;
string thisIterationOperatorType = string.Empty;
string thisIterationOperatorValue = string.Empty;
try
{
XmlNodeList nodeList = xmlNode.ChildNodes;
//Get Child node - Possible tags {<Or>, <And>, <Eq>, <Neq>, <Gt>, <Lt>, <Geq>, <Leq>, </BeginsWith>, <Contains>, <FieldRef>, <Value>}
foreach (XmlNode node in nodeList)
{
thisIterationOperatorType = string.Empty;
thisIterationOperatorValue = string.Empty;
//Check if it's one of these tag: <Or>, <And>, <Eq>, <Neq>, <Gt>, <Lt>, <Geq>, <Leq>, </BeginsWith>, <Contains>
thisIterationOperatorValue = GetOperatorString(node.Name, out thisIterationOperatorType);
if (thisIterationOperatorType == "statement")
operatorStack.Push(thisIterationOperatorValue);
//It's one of these tag: <Or>, <And>, <Eq>, <Neq>, <Gt>, <Lt>, <Geq>, <Leq>, </BeginsWith>, <Contains>
if (thisIterationOperatorValue != string.Empty)
{
ProcessRecursiveWhereNode(node, thisIterationOperatorValue, thisIterationOperatorType, ref operatorStack, ref valueQueue);
}
else //It is probably a <FieldRef> or <Value> tag.
{
if (node.Name == "FieldRef")
fieldName = node.Attributes["Name"].Value.ToString();
else if (node.Name == "Value")
value = node.LastChild.Value.ToString();
}
}
if (strOperatorType == "value" && strOperatorValue != string.Empty && fieldName != string.Empty && value != string.Empty)
{
valueQueue.Enqueue(string.Format(strOperatorValue, fieldName, "'" + value + "'"));
}
isSuccess = true;
}
catch
{
isSuccess = false;
throw;
}
return isSuccess;
}
Этот последний метод, возможно, мог бы быть включен в рекурсивный, но в моей первой итерации построения кода было более разумно создать отдельный метод, и я сохранил его таким образом.
Он просто получает некоторую информацию об операторах и связывает строку оператора, которая будет использоваться для построения отдельных частей оператора SQL Where.
static private string GetOperatorString(string tagName, out string operatorType)
{
string operatorString = string.Empty;
switch (tagName)
{
case "Or":
operatorString = "OR";
operatorType = "statement";
break;
case "And":
operatorString = "AND";
operatorType = "statement";
break;
case "Eq":
operatorString = "{0} = {1}";
operatorType = "value";
break;
case "Neq":
operatorString = "{0} != {1}";
operatorType = "value";
break;
case "Gt":
operatorString = "{0} > {1}";
operatorType = "value";
break;
case "Lt":
operatorString = "{0} < {1}";
operatorType = "value";
break;
case "Geq":
operatorString = "{0} >= {1}";
operatorType = "value";
break;
case "Leq":
operatorString = "{0} <= {1}";
operatorType = "value";
break;
case "BeginsWith":
operatorString = "{0} LIKE '{1}%";
operatorType = "value";
break;
case "Contains":
operatorString = "{0} LIKE '%{1}%";
operatorType = "value";
break;
default:
operatorString = string.Empty;
operatorType = string.Empty;
break;
}
return operatorString;
}
Я знаю, что это не полный инструмент конвертации, но это начало, и пока оно соответствует моим потребностям. Я надеюсь, что это поможет кому-то и сэкономит им драгоценное время.