基于数据字典的查询系统 最后一篇到完工
MS大家不喜欢我说思路的方式,今天一鼓作气,把剩下的关键代码,类图贴完,加上简单说明,本系列到此结束。
我觉得技术网站应当以交流思想为主,现在看来,大家比较关注代码,呵。
界面上不写了,都比较弱智,而且我是CS的。
1. 布尔表达式的处理。
前面说过,布尔表达式两种形式
Exp àCol + compareOp + Value
Exp àExp + boolOP +Exp
先定义compareOp和boolOp
这里没有用枚举而用静态单例,原因还是老调子,因为要有Show 和EXP
CompareOP
1 namespace SFTech.Query.Entity
2 {
3 public class CompareOp
4 {
5 private string _Show;
6 public String Show
7 {
8 get
9 {
10 return _Show;
11 }
12 private set
13 {
14 _Show = value;
15 }
16 }
17
18 private string _Exp;
19 public String Exp
20 {
21 get
22 {
23 return _Exp;
24 }
25 private set
26 {
27 _Exp = value;
28 }
29 }
30
31 private CompareOp(String show, String exp)
32 {
33 Show = show;
34
35 Exp = exp;
36 }
37
38
39 public static readonly CompareOp MAXTHAN = new CompareOp("大于", ">");
40 public static readonly CompareOp SMALLTHAN = new CompareOp("小于", "<");
41 public static readonly CompareOp EQUAL = new CompareOp("等于", "=");
42 public static readonly CompareOp MAXEQUAL = new CompareOp("大于等于", ">=");
43 public static readonly CompareOp SMALLEQUAL = new CompareOp("小于等于", "<=");
44 public static readonly CompareOp NOEQUAL = new CompareOp("不等于", "<>");
45 public static readonly CompareOp LIKE = new CompareOp("包含", "LIKE");
46
47
48 public static List<CompareOp> Compares
49 {
50 get
51 {
52 List<CompareOp> list = new List<CompareOp>();
53 list.Add(EQUAL);
54 list.Add(MAXTHAN);
55 list.Add(MAXEQUAL);
56 list.Add(SMALLEQUAL);
57 list.Add(SMALLTHAN);
58 list.Add(NOEQUAL);
59 list.Add(LIKE);
60 return list;
61 }
62 }
63
64
65 }
66 }
67
BoolOp
namespace SFTech.Query.Entity
{
public class BoolOp
{
private string _Show;
private string _Exp;
private int _Value;
private BoolOp(String show, string exp, int value) { _Show = show; _Exp = exp; _Value = value; }
public String Show{get{return _Show ;}}
public override string ToString()
{
return _Exp.ToString();
}
public string Exp
{
get
{
return _Exp;
}
}
/// <summary>
/// 并且
/// </summary>
public static readonly BoolOp And = new BoolOp("并且"," and ",0);
/// <summary>
/// 或者
/// </summary>
public static readonly BoolOp OR = new BoolOp("或者", " or ", 1);
}
}
下面讨论布尔表达式的生成方式 。
一种是Exp àCol + compareOp + Value
所以直观上类中应该有一个QueryCol,一个comareOP,和一个value做为布表达式的成员。
而对于value ,当然,我们还要分show和Exp
而对于第二种生成方式。
Exp àExp + boolOP +Exp
还是用“妥协的设计方式”,加一个不论不类的类AddBoolOp,有BoolOp和BoolExpresion两个成员。
这样BoolExpression再加一个List<AddBoolOp>就可以了。
对于在两种方式切换,我们加一个IsFirst的属性、
具体内容不多说,写代码里(大家要的就是这个吧,不好意思,两天了,我还是窝不住火,说话冲点,各位大爷息怒)、
对了,生成条件表达式的内容加在ToString()中了,这个招确实有点下三滥,呵呵,不好意思啦。
namespace SFTech.Query.Entity
{
/// <summary>
/// 表示一个布尔表达式
/// </summary>
public class BoolExpression
{
public BoolExpression(QueryCol col, CompareOp op, String value, String show)
{
this.DataCol = col;
this.CompareOp = op;
this.Value = value;
this.ShowValue = show;
this.Sequences = new ExpressionSequences();
IsSingle = true;
}
private ExpressionSequences _sequences;
public ExpressionSequences Sequences
{
get
{
return _sequences;
}
private set
{
_sequences = value;
}
}
private QueryCol _DataCol;
public QueryCol DataCol
{
get
{
return _DataCol;
}
set
{
_DataCol = value;
}
}
private string _Value;
public String Value
{
get
{
return _Value;
}
set
{
_Value = value;
}
}
private CompareOp _CompareOp;
public CompareOp CompareOp
{
get
{
return _CompareOp;
}
private set
{
_CompareOp = value;
}
}
private string _ShowValue;
public string ShowValue
{
get
{
return _ShowValue;
}
set
{
_ShowValue = value;
}
}
public override string ToString()
{
string result;
if (IsSingle)
result = " ("+this.DataCol.DataTable.Exp +"."+ this.DataCol.Exp +" "+ this.CompareOp.Exp + " '" + this.Value + "') ";
else
result = First.ToString();
foreach (var q in this.Sequences)
{
result = result + q.BoolOp.Exp + q.BoolExpression.ToString();
}
if(this.Sequences .Count > 0)
result = " ("+result +") ";
return result;
}
private bool _IsSigle;
public bool IsSingle
{
get
{
return _IsSigle;
}
set
{
_IsSigle = value;
if (_IsSigle == false)
{
this.First = new BoolExpression(this.DataCol, this.CompareOp, this.Value, this.ShowValue);
}
}
}
private BoolExpression _First;
public BoolExpression First
{
get
{
return _First;
}
private set
{
_First = value;
}
}
}
}
妥协类AddBoolExpression和其集合。
namespace SFTech.Query.Entity
{
public class AddBoolExpression
{
public AddBoolExpression(BoolOp op, BoolExpression exp)
{
BoolOp = op;
BoolExpression = exp;
}
private BoolOp _BoolOp;
public BoolOp BoolOp
{
get
{
return _BoolOp;
}
set
{
_BoolOp = value;
}
}
private BoolExpression _Exp;
public BoolExpression BoolExpression
{
get
{
return _Exp;// throw new System.NotImplementedException();
}
set
{
_Exp = value;
}
}
}
}
namespace SFTech.Query.Entity
{
public class ExpressionSequences : List<AddBoolExpression>
{
}
}
现在还剩对选择列的处理。
前面说了select后面的列,可以是group 的列,也可以是count,sum,也可以是是值,因此先定义输出的方式 ,还是静态类(这招快让我用吐了,呵呵)
namespace SFTech.Query.Entity
{
public class SelectType
{
private string show;
public string Show{
get{
return show;
}
private set {
show = value;
}
}
private string dbexp;
public string DBExp{
get{
return dbexp ;
}
private set {
dbexp = value;
}
}
private SelectType (string _Show,string _exp){
Show = _Show;
DBExp = _exp;
}
public static readonly SelectType Value = new SelectType("", "");
public static readonly SelectType MAX = new SelectType("最大", "MAX");
public static readonly SelectType MIN = new SelectType("最小", "MIN");
public static readonly SelectType COUNT = new SelectType("数量", "COUNT");
public static readonly SelectType SUM = new SelectType("求和", "SUM");
public static readonly SelectType AVG = new SelectType("平均值", "AVG");
public static List<SelectType> Types
{
get
{
List<SelectType> item = new List<SelectType>();
item.Add(MAX);
item.Add(MIN);
item.Add(SUM);
item.Add(COUNT);
item.Add(AVG);
return item;
}
}
}
}
下面就是选择的列了、
namespace SFTech.Query.Entity
{
/// <summary>
/// 表示选择的要输出的或分组的列
/// </summary>
public class SelectedCol
{
private bool _IsGroup;
/// <summary>
/// 是否分组
/// </summary>
public bool IsGroup
{
get
{
return _IsGroup;
}
set
{
_IsGroup = value;
}
}
private bool _isPrint;
public bool IsPrint
{
get
{
return _isPrint;
}
set
{
_isPrint = value;
}
}
private int _SelectedIndex;
/// <summary>
/// 选择/分组顺序
/// </summary>
public int SelectedIndex
{
get
{
return _SelectedIndex;
}
set
{
_SelectedIndex = value;
}
}
private QueryCol _DataCol;
public QueryCol DataCol
{
get
{
return _DataCol;
}
set
{
_DataCol = value;
}
}
private SelectType _Type;
public SelectType SelectType
{
get
{
return _Type;
}
set
{
_Type = value;
}
}
public SelectedCol(QueryCol Col, SelectType Type)
{
this.DataCol = Col;
this.SelectType = Type;
}
}
}
在定义一个选择列的集合,对一些条件,约束的判断加在这里了
namespace SFTech.Query.Entity
{
public class SelectedColCollection : IList<SelectedCol >,ICollection<SelectedCol>
{
private Query.Entity.QueryContext _context;
public Query.Entity.QueryContext Context
{
get
{
return _context;
}
set
{
_context = value;
}
}
private List<SelectedCol> _items;
private int GroupCount
{
get
{
return (from cols in this
where cols.IsGroup == true
select cols).Count();
}
}
public SelectedColCollection()
{
_items = new List<SelectedCol>();
}
#region IList<SelectedCol> 成员
public int IndexOf(SelectedCol item)
{
return _items.IndexOf(item);
}
public void Insert(int index, SelectedCol item)
{
_items.Insert (index, item);
}
public void RemoveAt(int index)
{
_items.RemoveAt(index);
}
public SelectedCol this[int index]
{
get
{
return _items[index];
}
set
{
throw new NotImplementedException();
}
}
#endregion
#region ICollection<SelectedCol> 成员
public void Add(SelectedCol item)
{
if (this.IndexOf(item) > 0)
throw new Exception("要添加的列已经存在!");
if (this.GroupCount > 0
&& item.IsGroup == false
&& item.SelectType == SelectType.Value)
{
throw new Exception("查询中已经包含了分组值,不能再添加非分组的求值。");
}
if(item.IsGroup == false && item.SelectType != SelectType.Value
&&( from se in this._items where se.SelectType == SelectType.Value && se.IsGroup ==false select se).Count ()>0)
throw new Exception ("已经有其他的输出值,不能再添加非值的统计值");
if (item.IsGroup == true)
{
var q = from s in _items
where s.SelectType == SelectType.Value
select s;
int i = 0;
while(_items .Count > 0 &&i<_items.Count ){
var r = _items[i];
if (r.SelectType == SelectType.Value && r.IsGroup == false)
{
_items.Remove(r);
this.Context.ColCollection.Add(r.DataCol );
}
else i++;
}
}
_items.Add(item);
if(item.SelectType == SelectType.Value )
this.Context.ColCollection.Remove(item.DataCol);
for (int i = 0; i < _items.Count; i++)
_items[i].SelectedIndex = i;
}
public void Clear()
{
_items.Clear();
}
public bool Contains(SelectedCol item)
{
return _items.Contains(item);
}
public void CopyTo(SelectedCol[] array, int arrayIndex)
{
_items.CopyTo (array, arrayIndex);
}
public int Count
{
get { return _items.Count; }
}
public bool IsReadOnly
{
get { return true; }
}
public bool Remove(SelectedCol item)
{
return _items.Remove(item);
}
#endregion
#region IEnumerable<SelectedCol> 成员
public IEnumerator<SelectedCol> GetEnumerator()
{
return _items.GetEnumerator();
}
#endregion
#region IEnumerable 成员
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
#endregion
}
}
最后是一个生成查询的类QueryContext。
namespace SFTech.Query.Entity
{
public class QueryContext
{
private JoinConditions _conditions;
private TableCollection AllTables;
private TableCollection _SelectedTables;
public TableCollection SelectedTables
{
get
{
return _SelectedTables;
}
set
{
_SelectedTables = value;
}
}
private TableCollection _CanSelectTable;
public TableCollection CanSelelctTables
{
get
{
return _CanSelectTable;
}
set
{
_CanSelectTable = value;
}
}
private ColCollection _ColCollection;
public ColCollection ColCollection
{
get
{
return _ColCollection;
}
set
{
_ColCollection = value;
}
}
public QueryContext()
{
this.SelectedColCollection = new SelectedColCollection();
this.SelectedTables = new TableCollection();
this.CanSelelctTables = new TableCollection();
this._conditions = new JoinConditions();
this.ColCollection = new ColCollection();
this.SelectedColCollection.Context = this;
AllTables = new TableCollection();
using (DBDataContext context = Global.Context )
{
var tablecols = from table in context.SE_Table
from col in context.SE_Col
where col.ColTable == table.Exp
group new { Table = table, Col = col } by table;
foreach (var t in tablecols)
{
QueryTable table = new QueryTable(t.Key.Exp, t.Key.Show);
foreach (var c in t)
{
table.Cols.Add(new QueryCol(c.Col.ColExp, c.Col.ColShow, table, (int)c.Col.ColType, c.Col.ColStateString));
}
AllTables.Add(table);
CanSelelctTables.Add(table);
}
var joins = from j in context.SE_Join
select j;
foreach(var join in joins){
_conditions.Add(new JoinCondition(AllTables[join.Parent].Cols[join.ParentCol], AllTables[join.Child].Cols[join.ChildCol]));
}
}
}
public void Add(QueryTable Table)
{
if (this.SelectedTables.Count != 0)
{
var q = from joins in this._conditions
from tables in this.SelectedTables
where (joins.LeftCol.DataTable.Exp == Table.Exp
&& joins.RightCol.DataTable.Exp == tables.Exp)
|| (joins.RightCol.DataTable.Exp == Table.Exp
&& joins.LeftCol.DataTable.Exp == tables.Exp)
select joins;
if (q.Count() == 0)
{
throw new Exception("无法将" + Table.Show + "添加到现有查询,找不到可以关联的信息。");
}
}
this.SelectedTables.Add(Table);
this.ColCollection.AddRange(Table.Cols);
this.CanSelelctTables.Clear();
var canselect = from all in this.AllTables
from canjoin in this._conditions
from selected in this.SelectedTables
where (all.Exp == canjoin.LeftCol.DataTable.Exp
&& selected.Exp == canjoin.RightCol.DataTable.Exp)
|| (all.Exp == canjoin.RightCol.DataTable.Exp
&& selected.Exp == canjoin.LeftCol.DataTable.Exp)
select all;
foreach (var canjontable in canselect)
{
if (this.SelectedTables.Contains(canjontable) == false)
{
this.CanSelelctTables.Add(canjontable);
}
}
}
public void Clear()
{
this.SelectedTables.Clear();
this.SelectedColCollection.Clear();
this.ColCollection.Clear();
this.CanSelelctTables.Clear();
foreach (QueryTable table in AllTables)
{
this.CanSelelctTables.Add(table);
}
this.BoolExpression = null;
}
private SelectedColCollection _SelectdCoCollection;
public SelectedColCollection SelectedColCollection
{
get
{
return _SelectdCoCollection;
}
private set
{
_SelectdCoCollection = value;
}
}
private BoolExpression _BoolExp;
public BoolExpression BoolExpression
{
get
{
return _BoolExp;
}
set
{
_BoolExp = value;
}
}
/// <summary>
/// 将查询保存在数据库中
/// </summary>
public void Save()
{
throw new System.NotImplementedException();
}
public String GetSql()
{
string groupstr="";
string selectedstr="";
string tablestr="";
string exp = "";
#region 得到选择的表
if(this.SelectedTables.Count == 0)
throw new Exception ("无法查生成查询:没有选择表");
TableCollection addTables = new TableCollection();
foreach (QueryTable table in this.SelectedTables)
{
if (addTables.Count == 0)
{
tablestr = " FROM " + table.Exp;
}
else
{
var q = from addedtable in addTables
from joins in _conditions
where (addedtable.Exp == joins.LeftCol.DataTable.Exp
&& table.Exp == joins.RightCol.DataTable .Exp)
|| (addedtable.Exp == joins.RightCol.DataTable.Exp
&& table.Exp == joins.LeftCol.DataTable.Exp)
select joins;
if (q.Count() == 0)
throw new Exception("无法形成连接表");
string join = "";
foreach (var j in q)
{
if (join != "")
join = join + " AND ";
join ="["+ j.LeftCol.DataTable.Exp +"].["+j.LeftCol.Exp+ "]=["+j.RightCol .DataTable.Exp +"].["+j.RightCol.Exp +"]";
}
tablestr = tablestr + " JOIN [" + table.Exp + "] ON (" + join + ")";
}
addTables.Add(table);
}
#endregion
#region 得到选择列和分组列
var groupCols = from cols in this.SelectedColCollection
orderby cols.SelectedIndex
select cols;
if((from col in this.SelectedColCollection
where col.IsPrint == true select col ).Count()==0)
throw new Exception("无法生成查询:没有选择要输出的列");
if (groupCols.Count() > 0)
{
groupstr = "";
selectedstr = "SELECT ";
foreach (var groucol in groupCols)
{
if (groucol.IsGroup)
{
if(groupstr == "")
groupstr =" GROUP BY ";
groupstr += "[" + groucol.DataCol.DataTable.Exp + "].[" + groucol.DataCol.Exp + "],";
}
selectedstr +=groucol.SelectType.DBExp + "([" + groucol.DataCol.DataTable.Exp + "].[" + groucol.DataCol.Exp + "]) AS [" + groucol.DataCol.Show + groucol.SelectType.Show +"],";
}
if(groupstr != "")
groupstr = groupstr.Substring(0, groupstr.Length - 1);//去掉最后一 个,
selectedstr = selectedstr.Substring(0, selectedstr.Length - 1);
}
if(this.BoolExpression != null)
exp =" WHERE "+ this.BoolExpression.ToString();
#endregion
return selectedstr + tablestr + exp + groupstr;
}
}
}
其中对多表连查的条件判断已经加到里面了,到此,全部关键代码。算法都已经搞定。
至于界面是BS还是CS 各位自便。
在此贴上几张俺的CS效果图(很草,算demo吧)
主界面
串查询
数值(和串基本相同)
状态位
输出方式
仍然那句,欢迎拍砖(理性,不是急性)