提问人:Patashu 提问时间:9/28/2022 最后编辑:jarlhPatashu 更新时间:9/28/2022 访问量:75
SqlGeography 不实现 IComparable 接口,从而阻止 DataSet.RejectChanges() 成功
SqlGeography does not implement IComparable interface, preventing DataSet.RejectChanges() from succeeding
问:
我正在开发一个具有 SQL 后端的企业应用程序,并使用 System.Data.DataSet 等在 C# 中表示该数据。
以前,我们在将 SqlGeography 放入 DataTable 之前将其转换为 DbGeography,一切正常,直到我们发现对某些浮点值进行此往返操作具有微小的不精确性,实际上会影响基础字节,因此我们必须放弃 DbGeography 并将其始终保留为 SqlGeography。这主要有效,除了显示停止器。dataSet.RejectChanges() 现在由于以下异常而不起作用:
------------System.Data.DataException: Type 'Microsoft.SqlServer.Types.SqlGeography, Microsoft.SqlServer.Types, Version=11.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' does not implement IComparable interface. Comparison cannot be done.
at System.Data.Common.SqlUdtStorage.CompareValueTo(Int32 recordNo1, Object value)at System.Data.Index.CompareRecords(Int32 record1, Int32 record2)
at System.Data.RBTree`1.RBInsert(Int32 root_id, Int32 x_id, Int32 mainTreeNodeID, Int32 position, Boolean append)
at System.Data.Index.InitRecords(IFilter filter)
at System.Data.DataTable.GetIndex(IndexField[] indexDesc, DataViewRowState recordStates, IFilter rowFilter)
at System.Data.DataColumn.get_SortIndex()
at System.Data.DataColumn.IsNotAllowDBNullViolated()
at System.Data.DataSet.EnableConstraints()
at System.Data.DataSet.set_EnforceConstraints(Boolean value)
at System.Data.DataSet.RejectChanges()
at CargoWise.EntityFramework.RowFactory.Rollback() in
我检查了这曾经是如何用于 DbGeography 的。它之所以有效,是因为它不是 SQL 类型(没有实现 INullable,请参阅 System.Data.Common.DataStorage CreateStorage),因此它创建了一个 ObjectStorage 而不是 SqlUdtStorage。对于未实现 IComparable 的数据类型,ObjectStorage 最终会求助于 doing。ToString() 来比较它们。在这种情况下,这已经足够了,因为我们只需要知道 'null 还是 not null?) IsNotAllowDBNullViolated() 就可以工作(据我所知,无论如何,这没有导致任何缺陷)。但是,如果未实现 IComparable,则 SqlUdtStorage 没有回退 - 它会引发异常。
这真的感觉像是“这对任何人来说都是如何工作的?”和“谷歌历史上怎么没有其他人遇到过这个错误?”的情况之一。无论如何,我正在考虑我的选择,到目前为止,事情看起来很黯淡。
我不能子类化 SqlUdtStorage,它是内部密封的。我也不能子类化 SqlGeography,它是内部密封的。我可以围绕 SqlGeography 制作一个薄包装器来实现 IComparable,并将其用作 DataColumn 的类型而不是 SqlGeography,我认为这会起作用。我还在考虑这样一种可能性,每当我制作一个具有 SqlGeography 类型的 DataColumn(必须找到代码库中的所有位置)时,使用反射来构造具有正确参数的 ObjectStorage 并将其分配给DataColumn._storage。我可以简单地让所有 SqlGeography 列都为空,但我不确定这有多容易/是否会导致任何缺陷。
但所有这些想法都有一个共同点——它们感觉太笨拙了,无法成为解决这个问题的预期方法。有没有预定的方法?如果没有,最干净的解决方法是什么?
答:
我最终使用了“通过反射将_storage字段更改为 ObjectStorage”解决方案。代码如下。请注意,它必须在将 DataColumn 添加到 DataTable 之后发生,因为这样做的行为会_storage无效。
destTable.Columns.Add(dataColumn);
//hack to fix https://stackoverflow.com/questions/73875363/sqlgeography-does-not-implement-icomparable-interface-preventing-dataset-reject
if (sourceSchema.DotNetType == typeof(SqlGeography))
{
var storageField = typeof(DataColumn).GetField("_storage", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var objectStorageType = typeof(DataColumn).Assembly.GetType("System.Data.Common.ObjectStorage");
var objectStorageConstructor = objectStorageType.GetConstructor(System.Reflection.BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[2] { typeof(DataColumn), typeof(Type) }, null);
var objectStorageInstance = objectStorageConstructor.Invoke(new object[2] { dataColumn, typeof(SqlGeography) });
storageField.SetValue(dataColumn, objectStorageInstance);
}
评论