之前我写了3篇关于表达式树解析的文章
干货!表达式树解析"框架"(1)
干货!表达式树解析"框架"(2)
干货!表达式树解析"框架"(3)
这3篇文章更多的是说明一种方法,一种思路,而代码比较少
在发出来之后也有些朋友互加了好友一起讨论
在经过一些时间的修改和优化后,就有了现在这个框架
目前这个框架实现了SqlServer和Oracle的解释
其他数据库1来是不熟2来没时间3来我更希望大家可以使用这个框架自己动手实现一个数据库的解析,非常有意思
Where(u => u.Birthday.Day == 1); //EXTRACT(DAY FROM a.BIRTHDAY) = 1Where(u => u.Name.Trim() == ""); //ltrim(rtrim(a.NAME)) = :auto_p0Where(u => string.IsNullOrEmpty(u.Name)); //a.NAME IS NULL OR a.NAME == ''
灵活 : 在轻量的基础上尽量做到了灵活,可以非常方便的解析各种C#的方法
void Where(string expected, Expression<Func<User, bool>> expr){ var parse = Faller.Create(expr); //通过LambdaExpression创建Faller对象 var sql = parse.ToWhere(OracleSaw.Instance);//使用特定的ISaw来格式化Sql语句 //parse.Parameters //解析过程中产生的参数 Assert.AreEqual(expected, sql);}
// 轻量级表达式树解析器 接口 public interface IFaller{ // 将表达式转为Where语句,不包含Where关键字 //saw: 自定义输出Sql语句的格式的对象 string ToWhere(ISaw saw); // 将表达式转为OrderBy语句,不包含OrderBy关键字 //saw: 自定义输出Sql语句的格式的对象 //asc: 正序或倒序标识 string ToOrderBy(ISaw saw, bool asc); // 将表达式转为Update中Set语句,不包含Set关键字 //saw: 自定义输出Sql语句的格式的对象 string ToSets(ISaw saw); // 将表达式转为select语句中的列或列集合的Sql语句 //saw: 自定义输出Sql语句的格式的对象 string ToSelectColumns(ISaw saw); // 将表达式转为值或值集合的sql语句 //saw: 自定义输出Sql语句的格式的对象 string ToValues(ISaw saw); // 将表达式转为值或值集合的sql语句 //saw: 自定义输出Sql语句的格式的对象 string ToValues(ISaw saw, Func<string, string> replace); // 将表达式转为列集合和值集合2个sql语句,可用于拼装insert语句 //saw: 自定义输出Sql语句的格式的对象 KeyValuePair<string, string> ToColumnsAndValues(ISaw saw); // 转换Sql语句过程中产生的参数 ICollection<DbParameter> Parameters { get; }}
/// <summary> 定义一组方法,它支持自定义Sql语句的格式。 /// </summary> public interface ISaw { /// <summary> 解释二元操作 /// </summary> /// <param name="left">左元素</param> /// <param name="Operator">二元操作符</param> /// <param name="right">右元素</param> string BinaryOperation(string left, BinaryOperator @operator, string right); /// <summary> 解释Contains操作 /// </summary> /// <param name="not">是否为not</param> /// <param name="item">要在集合中查找的对象</param> /// <param name="array">要查找的集合</param> string ContainsOperation(bool not, string item, string[] array); /// <summary> 向参数集合中追加一个任意类型的参数,并返回参数名sql表达式 /// </summary> /// <param name="obj">需要追加的参数值</param> /// <param name="parameters">参数集合</param> string AddObject(object obj, ICollection<DbParameter> parameters); /// <summary> 向参数集合中追加一个数字类型的参数,并返回参数名sql表达式 /// </summary> /// <param name="number">需要追加的数字</param> /// <param name="parameters">参数集合</param> string AddNumber(IConvertible number, ICollection<DbParameter> parameters); /// <summary> 向参数集合中追加一个布尔类型的参数,并返回参数名sql表达式 /// </summary> /// <param name="obj">需要追加的布尔值</param> /// <param name="parameters">参数集合</param> string AddBoolean(bool value, ICollection<DbParameter> parameters); /// <summary> 向参数集合中追加当前时间,并返回参数名sql表达式 /// </summary> /// <param name="parameters">参数集合</param> string AddTimeNow(ICollection<DbParameter> parameters); /// <summary> 获取实体类型所映射的表名 /// </summary> /// <param name="type">实体类型</param> /// <param name="alias">别名</param> string GetTable(Type type, string alias); /// <summary> 获取实体属性或字段所映射的列名 /// </summary> /// <param name="table">表名或表别名</param> /// <param name="type">实体属性或字段</param> string GetColumn(string table, MemberInfo member); /// <summary> 获取列名和列别名组合后的sql表达式 /// </summary> /// <param name="columnName">列名</param> /// <param name="alias">列别名</param> string GetColumn(string columnName, string alias); /// <summary> 将.NET中的方法解释为sql表达式 /// </summary> /// <param name="method">需解释的方法</param> /// <param name="target">方法调用者</param> /// <param name="args">方法参数</param> /// <returns></returns> string ParseMethod(MethodInfo method, SawDust target, SawDust[] args); }
/// <summary> 表达式树解析结果 /// </summary> public struct SawDust { /// <summary> 结果类型 /// </summary> public readonly DustType Type; /// <summary> 结果值 /// </summary> public readonly Object Value; /// <summary> 无论结果类型 强制转换为Sql语句,DustType.Undefined抛出异常 /// </summary> public string ToSql(); /// <summary> 是否是非DustType.Sql和DustType.Undefined类型 /// </summary> public bool IsObject { get;} }
/// <summary> 表达式解析结果类型 /// </summary> [System.Serializable] public enum DustType { /// <summary> 未知 /// </summary> Undefined = 0, /// <summary> sql语句 /// </summary> Sql = 1, /// <summary> 未知类型 /// </summary> Object = 2, /// <summary> 数字 /// </summary> Number = 3, /// <summary> 数组 /// </summary> Array = 4, /// <summary> 布尔值 /// </summary> Boolean = 5, /// <summary> 时间 /// </summary> DateTime = 6, /// <summary> 二进制 /// </summary> Binary = 7, /// <summary> 字符串 /// </summary> String = 8, }
/// <summary> Sql表达式 /// </summary> public struct SqlExpr { public static explicit operator SqlExpr(string value); public static implicit operator bool(SqlExpr value); public static implicit operator byte(SqlExpr value); public static implicit operator char(SqlExpr value); public static implicit operator DateTime(SqlExpr value); public static implicit operator decimal(SqlExpr value); public static implicit operator double(SqlExpr value); public static implicit operator short(SqlExpr value); public static implicit operator int(SqlExpr value); public static implicit operator long(SqlExpr value); public static implicit operator sbyte(SqlExpr value); public static implicit operator float(SqlExpr value); public static implicit operator string(SqlExpr value); public static implicit operator ushort(SqlExpr value); public static implicit operator uint(SqlExpr value); public static implicit operator ulong(SqlExpr value); public static implicit operator Guid(SqlExpr value); public static implicit operator Byte[](SqlExpr value); }
/// <summary> 支持自定义Sql语句格式基类。 /// </summary> public abstract class BaseSaw : ISaw { /// <summary> 初始化并提供 DbProviderFactory /// </summary> /// <param name="factory"></param> protected BaseSaw(DbProviderFactory factory); /* * 接口已定义方法略... */ /// <summary> 解释未知的方法 /// </summary> /// <param name="method">需解释的方法</param> /// <param name="target">方法调用者</param> /// <param name="args">方法参数</param> protected virtual string ParseUnknownMethod(MethodInfo method, SawDust target, SawDust[] args); /// <summary> 获取新的数据库参数 /// </summary> /// <param name="obj">参数值</param> protected virtual DbParameter GetDbParameter(object obj); /// <summary> 别名分隔符 默认 AS ,如 Table AS it /// </summary> protected virtual string AliasSeparator { get; } /// <summary> 解释按位运算 与,或,非 /// </summary> /// <param name="val1">操作数1</param> /// <param name="val2">操作数2</param> /// <param name="opt">按位操作符</param> protected virtual string BitOperation(string val1, string val2, BitOperator opt); /// <summary> 解释取余运算 /// </summary> /// <param name="val1">操作数1</param> /// <param name="val2">操作数2</param> /// <returns></returns> protected virtual string ModuloOperation(string val1, string val2); /// <summary> 解释幂运算 /// </summary> /// <param name="val1">操作数1</param> /// <param name="val2">操作数2</param> /// <returns></returns> protected virtual string PowerOperation(string val1, string val2); /// <summary> 解释位移运算 /// </summary> /// <param name="val1">操作数1</param> /// <param name="val2">操作数2</param> /// <param name="opt">按位操作符</param> protected virtual string ShiftOperation(string val1, string val2, ShiftOperator opt); /// <summary> 解释 String.Trim 方法 /// </summary> /// <param name="target">方法调用者</param> /// <param name="arg">方法参数</param> protected virtual string StringTrim(string target, string arg); /// <summary> 解释 String.TrimEnd 方法 /// </summary> /// <param name="target">方法调用者</param> /// <param name="arg">方法参数</param> protected virtual string StringTrimEnd(string target, string arg); /// <summary> 解释 String.TrimStart 方法 /// </summary> /// <param name="target">方法调用者</param> /// <param name="arg">方法参数</param> protected virtual string StringTrimStart(string target, string arg); /// <summary> 解释 String.IsNullOrEmpty 方法 /// </summary> /// <param name="target">方法调用者</param> protected virtual string StringIsNullOrEmpty(string target); /// <summary> 解释 String.IsNullOrWhiteSpace 方法 /// </summary> /// <param name="target">方法调用者</param> protected virtual string StringIsNullOrWhiteSpace(string target); /// <summary> 解释 String.Length 方法 /// </summary> /// <param name="target">方法调用者</param> protected virtual string StringLength(string target); /// <summary> 解释 Object.ToString 方法 /// </summary> /// <param name="type">调用者类型</param> /// <param name="target">方法调用者</param> /// <param name="format">格式化参数</param> protected virtual string ObjectToString(DustType type, string target, string format); /// <summary> 解释 DateTime 中的数据 /// </summary> /// <param name="datetime">方法调用者</param> protected virtual string DateTimeToField(string datetime, DateTimeField field); /// <summary> 解释各种 Number.Parse 方法 /// </summary> /// <param name="target">string对象</param> protected virtual string SrtingToNumber(string target); #region abstract /// <summary> 返回实体类型所映射的表名 /// </summary> /// <param name="type">实体类型</param> protected abstract string TableName(Type type); /// <summary> 返回实体属性或字段所映射的列名 /// </summary> /// <param name="member">实体属性或字段</param> protected abstract string ColumnName(MemberInfo member); /// <summary> 参数的前缀符号,如SqlServer中的@,Oracle中的: /// </summary> protected abstract string ParameterPreFix { get; } /// <summary> 在数据库中表示当前时间的表达式,如SqlServer中的getdate,Oracle中的SYSDATE /// </summary> protected abstract string TimeNow { get; } /// <summary> 解释Like操作 /// </summary> /// <param name="val1">操作数1</param> /// <param name="val2">操作数2</param> /// <param name="opt">按位操作符</param> protected abstract string LikeOperation(string val1, string val2, LikeOperator opt); #endregion }
/// <summary> 二元操作符枚举 /// </summary> [Serializable] public enum BinaryOperator { /// <summary> 加 /// </summary> Add, /// <summary> 减 /// </summary> Subtract, /// <summary> 除 /// </summary> Divide, /// <summary> 取余 /// </summary> Modulo, /// <summary> 乘 /// </summary> Multiply, /// <summary> 幂运算 /// </summary> Power, /// <summary> 位移运算 a << b /// </summary> LeftShift, /// <summary> 位移运算 a >> b /// </summary> RightShift, /// <summary> 与 a && b 或 a AND b /// </summary> And, /// <summary> 或 a || b 或 a OR b /// </summary> Or, /// <summary> 等于 a == b /// </summary> Equal, /// <summary> 不等于 a != b 或 a <> b /// </summary> NotEqual, /// <summary> 大于 a > b /// </summary> GreaterThan, /// <summary> 大于等于 a >= b /// </summary> GreaterThanOrEqual, /// <summary> 小于 a < b /// </summary> LessThan, /// <summary> 小于等于 a <= b /// </summary> LessThanOrEqual, /// <summary> LIKE操作 a LIKE %b% /// </summary> Contains, /// <summary> LIKE操作 a LIKE b% /// </summary> StartWith, /// <summary> LIKE操作 a LIKE %b /// </summary> EndWith, /// <summary> LIKE操作 a NOT LIKE %b% /// </summary> NotContains, /// <summary> LIKE操作 a NOT LIKE b% /// </summary> NotStartWith, /// <summary> LIKE操作 a NOT LIKE %b /// </summary> NotEndWith, /// <summary> 位运算 如 a & b /// </summary> BitAnd, /// <summary> 位运算 如 a | b /// </summary> BitOr, /// <summary> 位运算 如 a ^ b /// </summary> BitXor, } /// <summary> 按位操作符枚举 /// </summary> [Serializable] public enum BitOperator { /// <summary> 位运算 如 a & b /// </summary> And, /// <summary> 位运算 如 a | b /// </summary> Or, /// <summary> 位运算 如 a ^ b /// </summary> Xor, } /// <summary> Like操作符枚举 /// </summary> [Serializable] public enum LikeOperator { /// <summary> LIKE操作 a LIKE %b% /// </summary> Contains, /// <summary> LIKE操作 a LIKE b% /// </summary> StartWith, /// <summary> LIKE操作 a LIKE %b /// </summary> EndWith, /// <summary> LIKE操作 a NOT LIKE %b% /// </summary> NotContains, /// <summary> LIKE操作 a NOT LIKE b% /// </summary> NotStartWith, /// <summary> LIKE操作 a NOT LIKE %b /// </summary> NotEndWith, } /// <summary> 位移操作符枚举 /// </summary> [Serializable] public enum ShiftOperator { /// <summary> 位移运算 a << b /// </summary> Left, /// <summary> 位移运算 a >> b /// </summary> Right, } /// <summary> 时间字段 /// </summary> [Serializable] public enum DateTimeField { /// <summary> 年 /// </summary> Year, /// <summary> 月 /// </summary> Month, /// <summary> 日 /// </summary> Day, /// <summary> 时 /// </summary> Hour, /// <summary> 分 /// </summary> Minute, /// <summary> 秒 /// </summary> Second, /// <summary> 星期 /// </summary> Week, }
再来一个已实现的MsSqlSaw
public class MsSqlSaw : BaseSaw { public static MsSqlSaw Instance = new MsSqlSaw(); private MsSqlSaw() : base(System.Data.SqlClient.SqlClientFactory.Instance) { } private static HashSet<string> KeyWords = InitKeyWords(); private static HashSet<string> InitKeyWords() { return new HashSet<string>("ABSOLUTE,ACTION,ADA,ADD,ADMIN,AFTER,AGGREGATE,ALIAS,ALL,ALLOCATE,ALTER,AND,ANY,ARE,ARRAY,AS,ASC,ASSERTION,AT,AUTHORIZATION,AVG,BACKUP,BEFORE,BEGIN,BETWEEN,BINARY,BIT,BIT_LENGTH,BLOB,BOOLEAN,BOTH,BREADTH,BREAK,BROWSE,BULK,BY,CALL,CASCADE,CASCADED,CASE,CAST,CATALOG,CHAR,CHARACTER,CHARACTER_LENGTH,CHAR_LENGTH,CHECK,CHECKPOINT,CLASS,CLOB,CLOSE,CLUSTERED,COALESCE,COLLATE,COLLATION,COLUMN,COMMIT,COMPLETION,COMPUTE,CONNECT,CONNECTION,CONSTRAINT,CONSTRAINTS,CONSTRUCTOR,CONTAINS,CONTAINSTABLE,CONTINUE,CONVERT,CORRESPONDING,COUNT,CREATE,CROSS,CUBE,CURRENT,CURRENT_DATE,CURRENT_PATH,CURRENT_ROLE,CURRENT_TIME,CURRENT_TIMESTAMP,CURRENT_USER,CURSOR,CYCLE,DATA,DATABASE,DATE,DAY,DBCC,DEALLOCATE,DEC,DECIMAL,DECLARE,DEFAULT,DEFERRABLE,DEFERRED,DELETE,DENY,DEPTH,DEREF,DESC,DESCRIBE,DESCRIPTOR,DESTROY,DESTRUCTOR,DETERMINISTIC,DIAGNOSTICS,DICTIONARY,DISCONNECT,DISK,DISTINCT,DISTRIBUTED,DOMAIN,DOUBLE,DROP,DUMMY,DUMP,DYNAMIC,EACH,ELSE,END,END-EXEC,EQUALS,ERRLVL,ESCAPE,EVERY,EXCEPT,EXCEPTION,EXEC,EXECUTE,EXISTS,EXIT,EXTERNAL,EXTRACT,FALSE,FETCH,FILE,FILLFACTOR,FIRST,FLASE,FLOAT,FOR,FOREIGN,FORTRAN,FOUND,FREE,FREETEXT,FREETEXTTABLE,FROM,FULL,FUNCTION,GENERAL,GET,GLOBAL,GO,GOTO,GRANT,GROUP,GROUPING,HAVING,HOLDLOCK,HOST,HOUR,IDENTITY,IDENTITYCOL,IDENTITY_INSERT,IF,IGNORE,IMMEDIATE,IN,INCLUDE,INDEX,INDICATOR,INITIALIZE,INITIALLY,INNER,INOUT,INPUT,INSENSITIVE,INSERT,INT,INTEGER,INTERSECT,INTERVAL,INTO,IS,ISOLATION,ITERATE,JOIN,KEY,KILL,LANGUAGE,LARGE,LAST,LATERAL,LEADING,LEFT,LESS,LEVEL,LIKE,LIMIT,LINENO,LOAD,LOCAL,LOCALTIME,LOCALTIMESTAMP,LOCATOR,LOWER,MAP,MATCH,MAX,MIN,MINUTE,MODIFIES,MODIFY,MODULE,MONTH,NAMES,NATIONAL,NATURAL,NCHAR,NCLOB,NEW,NEXT,NO,NOCHECK,NONCLUSTERED,NONE,NOT,NULL,NULLIF,NUMERIC,OBJECT,OCTET_LENGTH,OF,OFF,OFFSETS,OLD,ON,ONLY,OPEN,OPENDATASOURCE,OPENQUERY,OPENROWSET,OPENxml,OPERATION,OPTION,OR,ORDER,ORDINALITY,OUT,OUTER,OUTPUT,OVER,OVERLAPS,PAD,PARAMETER,PARAMETERS,PARTIAL,PASCAL,PATH,PERCENT,PLAN,POSITION,POSTFIX,PRECISION,PREFIX,PREORDER,PREPARE,PRESERVE,PRIMARY,PRINT,PRIOR,PRIVILEGES,PROC,PROCEDURE,PUBLIC,RAISERROR,READ,READS,READTEXT,REAL,RECONFIGURE,RECURSIVE,REF,REFERENCES,REFERENCING,RELATIVE,REPLICATION,RESTORE,RESTRICT,RESULT,RETURN,RETURNS,REVOKE,RIGHT,ROLE,ROLLBACK,ROLLUP,ROUTINE,ROW,ROWCOUNT,ROWGUIDCOL,ROWS,RULE,SAVE,SAVEPOINT,SCHEMA,SCOPE,SCROLL,SEARCH,SECOND,SECTION,SELECT,SEQUENCE,session,SESSION_USER,SET,SETS,SETUSER,SHUTDOWN,SIZE,SMALLINT,SOME,SPACE,SPECIFIC,SPECIFICTYPE,SQL,SQLCA,SQLCODE,SQLERROR,SQLEXCEPTION,SQLSTATE,SQLWARNING,START,STATE,STATEMENT,STATIC,STATISTICS,STRUCTURE,SUBSTRING,SUM,SYSTEM_USER,TABLE,TEMPORARY,TERMINATE,TEXTSIZE,THAN,THEN,TIME,TIMESTAMP,TIMEZONE_HOUR,TIMEZONE_MINUTE,TO,TOP,TRAILING,TRAN,TRANSACTION,TRANSLATE,TRANSLATION,TREAT,TRIGGER,TRIM,TRUE,TRUNCATE,TSEQUAL,UNDER,UNION,UNIQUE,UNKNOWN,UNNEST,UPDATE,UPDATETEXT,UPPER,USAGE,USE,USER,USING,VALUE,VALUES,VARCHAR,VARIABLE,VARYING,VIEW,WAITFOR,WHEN,WHENEVER,WHERE,WHILE,WITH,WITHOUT,WORK,WRITE,WRITETEXT,YEAR,ZONE".Split(','), StringComparer.OrdinalIgnoreCase); } protected override string TableName(Type type) { var name = type.Name; if (KeyWords.Contains(name)) { return string.Concat("[", name, "]"); } return name; } protected override string ColumnName(MemberInfo member) { var name = member.Name; if (KeyWords.Contains(name)) { return string.Concat("[", name, "]"); } return name; } protected override string ParameterPreFix { get { return "@"; } } protected override string TimeNow { get { return "GETDATE()"; } } protected override string LikeOperation(string val1, string val2, LikeOperator opt) { switch (opt) { case LikeOperator.Contains: return string.Concat(val1, " LIKE '%' + ", val2, " + '%'"); case LikeOperator.StartWith: return string.Concat(val1, " LIKE ", val2, " + '%'"); case LikeOperator.EndWith: return string.Concat(val1, " LIKE '%' + ", val2); case LikeOperator.NotContains: return string.Concat(val1, " NOT LIKE '%' + ", val2, " + '%'"); case LikeOperator.NotStartWith: return string.Concat(val1, " NOT LIKE ", val2, " + '%'"); case LikeOperator.NotEndWith: return string.Concat(val1, " NOT LIKE '%' + ", val2); default: throw new ArgumentOutOfRangeException("opt"); } } protected override string BitOperation(string val1, string val2, BitOperator opt) { switch (opt) { case BitOperator.And: return string.Concat(val1, " & ", val2); case BitOperator.Or: return string.Concat(val1, " | ", val2); case BitOperator.Xor: return string.Concat(val1, " ^ ", val2); default: throw new ArgumentOutOfRangeException("opt"); } } protected override string StringTrim(string target, string arg) { if (arg != null) { throw new NotSupportedException("不支持参数"); } return string.Concat("LTRIM(RTRIM(", target, "))"); } protected override string StringTrimEnd(string target, string arg) { if (arg != null) { throw new NotSupportedException("不支持参数"); } return string.Concat("RTRIM(", target, ")"); } protected override string StringTrimStart(string target, string arg) { if (arg != null) { throw new NotSupportedException("不支持参数"); } return string.Concat("LTRIM(", target, ")"); } protected override string StringLength(string target) { return string.Concat("LEN(", target, ")"); } protected override string SrtingToNumber(string target) { return string.Concat("CAST(", target, " as decimal)"); } protected override string ObjectToString(DustType type, string target, string format) { switch (type) { case DustType.Undefined: break; case DustType.Sql: break; case DustType.Object: break; case DustType.Number: if (format != null) { throw new NotSupportedException("不支持参数"); } return string.Concat("CAST(", target, " as NVARCHAR)"); case DustType.Array: break; case DustType.Boolean: break; case DustType.DateTime: switch (format) { case "'HH:mm'": return string.Concat("CONVERT(VARCHAR(5),", target, ",114)"); case "'HH:mm:ss'": return string.Concat("CONVERT(VARCHAR(8),", target, ",114)"); case "'HH:mm:ss.fff'": return string.Concat("CONVERT(VARCHAR(12),", target, ",114)"); case "'yyyy-MM-dd'": return string.Concat("CONVERT(VARCHAR(10),", target, ",21)"); case "'yyyy-MM-dd HH:mm'": return string.Concat("CONVERT(VARCHAR(16),", target, ",21)"); case "'yyyy-MM-dd HH:mm:ss'": return string.Concat("CONVERT(VARCHAR(19),", target, ",21)"); case "'yyyy-MM-dd HH:mm:ss.fff'": return string.Concat("CONVERT(VARCHAR(23),", target, ",21)"); case "'yy/MM/dd'": return string.Concat("CONVERT(VARCHAR(8),", target, ",111)"); case "'yyMMdd'": return string.Concat("CONVERT(VARCHAR(6),", target, ",112)"); default: break; } throw new NotSupportedException("不支持当前参数"); case DustType.Binary: break; case DustType.String: break; default: break; } return base.ObjectToString(type, target, format); } protected override string DateTimeToField(string datetime, DateTimeField field) { switch (field) { case DateTimeField.Year: return string.Concat("DATEPART(YYYY, ", datetime, ")"); case DateTimeField.Month: return string.Concat("DATEPART(mm, ", datetime, ")"); case DateTimeField.Day: return string.Concat("DATEPART(dd, ", datetime, ")"); case DateTimeField.Hour: return string.Concat("DATEPART(hh, ", datetime, ")"); case DateTimeField.Minute: return string.Concat("DATEPART(mi, ", datetime, ")"); case DateTimeField.Second: return string.Concat("DATEPART(ss, ", datetime, ")"); case DateTimeField.Week: return string.Concat("(DATEPART(w, ", datetime, ") - 1)"); default: throw new ArgumentOutOfRangeException("field"); } } }
https://code.csdn.net/jy02305022/blqw-faller
对C#感兴趣的朋友可以来群里讨论,入群方式见签名
新闻热点
疑难解答