- >
- >
- >
今天项目出现了如下的错误:
|
REPORT: | ||
|
“/”应用程序中的服务器错误。 当前命令发生了严重错误。应放弃任何可能产生的结果。 说明: 执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。
版本信息: Microsoft .NET Framework 版本:2.0.50727.42; ASP.NET 版本:2.0.50727.210 |
出现问题的对应表DDL是:
|
CODE: |
|
CREATE TABLE [dbo].[RM_Web_Info] ( [fdID] [int] IDENTITY (1, 1) NOT NULL , [fdClassID] [int] NOT NULL , [fdTitle] [varchar] (100) COLLATE Chinese_PRC_CI_AS NOT NULL , [fdContent] [text] COLLATE Chinese_PRC_CI_AS NOT NULL , [fdCreateTime] [smalldatetime] NOT NULL , [fdModifyTime] [smalldatetime] NOT NULL ) |
问题却是出现在text类型的fdContent字段上。
google+baidu有一种原因是:SQL Server打了SP4后出现的,建议卸裁SP4。但由于win2k3+SQL Server如果没有打SP4的话,是会有安全风险的,所以这个解决方案没有使用。
仔细检查代码后发现,操作数据库时,通过下面的代码构造SQL和相应的命令:
|
CODE: |
|
string strSql = "INSERT INTO {0}({1})VALUES({2});"; string strField = ""; string strVal = ""; int i = 0; SqlDBO.SqlCmdParam[] MyParam = new SqlDBO.SqlCmdParam[hTable.Count]; foreach (DictionaryEntry dict in hTable) { int idx = Array.IndexOf(strFields, dict.Key.ToString()); if (idx != -1) { MyParam[i] = MyDBO.CreateNewParam(string.Format("@{0}", strFields[idx]), SqlDBO.GetDbType(dict.Value), dict.Value); if (this.strPKName != dict.Key.ToString()) { strField += string.Format("[{0}]", strFields[idx]) + ","; strVal += string.Format("@{0}", strFields[idx]) + ","; } i++; } } if (i > 0) { strField = strField.TrimEnd(','); strVal = strVal.TrimEnd(','); if (i < hTable.Count) MyParam = (SqlDBO.SqlCmdParam[])RM.Common.Method.Redim(MyParam, i); } strSql = string.Format(strSql, this.strTableName, strField, strVal); |
有一个SqlDBO.GetDbType(Object) 类型的方法,根据变量的类型得到SqlDbType数据。方法由如下方式得到对应的值:
|
CODE: |
|
Type varType = _Var.GetType(); switch (Type.GetTypeCode(varType)) { //这里省掉 case TypeCode.String: default: { if (_Var.ToString().Length > 8000) { return SqlDbType.Text; } else { return SqlDbType.VarChar; } } } |
结合上面的代码片断,大致的原因就可以找出来了。
上面的代码得到的SQL命令格式如:
|
CODE: |
|
exec sp_executesql N'SELECT * FROM RM_Admin_Group WHERE fdID=@fdID;', N'@fdID int', @fdID = 1 |
也就是,虽然是在构造SQL命令,但是执行时,却是调用的系统存储过程。
这是其一,另外在C#中调用字符串的Length属性,得到的是字符串,与编码无关。当字符串在8000以内时,得到的是SqlDbType.VarChar 类型。这是其二。
两者结合的结果是。在调用sql_excutesql存储过程时,给一个VarChar类型的数据最大传递8000个字符的数据。而VarChar只有ASCII字符集才能存储8000个,否则像UTF-8这样的编码,最大长度就只能存放4000个了。
将上面得到SqlDbType的代码改成如下,则测试通过:
|
CODE: |
|
Type varType = _Var.GetType(); switch (Type.GetTypeCode(varType)) { //这里省掉 case TypeCode.String: default: { if (_Var.ToString().Length > 4000) { return SqlDbType.Text; } else { return SqlDbType.VarChar; } } } |
后记:任何便利的东西都会有副作用,创建参数时,最好还是根据字段的类型选择SqlDbType的值。