CopyToDataTable:如果一个字段为NULL,为什么会抛出错误,以及如何解决它?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CopyToDataTable:如果一个字段为NULL,为什么会抛出错误,以及如何解决它?相关的知识,希望对你有一定的参考价值。
题:
我正在使用Linq.Dynamic的修改版本来根据ajax请求对数据表进行排序(请参阅下面的代码)。到目前为止,它曾经工作得很好。
但是,我有一个问题:
如果我有一个包含NULL值的数据表,即使只有一行中的一个字段为NULL,我也会得到以下两个例外:
Object must be of type "string"
如果有几个相邻的值为NULL:
At least one object must implement IComparable
这是我的代码
using System.Linq;
using System.Data;
using System.Linq.Dynamic;
// Pre:
// sidx: Sort Field
// sord: Sort order ["ASC", "DESC"]
// page: current page number
// rows: pagesize in rows
public static string TestPaging(string sidx, string sord, string page, string rows)
{
string strReturnValue = null;
using (System.Data.DataTable dtAllData = GetDataTable())
{
IQueryable<System.Data.DataRow> iqOrderedPagedData = null;
if (string.IsNullOrEmpty(sidx) || string.IsNullOrEmpty(sord))
iqOrderedPagedData = dtAllData.AsEnumerable().AsQueryable();
else
iqOrderedPagedData = dtAllData.AsEnumerable().AsQueryable().OrderBy(sidx + " " + sord);
Int32 iPageSize = string.IsNullOrEmpty(rows) ? 100 : System.Convert.ToInt32(rows);
Int32 iPageIndex = System.Convert.ToInt32(page) - 1;
iqOrderedPagedData = iqOrderedPagedData.Skip(iPageIndex * iPageSize).Take(iPageSize);
//using (System.Data.DataTable dtOrderedPagedData = MyCopyToDataTable(iqOrderedPagedData))
using (System.Data.DataTable dtOrderedPagedData = iqOrderedPagedData.CopyToDataTable())
{
cjqGrid jqGrid = new cjqGrid();
//jqGrid.total = dtAllData.Rows.Count / iPageSize + 1;
jqGrid.total = (int)Math.Ceiling((float)dtAllData.Rows.Count / (float)iPageSize);
jqGrid.page = iPageIndex + 1;
jqGrid.records = dtAllData.Rows.Count;
jqGrid.data = dtOrderedPagedData;
strReturnValue = null; // Serialize(jqGrid, true);
jqGrid = null;
} // End Using dtOrderedPagedData
} // End Using dtAllData
//Response.ContentType = "application/json";
return strReturnValue;
}
TestPaging("USR_Domain", "desc", "1", "10");
问题似乎是扩展方法CopyToDataTable,在这一行:
if (!e.MoveNext ())
这使得它对表进行排序,这意味着它在类System.Linq.SortSequenceContext 中调用函数Compare
在这一行抛出错误的地方
comparison = comparer.Compare(keys[first_index], keys[second_index]);
这里的单声道版本与我的修复程序,使单声道方法实际上工作。 但是,该错误也发生在普通的旧版MS .NET 4.0中。 (我需要mono来将我的Linq-using方法反向移植到.NET 2.0)
public static DataTable CopyToDataTable<T> (this IEnumerable<T> source)
where T : DataRow
{
DataTable dt = new DataTable ();
IEnumerator<T> e = source.GetEnumerator ();
if (!e.MoveNext ())
throw new InvalidOperationException ("The source contains no DataRows");
foreach (DataColumn col in e.Current.Table.Columns)
dt.Columns.Add (new DataColumn (col.ColumnName, col.DataType, col.Expression, col.ColumnMapping));
CopyToDataTable<T> (source, dt, LoadOption.PreserveChanges);
return dt;
}
public static void CopyToDataTable<T> (this IEnumerable<T> source, DataTable table, LoadOption options)
where T : DataRow
{
if (object.ReferenceEquals(typeof(T), typeof(System.Data.DataRow)))
{
foreach (System.Data.DataRow drRowToCopy in source)
{
System.Data.DataRow drNewRow = table.NewRow();
for (int i = 0; i < drRowToCopy.ItemArray.Length; ++i)
{
drNewRow[i] = drRowToCopy[i];
} // Next i
table.Rows.Add(drNewRow);
} // Next dr
}
else
CopyToDataTable<T>(source, table, options, null);
}
只要一行的一列中只有一个值为NULL,就会出现问题...
任何人都可以建议我如何解决这个问题? 或者,当一个或两个字段为NULL时,如何从IEnumerable中创建DataTable而不会出现异常?
现在我通过在比较中捕获异常来“修复”它。 但这只是对.NET 2.0的修复,我可以这样做,因为这个类不存在。 我需要一个适用于.NET 4.0的真正修复程序。
public override int Compare (int first_index, int second_index)
{
int comparison = 0;
try
{
comparison = comparer.Compare(keys[first_index], keys[second_index]);
}
catch (Exception ex)
{
//Console.WriteLine(ex.Message);
}
if (comparison == 0) {
if (child_context != null)
return child_context.Compare (first_index, second_index);
comparison = direction == SortDirection.Descending
? second_index - first_index
: first_index - second_index;
}
return direction == SortDirection.Descending ? -comparison : comparison;
}
PS:对于我的DataTable-OrderBy-Enhanced版本的Linq.Dynamic,请看这里: http://pastebin.com/PuqtQhfa
好的,我得到了解决方案:
很明显它与System.DbNull类型有一些东西
首先,因为我的DataTable中只有字符串,并且收到了错误消息:
Object must be of type "string"
但是字符串怎么可能不是字符串类型?当然只有它是DbNull。
如果你有两个相邻的DbNull.Values,你当然会得到:
At least one object must implement IComparable
因为DbNull没有实现IComparable。
毕竟,有趣的是,当排序列是一个具有NULL值的列时,它只会失败,但如果它是一个没有NULL值的列,它确实可以正常工作。
由于表本身包含所有空值而不管顺序如何,因此CopyToDataTable有时不起作用是不合逻辑的,因为它每次都复制所有值而不管顺序如何。
唯一合乎逻辑的结论是OrderBy在代码中调用时不会被执行,但只有在某些方法实际使用OrderBy生成的数据时才会执行。
一个快速的谷歌搜索带我到这个http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx
其中只需要读取前几行才能知道问题所在:
这篇文章介绍了最重要且经常被误解的LINQ功能之一。理解延迟执行是LINQ开发人员在希望利用这项技术的全部功能之前必须经历的仪式。
所以我突然意识到我刚刚通过通道权:)
显然,缺点是e.MoveNext()
在排序时会触发DbNull失败的比较,因为所说的DbNull没有实现IComparable。
具有讽刺意味的是,数据表也可以使用select语句进行排序,我最初并不知道(毕竟,我希望“order”方法被称为“order”,而不是“select”...)所以我刚刚将Linq.Dynamic中的OrderBy更改为
public static IQueryable OrderBy(this IQueryable source, string ordering, params object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (ordering == null) throw new ArgumentNullException("ordering");
if (object.ReferenceEquals(source.ElementType, typeof(System.Data.DataRow)))
{
using (DataTable dt = source.Cast<System.Data.DataRow>().CopyToDataTable())
{
return dt.Select("", ordering).AsQueryable();
}
瞧,虫子消失了。 而且由于它可以比使用Linq.Dynamic(它产生大约3份所有数据)更有效地单独选择过滤,所以我决定完全放弃Linq.Dynamic。 我仍在使用Linq take和skip,但是将来,我肯定会更不愿意使用Linq。
延迟执行是完全危险的,因为它会导致非常严重的可追踪错误。 “boooom”所需的只是在错误的地方的空值,或者缺少的界面(以及缺少的检查或缺少的泛型限制,如本例所示)......
以上是关于CopyToDataTable:如果一个字段为NULL,为什么会抛出错误,以及如何解决它?的主要内容,如果未能解决你的问题,请参考以下文章