Foreach项目更改未在循环外保留

时间:2012-07-20 08:05:08

标签: c# linq foreach parallel-processing persistence

我已经看过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; }

提前致谢。

3 个答案:

答案 0 :(得分:1)

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

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属性时也改变了fields类中的属性(即从info.Property中提取元数据并在字段类中基于该类的属性)因此,当我在调试器中查看字段类时,我可以看到许多属性已被更改(这误导我认为它是字段类的相同实例而不是副本)。 / p>

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

答案 1 :(得分:0)

您的DataRow对象看起来存在同步问题 - 您确定field.Validate不更新数据库中的属性吗?那么你的databaseMetadata不仅仅依旧坐在旧数据上了吗?

connection, rowfield.Validate的论点暗示你可能会做类似的事情......

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

答案 2 :(得分:0)

您可以调用databaseMetadata.Rows两次并检查它是相同的实例还是不同的实例。

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

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