数据访问层组件设计以及选型意识流 第三次封装(极致、极简而不简单)
Posted on 2012-08-06 15:27 Andrew Yin 阅读(402) 评论() 收藏 举报
首先,我们来分析SELECT 语法:
SELECT [ALL | DISTINCT] [TOP nExpr [PERCENT]] [Alias.] Select_Item
[[AS] Column_Name] [, [Alias.] Select_Item [[AS] Column_Name] ...]
FROM [FORCE] [DatabaseName!] Table [[AS] Local_Alias]
[ [INNER | LEFT [OUTER] | RIGHT [OUTER] | FULL [OUTER] JOIN DatabaseName!]
Table [[AS] Local_Alias] [ON JoinCondition ...]
[[INTO Destination] | [TO FILE FileName [ADDITIVE] | TO PRINTER [PROMPT] | TO SCREEN]]
[PREFERENCE PreferenceName] [NOCONSOLE] [PLAIN] [NOWAIT]
[WHERE JoinCondition [AND JoinCondition ...] [AND | OR FilterCondition [AND | OR FilterCondition ...]]]
[GROUP BY GroupColumn [, GroupColumn ...]] [HAVING FilterCondition] [UNION [ALL] SELECTCommand]
[ORDER BY Order_Item [ASC | DESC] [, Order_Item [ASC | DESC] ...]]
根据以上的语法,我的分析:
Distinct 一定要支持
Skip、 Top 一定要支持
Percent 很少用,可以不支持
[LEFT|RIGHT|FULL] JOIN ON 坚决不支持,用IN查询替换,然后在内存中JOIN
WHERE 多个条件组合
要支持的条件有: =, >, >=, <, <=, <>, IN, NOT IN, IS NULL, IS NOT NULL, LIKE, NOT LIKE 共12种条件
先不支持 BETWEEN, EXISTS, NOT EXSITS(都可用其他的条件替换)
Group 会影响结果集(配合各种聚合函数使用)---复杂,不实现,GROUP对表的扫描是和没GROUP一样的,只是影响网络传输而已
Having 多个条件组合---复杂,不实现,只是影响网络传输而已
Order By 支持,在分页的时候需要
注意,我们不支持OR,只支持AND,以为,OR条件可以被UNION代替,而UNION应该在内存中进行,而不是在数据库!
根据以上的分析,我写了以下的代码:
public enum WhereConditionType
{
Equal,
NotEqual,
Bigger,
NotBigger,
Smaller,
NotSmaller,
Like,
NotLike
}
public interface IWhereCondition { }
public abstract class ConditionBase
{
public object Column { get; set; }
}
public abstract class WhereConditionBase : ConditionBase, IWhereCondition
{
public WhereConditionType Type { get; set; }
public object ObjValue { get; set; }
}
public abstract class WhereInConditionBase : ConditionBase, IWhereCondition
{
public bool InOrNot { get; set; }
public IEnumerable<object> ObjValueList { get; set; }
}
public class WhereCondition<T> : WhereConditionBase
{
private T _value;
public T Value
{
get { return _value; }
set
{
ObjValue = value;
_value = value;
}
}
}
public class WhereLikeCondition : WhereCondition<string>
{
}
public class WhereInCondition<T> : WhereInConditionBase
{
private IEnumerable<T> _valueList;
public IEnumerable<T> ValueList
{
get { return _valueList; }
set
{
ObjValueList = value.Cast<object>();
_valueList = value;
}
}
}
public class WhereNullCondition : ConditionBase, IWhereCondition
{
public bool NullOrNot { get; set; }
}
public class OrderByCondition
{
public object Column { get; set; }
public bool AscOrNot { get; set; }
}
以上实现WHERE条件和ORDER BY.
然后我们写了对应的扩展方法,以及两个委托:
public static class SqlExtension
{
public static IWhereCondition SqlEqual<T>(this object column, T value)
{
return new WhereCondition<T>()
{
Column = column,
Value = value,
Type = WhereConditionType.Equal
};
}
public static IWhereCondition SqlNotEqual<T>(this object column, T value)
{
return new WhereCondition<T>()
{
Column = column,
Value = value,
Type = WhereConditionType.NotEqual
};
}
public static IWhereCondition SqlBigger<T>(this object column, T value)
{
return new WhereCondition<T>()
{
Column = column,
Value = value,
Type = WhereConditionType.Bigger
};
}
public static IWhereCondition SqlNotBigger<T>(this object column, T value)
{
return new WhereCondition<T>()
{
Column = column,
Value = value,
Type = WhereConditionType.NotBigger
};
}
public static IWhereCondition SqlSmaller<T>(this object column, T value)
{
return new WhereCondition<T>()
{
Column = column,
Value = value,
Type = WhereConditionType.Smaller
};
}
public static IWhereCondition SqlNotSmaller<T>(this object column, T value)
{
return new WhereCondition<T>()
{
Column = column,
Value = value,
Type = WhereConditionType.NotSmaller
};
}
public static IWhereCondition SqlLike(this object column, string value)
{
return new WhereLikeCondition
{
Column = column,
Value = value,
Type = WhereConditionType.Like
};
}
public static IWhereCondition SqlNotLike(this object column, string value)
{
return new WhereLikeCondition
{
Column = column,
Value = value,
Type = WhereConditionType.NotLike
};
}
public static IWhereCondition SqlIn<T>(this object column, IEnumerable<T> valueList)
{
return new WhereInCondition<T>
{
Column = column,
ValueList = valueList,
InOrNot = true
};
}
public static IWhereCondition SqlNotIn<T>(this object column, IEnumerable<T> valueList)
{
return new WhereInCondition<T>
{
Column = column,
ValueList = valueList,
InOrNot = false
};
}
public static IWhereCondition SqlNull(this object column)
{
return new WhereNullCondition
{
Column = column,
NullOrNot = true
};
}
public static IWhereCondition SqlNotNull(this object column)
{
return new WhereNullCondition
{
Column = column,
NullOrNot = false
};
}
public static OrderByCondition SqlOrderBy(this object column)
{
return new OrderByCondition
{
Column = column,
AscOrNot = true
};
}
public static OrderByCondition SqlOrderByDesc(this object column)
{
return new OrderByCondition
{
Column = column,
AscOrNot = false
};
}
}
public delegate IWhereCondition WhereDelegate<TK>(TK p);
public delegate OrderByCondition OrderByDelegate<TK>(TK p);
好吧,现在让我们一个类解决问题吧:
public class LinqDAC<T> : DACBase<T>, IEnumerable<T> where T : class, new()
{
private bool _isDistinct;
private int _skipNum = -1;
private int _tackNum = -1;
private string _cmdAlians;
private IList<IWhereCondition> _whereConditions = new List<IWhereCondition>();
private IList<OrderByCondition> _orderByConditions = new List<OrderByCondition>();
public LinqDAC<T> SqlDistinct()
{
_isDistinct = true;
return this;
}
public LinqDAC<T> SqlSkip(int skipNum)
{
_skipNum = skipNum;
return this;
}
public LinqDAC<T> SqlTack(int tackNum)
{
_tackNum = tackNum;
return this;
}
public LinqDAC<T> SqlSelect(string cmdAlians)
{
_cmdAlians = cmdAlians;
return this;
}
public LinqDAC<T> SqlWhere(Expression<WhereDelegate<T>> whereDelegate)
{
var expStr = whereDelegate.Body.ToString();
var whereCondition = whereDelegate.Compile()(new T());
var conditionBase = whereCondition as ConditionBase;
var dotIndex = expStr.IndexOf('.');
var endIndex = expStr.IndexOf('.', dotIndex + 1);
var endIndex2 = expStr.IndexOf(')');
endIndex = endIndex < endIndex2 ? endIndex : endIndex2;
if (conditionBase != null) conditionBase.Column = expStr.Substring(dotIndex + 1, endIndex - dotIndex - 1);
_whereConditions.Add(whereCondition);
return this;
}
public LinqDAC<T> SqlOrderBy(Expression<OrderByDelegate<T>> orderByDelegate)
{
var expStr = orderByDelegate.Body.ToString();
var dotIndex = expStr.IndexOf('.');
var endIndex = expStr.IndexOf('.', dotIndex + 1);
var endIndex2 = expStr.IndexOf(')');
endIndex = endIndex < endIndex2 ? endIndex : endIndex2;
var orderByCondition = orderByDelegate.Compile()(new T());
if (orderByCondition != null) orderByCondition.Column = expStr.Substring(dotIndex + 1, endIndex - dotIndex - 1);
_orderByConditions.Add(orderByCondition);
return this;
}
public IEnumerator<T> GetEnumerator()
{
//得到语句并且查询
return ToList().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IList<T> ToList()
{
string providerAlians = DataBaseInstance.ProviderAlians;
string connAlians = DataBaseInstance.ConnAlians;
AdoHelper ado = AdoHelper.CreateHelper(providerAlians);
string conStr = DbConnectionStore.TheInstance.GetConnection(connAlians);
if (string.IsNullOrEmpty(_cmdAlians))
_cmdAlians = "Select" + typeof(T).Name;
var com = AdoHelper.GetCommandInfo(connAlians, _cmdAlians);
var comtext = com.RealCommandText(typeof (T), ado, _whereConditions, _orderByConditions, _isDistinct, _skipNum, _tackNum);
List<IDataParameter> outP;
return ado.QuerySqlForList<T, string, T>(conStr, comtext, null, null, out outP,
connAlians, _cmdAlians);
}
public void SqlDelete(string cmdAlians = null)
{
_cmdAlians = cmdAlians;
string providerAlians = DataBaseInstance.ProviderAlians;
string connAlians = DataBaseInstance.ConnAlians;
AdoHelper ado = AdoHelper.CreateHelper(providerAlians);
string conStr = DbConnectionStore.TheInstance.GetConnection(connAlians);
if (string.IsNullOrEmpty(_cmdAlians))
_cmdAlians = "Delete" + typeof(T).Name;
var com = AdoHelper.GetCommandInfo(connAlians, _cmdAlians);
var comtext = com.RealCommandText(typeof(T), ado, _whereConditions, _orderByConditions);
List<IDataParameter> outP;
ado.ExecuteNonQuery(conStr, comtext, null, CommandType.Text, out outP,
connAlians, _cmdAlians);
}
public void SqlUpdate(string cmdAlians = null)
{
_cmdAlians = cmdAlians;
string providerAlians = DataBaseInstance.ProviderAlians;
string connAlians = DataBaseInstance.ConnAlians;
AdoHelper ado = AdoHelper.CreateHelper(providerAlians);
string conStr = DbConnectionStore.TheInstance.GetConnection(connAlians);
if (string.IsNullOrEmpty(_cmdAlians))
_cmdAlians = "Update" + typeof(T).Name;
var com = AdoHelper.GetCommandInfo(connAlians, _cmdAlians);
var comtext = com.RealCommandText(typeof(T), ado, _whereConditions, _orderByConditions);
List<IDataParameter> outP;
ado.ExecuteNonQuery(conStr, comtext, null, CommandType.Text, out outP,
connAlians, _cmdAlians);
}
public void Clear()
{
_whereConditions = new List<IWhereCondition>();
_orderByConditions = new List<OrderByCondition>();
_isDistinct = false;
_skipNum = -1;
_tackNum = -1;
_cmdAlians = null;
}
}
注意到了么?我们继承了之前的DACBase<T>类, 还实现了IEnumerable<T>接口。支持延迟查询哦!
SqlDistinct 是否要Distinct
SqlSkip 跳过多少记录
SqlTack 返回多少记录
SqlSkip + SqlTack 完美支持分页哦
SqlSelect 指定一个命令执行,默认的是Select + T的类型名
SqlWhere 加上各种Where 条件(支持12种条件)
SqlOrderBy 加上排序,支持倒序
GetEnumerator 返回迭代器,支持延迟查询
ToList 立即查询
SqlDelete 执行删除
SqlUpdate 执行更新
Clear 把类置于初始状态,已执行下一个命令
本组件的缺点,很明显,在上面的分析部分已经讲明白了,不支持的就是不支持,我不打算去做数据库关系的MAPPING。
但是你说这个东西不能用,DAAB都能做的事它都能做,并且比DAAB好多了吧。
那么,我们看看它的使用方式吧:
var dac = DependencyResolver.UnityDependencyResolver.Resolve<LinqDAC<ShopInfo>>();
var retList = new List<ShopInfo>();
var list = dac.SqlWhere(p => p.ShopID.SqlEqual(context.InnerRequest))
//.SqlDistinct().SqlTack(1).SqlSkip(0)
.ToList()
和EF差不多吧。
具体使用指南将在专门的使用指南里介绍,稍后我将上传全部源码。
回过都来,我们来看看它的短板的替代方案:
第一,JOIN 查询不支持, 没关系,前面以前说了,用IN查询来限定第二个结果集的大小,然后在内存里对两个结果集进行JOIN
第二,Group BY, 直接使用内存里的Group BY。
来看看丰富的集合操作的扩展方法吧:

对以上扩展方法的分析:
Aggregate 聚合函数
Aggregate 加上闭包传进列表长度能得到平均值
Aggregate 在RROUP BY 中特别好用
ANY, ALL 返回BOOL
CONTAINS 指定一个比较器, 默认的构造器模板,传进一个委托返回BOOL 与ANY用法有相同之处
Avg只能返回一个DECIMAL,用 Aggregate 更好
SUM
COUNT可带条件, 用 Aggregate 可实现
LongCount
MIN/MAXCANCAT 不带条件
Distinct 带比较器
UNION 带比较器 = CANCAT + Distinct
Except 带比较器
DefaultOrEmpty在实现LEFT Join的时候好用啊!!!
First带条件
FirstORDefault可实现DefaultIFEmpty,但更强大
Last
LastOrDefault
Single 不止一个就报错
SingleOrDefault
ElementAt
ElementAtDefault
ToDictnary
ToLookUp
ToArray
ToList
WHERE
Tack
Skip
SkipWhile
CAST
OfType
Reverse
SequenceEqual在自动化测试中很好用,带比较器
ORDERBY 正序和倒序
GROUPBY
GROUPJOIN
JOIN,条件为TRUE可实现笛卡尔乘积
INTERSECT 取交集
SELECT
SELECTMANY 可实现LEFT JOIN,笛卡尔乘积,LEFT JOIN 和 RIGHT join 加起来UNION就是 FULL JOIN
可见,集合操作的扩展方法是极其丰富的,没有问题。
好,接下来的问题是,用IN 查询实现 限定JOIN查询的 第二个记录集的大小有没有性能问题。
我们知道一个in后面只能有1000个值
如果我们JION两张表,一张是15万的数据,另一张是2亿的数据
我们就要用150次IN查询 ,并且把结果UNION起来,好在我已经给你做了,你不需要写一个 for(var i=0, i <150, i++){}
现在只是需要确认一下性能和原本的JOIN查询差距有多大~~
一会出性能测试报告,敬请期待 。
PS: 测试结果出来的, IN查询的效率肯定是比JOIN 低的。究竟低多少呢?
6.2万 join 2亿, JOIN是0.015秒,
IN查询是0.46秒,
大数据量,差的比较多了,不过,小数据量就完全没区别了。
大数据量的情况下,查询结果其实都没有返回的,网络不行啊!
上面的黄色高亮是本组件的短板,也就是缺点了~~
好了,终于写完了,算是对自己大约一个星期工作量的总结,写完了就对得起自己的熬夜了,也对得起公司,对得起大家了。
我真TM是个偏执的人!哈哈
PS, 上一篇中我可能犯了两个错误,以个人感受概全了大家的感受,以偏概全了:
一,也许我错了,让销售录入信息可能是只有我这样的懒销售才感到头痛,大部分销售是不会头痛的,他们可以一边谈客户一边录入,可以在家打开笔记本录入~~
二,也许我又错了,一个系统里应该可以存在两个以及以上的数据访问组件,然后优势互补
本系列四篇博文,只是做自己的工作总结,和发表自己的个人看法~不带其他感情色彩哦~~读者,切记切记
























