SQL查询语句对象化的实现(C#)

henryfan1 2005-08-28 03:34:11
在开发数据库应用的过程难免会编写大量的SQL语句,其中大部份是查询语句;为不同情况编写查询语句是一件很烦琐的事件。用过hibernate或Nhibernate会了解到把SQL查询语句对象化后使用起非常方便和快捷;也大大减少在编写查询SQL语句所带来的错误等问题。
前段时间在编写一个数据处理类的时候同样遇到这个问题,经过一段时间思考和设计现实现了SQL查询语句对象化的功能;在这里我把自己小小的成果共享一下。
在讲解前先看几个例子(数据是SQLServer的Northwind)注意:例子中所涉及的除查询对象化外还包含整个数据处理类的使用,那部分还在设计和完善当中。

1)以上语句是查询订单ID为10264的订单信息
using(HFSoft.Data.IDataSession session = HFSoft.Data.DataSessionFactory.OpenSession())
{
session.Open();
HFSoft.Data.QueryCmd query = new QueryCmd("Orders");
query.Expreesion.Add(new HFSoft.Data.EqExpression("OrderID",10264));
System.Data.DataSet myDS = session.ExecuteDataSet(query.BuilderCmd(session));
}
对象生成的SQL语句:
SELECT * FROM Orders where 1=1 And (OrderID = @OrderID0)

2)以上语句是查询订单ID大于10264并且小于10600的订单信息
using(HFSoft.Data.IDataSession session = HFSoft.Data.DataSessionFactory.OpenSession())
{
session.Open();
HFSoft.Data.QueryCmd query = new QueryCmd("Orders");
query.Expreesion.Add( new HFSoft.Data.LeExpression("OrderID",10264),
new HFSoft.Data.RtExpression("OrderID",10600));
System.Data.DataSet myDS = session.ExecuteDataSet(query.BuilderCmd(session));
}
对象生成的SQL语句:
SELECT * FROM Orders where 1=1 And (OrderID > @OrderID0) And (OrderID < @OrderID1)

4)以上语句是查询订单ID大于10264并且小于10600或编号是10601,10602,10605的订单信息
using(HFSoft.Data.IDataSession session = HFSoft.Data.DataSessionFactory.OpenSession())
{
session.Open();
HFSoft.Data.QueryCmd query = new QueryCmd("Orders");
query.Expreesion.Add( new HFSoft.Data.LeExpression("OrderID",10264),
new HFSoft.Data.RtExpression("OrderID",10600));
query.Expreesion.Add(HFSoft.Data.UintType.Or,new HFSoft.Data.InExpression("OrderID",new int[]{10601,10602,10605}));
System.Data.DataSet myDS = session.ExecuteDataSet(query.BuilderCmd(session)); }
对象生成的SQL语句:
SELECT * FROM Orders where 1=1 And (OrderID > @OrderID0) And (OrderID < @OrderID1) Or (OrderID in (@OrderID20,@OrderID21,@OrderID22))

从上面的例子我们可以看到对不的条件进行数据查询只是一件很简单的事情,你并不用为不同的查询情况写相应SQL语句。
接下来讲术这个查询对象实现,对象的最终就是把不同字符串并起来生成相应的SQL语句;SQL语句查询语句主要分为以下几大部份:获取的字段,表名称 ,条件,排序,分组;了解SELECT语句的对查询语句的组成部分比较了解。
其中比较难的就是条件部分处理,因为条件的组合是情况是比较多;所以设计起来相对比较复杂。在设计的过程中把条件单独抽取出来,并生成接口对条件的表达式进行描述:
/// <summary>
/// 表达式描述接口
/// 用于SQL语句条件表达式的描述
/// </summary>
public interface IExpression
{
/// <summary>
/// 获取表达式
/// </summary>
/// <param name="driver">数据处理设备提供者</param>
/// <returns>string</returns>
string GetFilter(HFSoft.Data.IDriverType driver);
/// <summary>
/// 获取表达式相关的参数
/// </summary>
/// <param name="driver">数据处理设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
System.Data.IDataParameter[] GetDataParams(HFSoft.Data.IDriverType driver);
/// <summary>
/// 序列表识
/// 本属性用于内容处理
/// </summary>
string Sequence
{
get;
set;
}
/// <summary>
/// 添加表达式
/// </summary>
/// <param name="unittype">合并类型(or|and)</param>
/// <param name="expressions">表达式对象</param>
void Add(UintType unittype,params IExpression[] expressions );
/// <summary>
/// 添加表达式
/// </summary>
/// <param name="expressions">表达式对象</param>
void Add(params IExpression[] expressions );

}

在接口描述中有很多地方离不开HFSoft.Data.IDriverType它是用于描述数据库类型。根据HFSoft.Data.IDriverType 对应生成SqlServer,MySql,Oracle等数据库的条件表达式。
为什么IExpression具有Add方法,并且添加的对象也是IExpression;因为条件自己可以包含多个子表达式,只有这样才能够活地组合成复杂的条件表达式。
接下来看下基于这个接口的实现
/// <summary>
/// 表达式基础类
/// </summary>
[Serializable]
public class Expression:IExpression
{
private string mName;
/// <summary>
/// 获取或设置相关的字段名
/// </summary>
public string Name
{
get
{
return mName;
}
set
{
mName = value;
}
}
private object mValue;
/// <summary>
/// 获取或设置相关的字段值
/// </summary>
public object Value
{
get
{
return mValue;
}
set
{
mValue = value;
}
}
private string mSequence = "";
/// <summary>
/// 获取或设置相关标识
/// 本属性用于内部处理
/// </summary>
public string Sequence
{
get
{
return mSequence;
}
set
{
mSequence = value;
}
}
#region IExpression 成员
/// <summary>
/// 获取表达式
/// </summary>
/// <param name="driver">数据处理设备提供者</param>
/// <returns>string</returns>
public virtual string GetFilter(HFSoft.Data.IDriverType driver)
{
return " 1=1 " + GetSubString(driver);
}
/// <summary>
/// 获取表达式相关的参数
/// </summary>
/// <param name="driver">数据处理设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
public virtual System.Data.IDataParameter[] GetDataParams(HFSoft.Data.IDriverType driver)
{
return GetSubParams(driver);
}

#endregion
private System.Collections.ArrayList _Expressions = new System.Collections.ArrayList();
private System.Collections.ArrayList _Units = new System.Collections.ArrayList();
/// <summary>
/// 添加相关表达式
/// </summary>
/// <param name="expressions">表达式对象</param>
public void Add(params IExpression[] expressions )
{
Add(UintType.And,expressions);
}
/// <summary>
/// 添加相关表达式
/// </summary>
/// <param name="unittype">表达式合并类型</param>
/// <param name="expressions">表达式对象</param>
public void Add(UintType unittype,params IExpression[] expressions )
{
if(expressions != null)
foreach(IExpression exp in expressions)
{
if(exp != null)
{
_Units.Add(unittype.ToString());
exp.Sequence = this.Sequence +_Expressions.Count;
_Expressions.Add(exp);
}
}
}
/// <summary>
/// 获取内部表达式
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>string</returns>
protected string GetSubString(HFSoft.Data.IDriverType driver)
{
if(_Units.Count == 0)
return "";
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for(int i =0;i< this._Units.Count;i++)
{
sb.Append(" " +this._Units[i] +" ("+ ((IExpression)_Expressions[i]).GetFilter(driver)+")");
}
return sb.ToString();
}
/// <summary>
/// 获以内部表达式的参数值
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
protected System.Data.IDataParameter[] GetSubParams(HFSoft.Data.IDriverType driver)
{
if(_Expressions.Count ==0)
return null;
if(_Expressions.Count ==1)
{
return ((IExpression)_Expressions[0]).GetDataParams(driver);
}
System.Collections.ArrayList lst = new System.Collections.ArrayList();
foreach(IExpression exp in this._Expressions)
{
System.Data.IDataParameter[] ps = exp.GetDataParams(driver);
if(ps !=null && ps.Length >0)
{
foreach(System.Data.IDataParameter dp in ps)
{
lst.Add(dp);
}
}
}
Array array = Array.CreateInstance(typeof(System.Data.IDataParameter),lst.Count);
lst.CopyTo(array);
return array as System.Data.IDataParameter[];
}
}

...全文
177 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
cdo 2005-08-29
  • 打赏
  • 举报
回复
晕,发错贴了,不好意思.
cdo 2005-08-29
  • 打赏
  • 举报
回复
[转]
步骤:
1、添加一个新项目->选择类库模板->命名为DBCustomAction
2、单击项目右键->添加新项->选择安装程序类(命名为DBCustomAction.cs)
3、在服务器资源管理器中添加->连接到数据库->指定用户密码(选择允许保存密码)->数据库选择master
4、切换到DBCustomAction.cs的视图状态->将服务器资源管理器数据库连接中的master.dbo拖动到designer中
5、添加一个新项sql.txt(注意要使用小写),输入下列sql代码
CREATE TABLE [dbo].[MK_Employees] (
[Name] [char] (30) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[Rsvp] [int] NULL ,
[Requests] [nvarchar] (4000) COLLATE SQL_Latin1_General_CP1_CI_AS NULL
) ON [PRIMARY];

ALTER TABLE [dbo].[MK_Employees] WITH NOCHECK ADD
CONSTRAINT [PK_MK_Employees] PRIMARY KEY CLUSTERED
(
[Name]
) ON [PRIMARY];
(P.S:也可以直接用SqlServer导出)

6、在sql.txt的右键属性中->生成操作->嵌入的资源
7、将DBCustomAction.cs切换到代码视图,添加下列代码
private string GetSql(string Name)
{
try
{
Assembly Asm = Assembly.GetExecutingAssembly();
Stream strm = Asm.GetManifestResourceStream(Asm.GetName().Name + "."+Name);
StreamReader reader = new StreamReader(strm);
return reader.ReadToEnd();
}
catch (Exception ex)
{
Console.Write("In GetSql:"+ex.Message);
throw ex;
}
}

private void ExecuteSql(string DataBaseName,string Sql)
{
System.Data.SqlClient.SqlCommand Command = new System.Data.SqlClient.SqlCommand(Sql,sqlConnection1);

Command.Connection.Open();
Command.Connection.ChangeDatabase(DataBaseName);
try
{
Command.ExecuteNonQuery();
}
finally
{
Command.Connection.Close();
}
}


protected void AddDBTable(string strDBName)
{
try
{
ExecuteSql("master","CREATE DATABASE "+ strDBName);
ExecuteSql(strDBName,GetSql("sql.txt"));
}
catch(Exception ex)
{
Console.Write("In exception handler :"+ex.Message);
}
}

public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
AddDBTable(this.Context.Parameters["dbname"]);
}


8、再添加一个新项目,(选择添加到解决方案中)->项目类型为安装项目->命名为DBCustomAction Installer
9、选择应用程序文件夹->添加->项目输出->主输出
10、在方案资源管理器中->右键安装项目(DBCustomAction Installer)->视图->用户界面
11、选中启动结点->添加对话框->文本A
12、选动文本框A->右键->上移一直到最顶端
13、选择文本框A属性->修改BannerText,(Specify Database Name)
14、修改BodyText(This dialog allows you to specify the name of the database to be created on the database server. )
15、修改EditLabel1(Name of DB),修改Edit1Porperty(CUSTOMTEXTA1),将其他Edit2,3,4的Edit(2,3,4)Visible属性设为false;
16、在方案资源管理器中->右键安装项目(DBCustomAction Installer)->视图->自定义操作
17、选中安装结点->添加->双击应用程序文件夹->主输出来自DBCustomAction(活动)->右键属性->CustomActiveData属性修改为/dbname=[CUSTOMTEXTA1]
18、编译生成,OK!

注:此文章主要来自于MSDN,本来代码使用vb.net,我用C#改写了一下,完整URL请参考
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsintro7/html/vxwlkWalkthroughUsingCustomActionToCreateDatabaseDuringInstallation.asp ;
另外没有接受客户数据库服务Server-Name和Password的对话框,我想既然可以接受DBNAME参数,那么接收Server-Name和Password应该也不难.

gmfirefox 2005-08-28
  • 打赏
  • 举报
回复
henryfan1 2005-08-28
  • 打赏
  • 举报
回复
其实Expression只是一个模板类,它自己本生并没有条件处理的能力只是一个简单的1=1;下面我们根据这个模板类派生出具体表达式类型。
/// <summary>
/// 基础表达式抽象类
/// </summary>
[Serializable]
public abstract class BaseExpression:Expression
{
/// <summary>
/// 获取表达式参数对象集
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>System.Data.IDataParameter[]</returns>
public override System.Data.IDataParameter[] GetDataParams(HFSoft.Data.IDriverType driver)
{
System.Data.IDataParameter p1 = driver.GetParameter(Name+ Sequence,Value);
return ParamsConcat(new System.Data.IDataParameter[]{p1},GetSubParams(driver));
}
/// <summary>
/// 获取表达式
/// </summary>
/// <param name="driver">数据设备提供者</param>
/// <returns>string</returns>
public override string GetFilter(HFSoft.Data.IDriverType driver)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(Name);
sb.Append(GetCompareType());
sb.Append(driver.FormatNameForParameter(Name + this.Sequence));
return sb.ToString();
}
/// <summary>
/// 表达式类型
/// =,like等
/// </summary>
/// <returns>string</returns>
protected abstract string GetCompareType();


}
/// <summary>
/// 等于表达式
/// </summary>
[Serializable]
public class EqExpression:BaseExpression
{
/// <summary>
/// 构造等于表达式对象
/// </summary>
public EqExpression()
{
}
/// <summary>
/// 构造指定名称和值的等于表达式对象
/// </summary>
/// <param name="name">名称</param>
/// <param name="value">值</param>
public EqExpression(string name,object value)
{
Name = name;
Value = value;
}
/// <summary>
/// 表达式比较符
/// </summary>
/// <returns>string</returns>
protected override string GetCompareType()
{
return " = ";
}
}
整个条件对象的设计就完成了,文章代码中只有实现了等于的表达式对象;我们可以按自己情况编写更复杂的表达式。条件表达式对象在整个查询对象中是比较核心的部心,因为在整个SQL查询语句中除了这些条件外其它地方都是固定的,剩下的就是把些不同的字符串合并起来,这些东西就不详细说了大家比较了解。

110,539

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧