2

MVC アプリケーションの MS Dynamics にユーザーを挿入する次のコードがあるとします。

public bool CreateContact(string email)
{
    if (crm.contacts.Count(x => x.Email == email) > 0)
         return false; //Email already exist in the Crm. Skip

    var contact = new contact {Email = email};
    crm.AddTocontacts(contact);
    crm.SaveChanges();

    return true;
}

Dynamics で重大なパフォーマンスの問題が発生した最近まで、ユーザーが同じ電子メール アドレスで登録するのを防ぐのに非常に効果的でした。

どうやら、ユーザーは大幅な遅延を受けており、このコードを実行するボタンを 3 回クリックすることがよくあります。

問題は、最初のリクエストで .SaveChanges() が終了する前に、異なる Http リクエストで .Count() が同時に起動していることです。その結果、同じ電子メール アドレスを持つ連絡先が表示されます。

すでにクライアント側から修正を追加しましたが、これがサーバー側でも実行できるかどうかを確認したいと思います。

このスレッドセーフにするための良い戦略は何でしょうか?


編集:

多くの人がここで提案しているように、CRM に制約を追加することが最善の解決策ですが、この問題が発見されるずっと前に CRM に重複が既に存在しているため、現時点ではその解決策を実装できません。どうやら、CRM と通信するアプリケーションが複数あるようです。

ロックとスレッド化の経験がほとんどないため、最終的に次のことを行いました。

internal static class ContactLock
{
    internal static readonly object Locker = new object();
}

public bool CreateContact(string email)
{
    lock(ContactLock.Locker)
    {
        if (crm.contacts.Any(x => x.Email == email))
            return false; //Email already exist in the Crm. Skip

        var contact = new contact {Email = email};
        crm.AddTocontacts(contact);
        crm.SaveChanges();

        return true;
    }
}

単体テストに合格し、問題なく動作しているようです。

4

3 に答える 3

5

この種の検証は、通常、基になるデータ ストア内の一意の制約によってサポートされている必要があります。CRM データベースに制約を作成できる場合は、そこを修正する必要があります。

あなたのコード スニペットは、ある種のロックが必要な典型的な場所を示しています。チェック ( 付きCount()) と はSaveChanges()ロックで保護する必要があります。静的なオブジェクトをロックすることから始めることをお勧めします。つまり、同時登録を防止するグローバル ロックになります。それが問題であることが判明した場合は、ロック戦略を修正できます。

通話に時間がかかることについては、対処する必要があります。email 列に一意の制約を追加すると、強制的にインデックスが作成され、おそらくパフォーマンスが大幅に向上します。可能であれば (繰り返しますが、私は CRM を知りません) 、存在を確認するAny()代わりに linq の演算子を使用する必要があります。Count()前者は最初のヒットで壊れる可能性がありますが、後者はスキャンを続ける必要があります。

于 2012-07-24T21:17:19.050 に答える
1

エンティティの事前作成プラグインは、systemuser各トランザクションをプラットフォームに渡す前に制約をチェックできます(つまり、電子メールアドレスは一意です)。これは、このimoを実行するための推奨/サポートされている方法です。

于 2012-07-25T05:57:25.117 に答える
0

最初に次の行を追加して、データベースで書き込みロックを取得できます。

crm.ExecuteCommand("select ID from contacts with (updlock, holdlock) where EMail = {0}", EMail);

そして、メソッドをトランザクションでラップする必要があります。これにより、一意性が実現され、デッドロックがなくなります。

于 2012-07-24T21:51:42.910 に答える