【问题标题】:Foreach item changes not preserved outside loopForeach 项目更改未在循环外保留
【发布时间】:2012-07-20 08:05:08
【问题描述】:

我已经看过this question,但它没有解释它,以便我了解实际发生的情况。我已经开发了多年,以前从未遇到过这种情况(尽管我最近才使用 Linq 和 Parallel)。

我的代码是:

Parallel.ForEach(databaseMetadata.Rows.Cast<DataRow>(), row => {
        var fieldName = row.Item("Name", "");
        var field = this.Fields.Where(f => f.Name.ToLower() == fieldName.ToLower()).SingleOrDefault();
        if(field != null) { field.Validate(this, connection, row); }
});

在 field.Validate 函数中,它将字段对象上名为“HasBeenValidated”的属性设置为 true,但是,一旦我退出此 Parallel.ForEach 循环,该属性就会设置回 false。有人可以解释为什么会发生这种情况以及我可以做些什么来确保循环内的更改在循环外保持不变。

已编辑:

下面是field.Validate中的代码副本:

internal void Validate(EntityAttribute entity, SqlConnection connection, [AllowNull] DataRow metadata) {
    this.HasBeenValidated = true;
    var isRequired = this.IsRequired;
    var maxLength = this.MaxLength;
    var isAutoGenerated = this.IsAutoGenerated;
    var dataType = this.member.PropertyType;
    var dataTypeAsString = "";
    if(metadata != null) {
        isRequired = metadata.Item("IsRequired", false);
        maxLength = metadata.Item("MaxLength", 0);
        isAutoGenerated = metadata.Item("IsAutoGenerated", false);
        dataTypeAsString = metadata.Item("DataType", "");
        if(dataTypeAsString == this.member.PropertyType.ToSqlServerDataType()) { dataTypeAsString = ""; }
    } else {
        dataTypeAsString = this.member.PropertyType.ToSqlServerDataType();
    }
    if(metadata == null || isRequired != this.IsRequired || maxLength != this.MaxLength || isAutoGenerated != this.IsAutoGenerated || dataTypeAsString != "") {
        var sql = string.Format((metadata == null ? "ALTER TABLE [{0}].[{1}] ADD" : "ALTER TABLE {0} ALTER COLUMN"), entity.Schema, entity.Name) + " " + this.Sql + ";";
        if(!connection.ExecuteCommand(sql, 1)) {
            throw new InvalidOperationException("Unable to create or alter column '" + this.Name + "' on table '" + entity.Name + "'.");
        }
    }
}

HasBeenValidated 属性在字段对象上定义为:

internal bool HasBeenValidated { get; set; }

提前致谢。

【问题讨论】:

  • 我想这里没有值类型在起作用?显然除了 bool 属性。您是否尝试过将其更改为普通的foreach 并逐步执行?
  • 当我们看不到 Validate(...),看不到数据来自哪里(即它是可重复的列表?还是 .Rows 实际查询时,您要求我们发表评论)数据),看不到你在哪里检查HasBeenValidated(或它被设置的地方),也看不到这是structclass的问题。我们可能假设 DataRow 表示这是DataTable,但即使这样的假设也是不合理的......此外,请注意DataTable 并不声称对于写操作是线程安全的,尽管推测这是相关的。
  • aka:你有一个我们可以实际看到的可重复的例子吗?
  • @AdamHouldsworth 和 DarenThomas:我会将其更改为 for 循环,看看会发生什么。
  • @MarcGravell:我已经包含了验证代码。行实际上来自数据表。 HasBeenValidated 是 fields 对象上的一个简单属性,我已将其包含在问题的编辑中。这段代码中根本没有结构,它们都是类。我没有在此代码中将任何数据写入数据表,因此线程安全不是问题(无论如何都与数据表写入有关)。

标签: c# linq foreach parallel-processing persistence


【解决方案1】:

抱歉,如果我浪费了任何人的时间,但我已经找出了这个问题的原因。 this.Fields 列表是可查询的字段类型的 IEnumerable。我认为这比有一个贪婪的列表要好(因为这个类上有很多列表)。生成Fields列表的代码是:

this.Fields = allProperties
    .Select(property => new { Property = property, Field = property.GetCustomAttributes(typeof(FieldAttribute), true).SingleOrDefault() as FieldAttribute })
    .Where(info => info.Field != null && (info.Field as ListAttribute) == null)
    .Select(info => { info.Field.Member = info.Property; return info.Field; });

我完全没有意识到 GetCustomAttributes 每次调用时都会意外地(从我的角度来看)重新生成属性类的副本。

如果这是一个更简单的类,我可能会更早地怀疑这一点,但是在设置 Member 属性时我也在更改字段类中的属性(即从 info.Property 中提取元数据并根据该类的属性)所以当我在调试器中查看字段类时,我可以看到很多属性已更改(这误导我认为它是字段类的同一个实例而不是副本)。

如果我在这方面浪费了任何人的时间和精力,我真的很抱歉,但希望通过发布我的错误,这可以帮助将来可能会在非贪婪的 Linq 表达式中以类似方式使用 GetCustomAttribute 时遇到困难的其他人。

【讨论】:

    【解决方案2】:

    您的 DataRow 对象似乎存在同步问题 - 您确定 field.Validate 不会更新数据库中的属性吗?而且您的databaseMetadata 不只是仍在使用旧数据吗?

    connection, rowfield.Validate 的参数表明您可能正在做类似的事情......

    尝试使用普通的for 循环,看看会发生什么。你有同样的问题吗?

    【讨论】:

      【解决方案3】:

      你能不能调用 databaseMetadata.Rows 两次并检查它是同一个实例还是不同的实例。

      bool isEquals = ReferenceEquals(databaseMetadata.Rows, databaseMetadata.Rows)
      

      如果它不支持不变性,我希望它在您每次访问 Rows 属性时返回行的副本。

      【讨论】:

        猜你喜欢
        • 2011-12-06
        • 2017-11-04
        • 2016-12-04
        • 2016-02-07
        • 1970-01-01
        • 1970-01-01
        • 2012-01-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多