调用方法时 C# Guid 方法参数被破坏(更改)
C# Guid method parameter being borked (changed) when calling a method
我这里有一个有趣的。我正在调用一个带有 guid 参数的方法,在调用调用和被调用方法的第一步之间的某处,说 guid 参数正在更改。
在有人要求代码示例之前,这个 WAY 太复杂了,无法尝试做一个简单的变体 - 为什么,你问?很简单,如果这是一个重新创建的简单问题(而且,是的,我确实创建了一个完整的解决方案,只重新创建了尽可能接近的孤立条件作为测试,看看它是否确实是一个简单的重新创建问题) ,编写编译器的人会在它投入生产之前解决这个问题。这个在环境的深处,我想知道以前是否有人见过它,如果见过,他们是如何绕过它的。
如果没有,那么 Microsoft Compiler 的人会想看看这个并修复它,因为这个很糟糕,真的很糟糕。
这是问题所在:
如果我有这样的方法调用:
Guid aGuid = Guid.NewGuid();
List<SomeClass> vList = _Handler.SomeMethodThatGeneratesTheList(aGuid)(*1);
代码在其他地方(在通过 Nuget 引入的不同项目中):
public List<SomeClass> SomeMethodThatGeneratesTheList(Guid aGuid)
(*2){
// Do Some handling
}
在(*1)点调用代码时,guid参数就好了。在入口点 (*2) 的调用和入口之间没有任何东西(字面意思是在方法的入口点,尚未发生任何处理,我们处于方法的最开始),参数 aGuid 已更改来自这样的东西:
{c8f09e25-9779-431e-8086-a80400c13bec}
像这样:
{1518dcc0-0000-0000-0000-000000000000}
奇怪的是,当我重新 运行 测试时,替代值发生了变化,但似乎是一种增量方式,例如
{145de090-0000-0000-0000-000000000000}
只有第一段发生了变化,其余的都保持为零。
实际的 NUnit 单元测试代码如下所示:
[Test]
public void TestHandleDbmetaDataDapperResultSet()
{
// Arrange
// Act
List<DbMetaDataTableOrView> vResult =
_DbMetaDataContext
.HandlerDbMetaDataTableOrViewInfo
.FetchAllTableInfoAndTranslate
(_CodeGenParams.DatabaseInfoId);
// Assert
vResult.Should().NotBeNull();
}
更新 2:更新的代码消除了提供参数值的东西以某种方式被冲洗的可能性,如下所示:
[Test]
public void TestHandleDbmetaDataDapperResultSet()
{
// Arrange
Guid vTestParam = Guid.NewGuid();
// Act
//List<DbMetaDataTableOrView> vResult =
// _DbMetaDataContext
// .HandlerDbMetaDataTableOrViewInfo
// .FetchAllTableInfoAndTranslate(_CodeGenParams.DatabaseInfoId);
List<DbMetaDataTableOrView> vResult =
_DbMetaDataContext
.HandlerDbMetaDataTableOrViewInfo
.FetchAllTableInfoAndTranslate(vTestParam);
// Assert
vResult.Should().NotBeNull();
}
起始值:{c86c90ef-a284-470f-8949-36eacbd74afe}
进入调用代码时的参数:{13a6d9d0-0000-0000-0000-000000000000}.
甚至将生成 guid 的行更改为此
Guid vTestParam = Guid.Parse("c86c90ef-a284-470f-8949-36eacbd74afe");
在第一个 运行 上产生这个:{1428e040-0000-0000-0000-000000000000}
这是第二个:{1457d9a0-0000-0000-0000-000000000000}
其中 _CodeGenParams.DatabaseInfoId 包含必需的 Guid 值。被调用方法的签名很简单:
public List<DbMetaDataTableOrView> FetchAllTableInfoAndTranslate
(Guid aDatabaseId)
更新(根据请求,调用方法的全部内容因此提供:)
public List<DbMetaDataTableOrView> FetchAllTableInfoAndTranslate
(Guid aDatabaseId)
{
List<TableInfo> vTableList = FetchAllTableInfo(aDatabaseId);
List<DbMetaDataTableOrView> vResult = new List<DbMetaDataTableOrView>();
DbMetaDataTableOrView vDbTable;
foreach (TableInfo vTable in vTableList)
{
vDbTable =
vTable.DbMetaData.DeSerialize<DbMetaDataTableOrView>
(vTable.DbMetaDataDotNetType);
vResult.Add(vDbTable);
}
return vResult;
}
输入代码时在第一个大括号处打断点,参数已更改。
修改是在单击一步时发生的,正如我之前所说的,实际上是调用和进入代码之间的一步。
其余的一些细节:
被调用的方法存在于一个单独的 class 中,在一个单独的项目 (.csproj) 中实现,该项目已存储为 nuget 包。这里的所有小工具都是最新的:VS 2017 Enterprise,当前 nuget.exe,.NET Framework 4.7.
调用单元测试通过对被测项目的引用继承了被调用的nuget,以通常的(nuget)方式获取nuget包。包本身将调用的 class 方法公开为接口的一部分,这样 _DbMetaDataContext 是 IDbMetaDataContext 的一个实现,所有祖先 classes 都是如此。所有方法都作为其各自接口的实现公开。这样做是为了确保 DI 按预期工作并且当前 DI 引擎是 Ninject。到目前为止,所有基于 DI 的单元测试都运行良好。
我还没有用另一个 DI 库尝试过这个,我已经使用 Ninject 这么长时间了,没有任何问题,以至于我从来没有费心去查看另一个库。我现在只是想把它作为一个可能的困难来源来消除。接下来将检查 SimpleInjector。
这需要一段时间,因为上述方法处于相当大的食物链的顶端,因为它们是 lot 底层库的最终结果代码(因此无法提供 "simple" 重现此代码的方法)。被测方法是 class 中唯一失败的方法,class 中的所有其他方法都很好地通过了它们的单元测试,但是,none 中的方法将 Guid 作为一个参数。字符串传递等就可以了。由此我得出结论,这不是 class 的问题,也不是继承链的问题,也不是接口的使用问题,这只是在该环境中处理 Guid 参数的问题。
至于处理是什么,这就是为什么我要尝试将 DI 排除在外(这不是一项简单的任务),因为它 可能 是 Ninject 的方式接口和具体之间的绑定 class。我有很多静态方法为我执行绑定链,所以当我最终到达我想使用最终小工具的地方时,我不必记住所有绑定。到目前为止,该方法一直运行良好,使用绑定调用的单元测试都在 DI 环境中工作,它只是最后一组似乎引入了这种行为。
我会在了解更多信息后对其进行编辑。
这是进入方法时的调用堆栈:
我见过的最奇怪的事情。将参数类型从标准 "Guid aParam" 更改为 "ref Guid aParam",现在可以正常工作了。
更新:虚假成功。似乎它还取决于您何时重新启动系统。我仍然能够产生问题,并且在进一步调查之后得出结论,这可能是一个 debugger/ReSharper 问题,因为我现在可以使用简单的代码创建一个案例来复制相同的问题。一旦只需要创建一个单独的解决方案,添加一个带有实现接口的 class 的包,添加一个与其他简单 class 不同的接口,将整个东西变成一个 NuGet 包,创建第二个解决方案, 创建一个简单的 class 用于 NuGet 包中 class 的实例并开始单步执行。在那里失败,或者至少调试器在单步执行代码时无法显示正确的值。
这很可能是优化代码的调试器问题。我遇到了完全相同的现象(甚至 GUID 的数字模式相同),在挠头并盯着代码看了几个小时后,我发现我启用了代码优化。在我禁用它之后,一切都按预期运行。
在我的特殊情况下,我查看了反汇编代码,发现不是在被调用方法的堆栈帧中搜索 GUID,而是从调用方方法的堆栈帧中获取 GUID(SBP+xx
而不是SBP-xx
,这是特定的优化)。然而,调试器在当前堆栈帧中搜索它的值,因此显示了奇怪的值。
我这里有一个有趣的。我正在调用一个带有 guid 参数的方法,在调用调用和被调用方法的第一步之间的某处,说 guid 参数正在更改。
在有人要求代码示例之前,这个 WAY 太复杂了,无法尝试做一个简单的变体 - 为什么,你问?很简单,如果这是一个重新创建的简单问题(而且,是的,我确实创建了一个完整的解决方案,只重新创建了尽可能接近的孤立条件作为测试,看看它是否确实是一个简单的重新创建问题) ,编写编译器的人会在它投入生产之前解决这个问题。这个在环境的深处,我想知道以前是否有人见过它,如果见过,他们是如何绕过它的。
如果没有,那么 Microsoft Compiler 的人会想看看这个并修复它,因为这个很糟糕,真的很糟糕。
这是问题所在: 如果我有这样的方法调用:
Guid aGuid = Guid.NewGuid();
List<SomeClass> vList = _Handler.SomeMethodThatGeneratesTheList(aGuid)(*1);
代码在其他地方(在通过 Nuget 引入的不同项目中):
public List<SomeClass> SomeMethodThatGeneratesTheList(Guid aGuid)
(*2){
// Do Some handling
}
在(*1)点调用代码时,guid参数就好了。在入口点 (*2) 的调用和入口之间没有任何东西(字面意思是在方法的入口点,尚未发生任何处理,我们处于方法的最开始),参数 aGuid 已更改来自这样的东西:
{c8f09e25-9779-431e-8086-a80400c13bec}
像这样:
{1518dcc0-0000-0000-0000-000000000000}
奇怪的是,当我重新 运行 测试时,替代值发生了变化,但似乎是一种增量方式,例如
{145de090-0000-0000-0000-000000000000}
只有第一段发生了变化,其余的都保持为零。
实际的 NUnit 单元测试代码如下所示:
[Test]
public void TestHandleDbmetaDataDapperResultSet()
{
// Arrange
// Act
List<DbMetaDataTableOrView> vResult =
_DbMetaDataContext
.HandlerDbMetaDataTableOrViewInfo
.FetchAllTableInfoAndTranslate
(_CodeGenParams.DatabaseInfoId);
// Assert
vResult.Should().NotBeNull();
}
更新 2:更新的代码消除了提供参数值的东西以某种方式被冲洗的可能性,如下所示:
[Test]
public void TestHandleDbmetaDataDapperResultSet()
{
// Arrange
Guid vTestParam = Guid.NewGuid();
// Act
//List<DbMetaDataTableOrView> vResult =
// _DbMetaDataContext
// .HandlerDbMetaDataTableOrViewInfo
// .FetchAllTableInfoAndTranslate(_CodeGenParams.DatabaseInfoId);
List<DbMetaDataTableOrView> vResult =
_DbMetaDataContext
.HandlerDbMetaDataTableOrViewInfo
.FetchAllTableInfoAndTranslate(vTestParam);
// Assert
vResult.Should().NotBeNull();
}
起始值:{c86c90ef-a284-470f-8949-36eacbd74afe} 进入调用代码时的参数:{13a6d9d0-0000-0000-0000-000000000000}.
甚至将生成 guid 的行更改为此
Guid vTestParam = Guid.Parse("c86c90ef-a284-470f-8949-36eacbd74afe");
在第一个 运行 上产生这个:{1428e040-0000-0000-0000-000000000000} 这是第二个:{1457d9a0-0000-0000-0000-000000000000}
其中 _CodeGenParams.DatabaseInfoId 包含必需的 Guid 值。被调用方法的签名很简单:
public List<DbMetaDataTableOrView> FetchAllTableInfoAndTranslate
(Guid aDatabaseId)
更新(根据请求,调用方法的全部内容因此提供:)
public List<DbMetaDataTableOrView> FetchAllTableInfoAndTranslate
(Guid aDatabaseId)
{
List<TableInfo> vTableList = FetchAllTableInfo(aDatabaseId);
List<DbMetaDataTableOrView> vResult = new List<DbMetaDataTableOrView>();
DbMetaDataTableOrView vDbTable;
foreach (TableInfo vTable in vTableList)
{
vDbTable =
vTable.DbMetaData.DeSerialize<DbMetaDataTableOrView>
(vTable.DbMetaDataDotNetType);
vResult.Add(vDbTable);
}
return vResult;
}
输入代码时在第一个大括号处打断点,参数已更改。
修改是在单击一步时发生的,正如我之前所说的,实际上是调用和进入代码之间的一步。
其余的一些细节:
被调用的方法存在于一个单独的 class 中,在一个单独的项目 (.csproj) 中实现,该项目已存储为 nuget 包。这里的所有小工具都是最新的:VS 2017 Enterprise,当前 nuget.exe,.NET Framework 4.7.
调用单元测试通过对被测项目的引用继承了被调用的nuget,以通常的(nuget)方式获取nuget包。包本身将调用的 class 方法公开为接口的一部分,这样 _DbMetaDataContext 是 IDbMetaDataContext 的一个实现,所有祖先 classes 都是如此。所有方法都作为其各自接口的实现公开。这样做是为了确保 DI 按预期工作并且当前 DI 引擎是 Ninject。到目前为止,所有基于 DI 的单元测试都运行良好。
我还没有用另一个 DI 库尝试过这个,我已经使用 Ninject 这么长时间了,没有任何问题,以至于我从来没有费心去查看另一个库。我现在只是想把它作为一个可能的困难来源来消除。接下来将检查 SimpleInjector。
这需要一段时间,因为上述方法处于相当大的食物链的顶端,因为它们是 lot 底层库的最终结果代码(因此无法提供 "simple" 重现此代码的方法)。被测方法是 class 中唯一失败的方法,class 中的所有其他方法都很好地通过了它们的单元测试,但是,none 中的方法将 Guid 作为一个参数。字符串传递等就可以了。由此我得出结论,这不是 class 的问题,也不是继承链的问题,也不是接口的使用问题,这只是在该环境中处理 Guid 参数的问题。 至于处理是什么,这就是为什么我要尝试将 DI 排除在外(这不是一项简单的任务),因为它 可能 是 Ninject 的方式接口和具体之间的绑定 class。我有很多静态方法为我执行绑定链,所以当我最终到达我想使用最终小工具的地方时,我不必记住所有绑定。到目前为止,该方法一直运行良好,使用绑定调用的单元测试都在 DI 环境中工作,它只是最后一组似乎引入了这种行为。
我会在了解更多信息后对其进行编辑。 这是进入方法时的调用堆栈:
我见过的最奇怪的事情。将参数类型从标准 "Guid aParam" 更改为 "ref Guid aParam",现在可以正常工作了。
更新:虚假成功。似乎它还取决于您何时重新启动系统。我仍然能够产生问题,并且在进一步调查之后得出结论,这可能是一个 debugger/ReSharper 问题,因为我现在可以使用简单的代码创建一个案例来复制相同的问题。一旦只需要创建一个单独的解决方案,添加一个带有实现接口的 class 的包,添加一个与其他简单 class 不同的接口,将整个东西变成一个 NuGet 包,创建第二个解决方案, 创建一个简单的 class 用于 NuGet 包中 class 的实例并开始单步执行。在那里失败,或者至少调试器在单步执行代码时无法显示正确的值。
这很可能是优化代码的调试器问题。我遇到了完全相同的现象(甚至 GUID 的数字模式相同),在挠头并盯着代码看了几个小时后,我发现我启用了代码优化。在我禁用它之后,一切都按预期运行。
在我的特殊情况下,我查看了反汇编代码,发现不是在被调用方法的堆栈帧中搜索 GUID,而是从调用方方法的堆栈帧中获取 GUID(SBP+xx
而不是SBP-xx
,这是特定的优化)。然而,调试器在当前堆栈帧中搜索它的值,因此显示了奇怪的值。