[英]Build SQL Query from TreeView nodes
我正在尝试构建简单的sql查询编辑器,我需要能够编辑查询条件(在WHERE
关键字之后的部分)。
我有显示条件的树,如下所示:
我在将节点转换为正确的SQL代码时遇到问题。
我可以遍历节点,但这给了我以下结果:
AND
Name = John
OR
Surname = Smith
Lastname = Smith
但是我需要这个(有效的SQL):
Name = John
AND
(
Surname = Smith
OR
Lastname = Smith
)
我怎样才能做到这一点?
这是我用来扩展TreeNode并从屏幕快照创建示例的代码:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var root = new OperatorNode(OperatorType.AND);
var treeNode = new SqlTextNode("Name", ContitionType.EQUAL, "John");
root.Nodes.Add(treeNode);
var orNode = new OperatorNode(OperatorType.OR);
var node2 = new SqlTextNode("Surname",ContitionType.EQUAL, "Smith");
var node3 = new SqlTextNode("Lastname",ContitionType.EQUAL, "Smith");
orNode.Nodes.Add(node2);
orNode.Nodes.Add(node3);
root.Nodes.Add(orNode);
treeView1.Nodes.Add(root);
treeView1.ExpandAll();
}
private void button1_Click(object sender, EventArgs e)
{
CallRecursive(treeView1);
}
private void PrintRecursive(TreeNode treeNode)
{
Debug.WriteLine(treeNode.Text);
foreach (TreeNode tn in treeNode.Nodes)
{
PrintRecursive(tn);
}
}
private void CallRecursive(TreeView treeView)
{
TreeNodeCollection nodes = treeView.Nodes;
foreach (TreeNode n in nodes)
{
PrintRecursive(n);
}
}
}
public class OperatorNode : TreeNode
{
private OperatorType _operator;
public OperatorType Operator
{
get { return _operator; }
set
{
if(value==_operator) return;
_operator = value;
Text = _operator.ToString();
}
}
public OperatorNode(OperatorType @operator) : base(@operator.ToString())
{
_operator = @operator;
}
}
public class SqlTextNode : TreeNode
{
private string _fieldName;
private ContitionType _condition;
private string _value;
public SqlTextNode(string fieldName, ContitionType condition, string value)
{
_fieldName = fieldName;
_condition = condition;
_value = value;
UpdateText();
}
public string FieldName
{
get { return _fieldName; }
set
{
if (value == _fieldName) return;
_fieldName = value;
UpdateText();
}
}
public ContitionType Condition
{
get { return _condition; }
set
{
if (value == _condition) return;
_condition = value;
UpdateText();
}
}
public string Value
{
get { return _value; }
set
{
if (value == _value) return;
_value = value;
UpdateText();
}
}
private void UpdateText()
{
Text = string.Format("{0} {1} {2}", _fieldName, Enums.GetEnumDescription(_condition), _value);
}
}
public enum OperatorType
{
AND,
OR
}
public enum ContitionType
{
EQUAL,
NOT_EQUAL
}
public static class Enums
{
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}
}
有两种不同类型的节点: OperatorNode
和SqlTextNode
。 您可以轻松检查节点的类型并执行相应的操作:
如果该节点是SqlTextNode
只需像SqlTextNode
一样打印它即可。
如果节点是OperatorNode
,则需要用()
覆盖所有子代,然后在它们之间插入运算符。
该算法可以通过许多不同的方式实现,但我将添加一个简单的示例:
private void PrintRecursive(TreeNode treeNode, int indentation)
{
var indent = new string(' ', indentation * IndentSize);
// SqlTextNode doesn't have children
if (treeNode is SqlTextNode)
Debug.WriteLine(indent + treeNode.Text);
else
{
Debug.WriteLine(indent + "(");
for (int i=0; i<treeNode.Nodes.Count; i++ )
{
PrintRecursive(treeNode.Nodes[i], indentation+1);
if (i!=treeNode.Nodes.Count-1)
{
// print the operator
indent = new string(' ', (indentation+1) * IndentSize);
Debug.WriteLine(indent + treeNode.Text);
}
}
Debug.WriteLine(indent + ")");
}
}
如果要将缩进添加到最终查询中,则可以通过reqursion函数( CallRecursive
)作为参数,并在需要时增加它。
这是我的答案版本:
我在自定义节点类型中覆盖了ToString
:
public class OperatorNode : TreeNode
{
private OperatorType _operator;
public OperatorType Operator
{
get { return _operator; }
set
{
if(value==_operator) return;
_operator = value;
Text = _operator.ToString();
}
}
public OperatorNode(OperatorType @operator) : base(@operator.ToString())
{
_operator = @operator;
}
public override string ToString()
{
List<string> n = (from TreeNode node in Nodes select node.ToString()).ToList();
return " ( " + string.Join(Environment.NewLine + _operator + " ", n) + " ) ";
}
}
public class SqlTextNode : TreeNode
{
private string _fieldName;
private ContitionType _condition;
private string _value;
public SqlTextNode(string fieldName, ContitionType condition, string value)
{
_fieldName = fieldName;
_condition = condition;
_value = value;
UpdateText();
}
public override string ToString()
{
return string.Format("{0} {1} {2}", _fieldName, _condition.GetDescription(), _value);
}
public string FieldName
{
get { return _fieldName; }
set
{
if (value == _fieldName) return;
_fieldName = value;
UpdateText();
}
}
public ContitionType Condition
{
get { return _condition; }
set
{
if (value == _condition) return;
_condition = value;
UpdateText();
}
}
public string Value
{
get { return _value; }
set
{
if (value == _value) return;
_value = value;
UpdateText();
}
}
private void UpdateText()
{
Text = string.Format("{0} {1} {2}", _fieldName, _condition.GetDescription(), _value);
}
}
这样,每个节点都有负责创建查询部分的逻辑。
有了它,我可以使用以下功能生成完整的查询:
private void PrintQuery(TreeView treeView)
{
string s = string.Empty;
TreeNodeCollection nodes = treeView.Nodes;
s = nodes.Cast<TreeNode>().Aggregate(s, (current, n) => current + (n.ToString() + Environment.NewLine)).Trim();
//remove unwanted brackets
s = s.Remove(s.Length - 1).Substring(1).Trim();
textBox1.Text = s;
}
输出没有缩进,但是我不必显示最终查询,这只是调试的临时结果。
我知道我必须用单引号将条件值转义,但这很容易:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.