代码行只有在使用断点单步调试时才能正常工作

9
我所能想到的唯一可能是竞态条件,但据我所知,调用方法和代码行均为同步。
/// <summary>
/// Gets the log format string for an info-level log.
/// </summary>
public static string Info<T>(string action, T obj)
{
    var stringBuilder = new StringBuilder(String.Format(
        "Action: {0} \tObject: {1} \tUser: {2} \tJson: ",
        action, typeof(T).Name, User
    ));

    // Set all virtual properties to null. This should stop circular references of navigation properties.
    var virtualProperties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(x => x.GetSetMethod().IsVirtual && !x.PropertyType.IsPrimitive);
    foreach (var propInfo in virtualProperties)
    {
        propInfo.SetValue(obj, null); // This Line is the culprit.
    }

    GetJsonSerializer().Serialize(obj, stringBuilder);

    return stringBuilder.ToString();
}

如果我在循环之前设置断点,并逐步执行一次(或者只是在该行上设置断点),则将执行propInfo.SetValue(obj,null)代码行,但如果我不使用断点,则它永远不会将属性设置为null。为什么会这样? 具体详情:
  • 如果我不使用断点,则无法正常工作。
  • 如果我在foreach的顶部放置一个断点并按f5键,它不起作用。
  • 如果我在foreach的顶部放置断点并通过f10逐步执行,它就能正常工作。
  • 如果我在propInfo.SetValue(obj,null);代码行上放置断点,则它可以正常工作。
  • 循环后的断点仍显示值不为null。
  • 如果我将null更改为 5 (这不是有效值),则会引发异常,告诉我它不是有效值。

澄清一下,“不起作用”意味着它不能将属性设为null。

我尝试过:
  • 重启Visual Studio(2013)
  • 更改代码行(以前是default(T)
  • 项目属性->生成->优化代码(最初关闭)
编辑

已经缩小范围,EF导航属性是这种行为的原因。代码正在运行,但由于某种原因,导航属性拒绝成为空。那么关于导航属性有什么特别之处导致了这种行为呢?


如果您尝试非 EF T 类型会发生什么?也许实体框架没有“接受”属性更改,我们可能能够确定原因。 - 31eee384
确实,原始虚拟属性正在被更改。只是EF导航属性没有被更改。我想我会在问题中添加一个EF标签。 - Shelby115
更具体地说,我建议创建一个新的、非 EF 类,其中包含非原始虚拟属性,对其进行初始化,并将其传递到此函数中。这甚至可以是一个单元测试。如果它适用于这些情况而不适用于 EF,则我会说它与 EF 有关(即使这没有意义!) - 31eee384
1
是的,这是一个 EF 导航属性的问题。非 EF 虚拟非原始类型会被设置为 null。 - Shelby115
这是懒加载的问题。在设置值之前进行GetValue使其正常工作。尽管我现在相当确定自己知道原因,但我仍然很好奇其中的解释。我并不完全理解正在发生的事情。 - Shelby115
显示剩余4条评论
2个回答

7

懒加载

导航属性是懒加载的,因此当序列化器查看它们时,它们会被原始值覆盖。因此,设置为null一直有效,但被懒加载覆盖了。

调试

调试出现的原因是因为我在执行SetValue代码行之前查看了值。这导致导航属性在执行代码行之前加载了值,从而导致null值未被覆盖。

解决方案

foreach (var propInfo in virtualProperties)
{
    propInfo.GetValue(obj); // Trigger any navigation property to load.
    propInfo.SetValue(obj, null);
}

只是好奇,如果在 typeof(T).GetProperties(...) 的结尾添加 .All() 或 .ToList() 选择器,是否会强制集合枚举并加载所有项? - Ross Bush
.ToList() 添加到 typeof(T).GetProperties()... 不会强制加载。这是因为它在属性集合上而不是属性本身上。我进行了测试以确认我的怀疑。 - Shelby115

1

我在一个多对多的EF6代码优先设置中遇到了非常相似的问题。在我的Web API控制器中,我一直在从DTO设置的值中设置对象的导航属性。当一个被传递到我的repo时,它们的属性没有被更新因为懒加载,但是在调试模式下,如果我步进到那段代码附近,它会起作用。最终我使用了:

try {
    TheDatabase.Configuration.LazyLoadingEnabled = false;
    ...
    ...
}
finally {
    TheDatabase.Configuration.LazyLoadingEnabled = true;
}

目前看来这个工作正常。由于我的dbContext每个控制器都被处理,我认为这不会是问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接