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 を 2 回呼び出して、同じインスタンスか別のインスタンスかを確認できますか。

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

不変性をサポートしていない場合は、Rows プロパティにアクセスするたびに行のコピーが返されることを期待しています。

于 2012-07-20T10:02:44.727 に答える