1

我已经看过这个问题,但它没有解释它,所以我了解实际发生了什么。我已经开发了多年,以前从未遇到过这种情况(尽管我对 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; }

提前致谢。

4

3 回答 3

1

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

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 绊倒的其他人。

于 2012-07-23T08:01:49.450 回答
0

看起来您的DataRow对象存在同步问题 - 您确定field.Validate不更新数据库中的属性吗?并且您databaseMetadata不只是仍然坐在旧数据上?

建议您可能正在做类似事情connection, row的论据......field.Validate

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

于 2012-07-20T08:09:42.033 回答
0

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

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

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

于 2012-07-20T10:02:44.727 回答