微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 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())
            {
                cj@R_502_5344@ j@R_502_5344@ = new cj@R_502_5344@();
                //j@R_502[email protected] = dtAllData.Rows.Count / iPageSize + 1;
                j@R_502[email protected] = (int)Math.Ceiling((float)dtAllData.Rows.Count / (float)iPageSize);
                j@R_502[email protected] = iPageIndex + 1;
                j@R_502[email protected] = dtAllData.Rows.Count;
                j@R_502[email protected] = dtOrderedPagedData;
                strReturnValue = null; // Serialize(j@R_502_5344@,true);
                j@R_502_5344@ = null;
            } // End Using dtOrderedPagedData 

        } // End Using dtAllData

        //Response.ContentType = "application/json";
        return strReturnValue;
    }


TestPaging("USR_Domain","desc","1","10");

问题似乎是扩展方法CopyToDataTable,在这一行:

if (!e.MoveNext ())

这使得它对表进行排序,这意味着它在类中调用函数Compare
System.Linq.SortSequenceContext

在这一行抛出错误的地方

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

其中只需要读取前几行才能知道问题所在:

This post covers one of the most important and frequently
misunderstood LINQ features. Understanding deferred execution is a
rite of passage that LINQ developers must undergo before they can hope
to harness the full power of this technology.

所以我突然意识到我刚刚通过通道权:)

显然缺点是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”所需要的只是在错误的地方的空值,或者缺少接口(以及缺少检查或缺少一般限制,如本例所示)…

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐