2012-07-24 71 views
2

說我有下面的代碼,接入用戶爲MS動態的MVC應用程序:戰略線程安全的數據庫訪問

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的主要性能問題時。

顯然,用戶正在獲得巨大的延遲,並且經常三次點擊觸發這段代碼的按鈕。

問題是,.Count()在不同的Http Requests中同時觸發.SaveChanges()在第一個Request中完成。因此,我們看到有相同電子郵件地址的聯繫人。

雖然我已經從客戶端添加了一個修復程序,但我想看看這是否也可以在服務器端完成。

什麼是一個好的策略,使這個線程安全?


編輯:

雖然對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; 
    } 
} 

它通過我的單元測試,似乎是工作沒有任何問題。

+3

這個工作時禁用按鈕? – 2012-07-24 21:15:19

+0

奧斯汀,是的,我已經在客戶端做到了。但我想看看我是否也可以從服務器上做到這一點。 – 2012-07-24 21:32:19

+0

對於a)意圖和b)可能的優化,優先選擇'.Any(x => x.Email == email)'Count'(x => x.Email == email)> 0'。 – 2012-07-24 21:54:37

回答

5

這種驗證通常應由基礎數據存儲中的唯一約束支持。如果可以在CRM數據庫中創建約束條件,那就是修復應該去的地方。

您的代碼片段顯示了需要某種鎖定的典型位置。支票(Count())和SaveChanges()應該由鎖保護。我建議你首先鎖定一個靜態的對象 - 這意味着它將是一個全局鎖,防止同時註冊。如果這證明是一個問題,您可以修改鎖定策略。

關於需要大量時間的通話 - 這是您應該解決的問題。爲電子郵件列添加唯一約束將強制索引它,這可能會提高性能。如果可能(再次,我不知道CRM),您應該使用linq的Any()運營商而不是Count()來檢查是否存在。前者可以打破第一擊,而後者將不得不繼續掃描。

+0

我不認爲應用程序鎖是一個好主意。如果應用程序是負載平衡的呢?它不會工作。 – usr 2012-07-24 21:50:11

+0

不,它在負載平衡的情況下效果不佳,在這種情況下,DB約束是唯一的出路。 – 2012-07-24 21:52:06

+0

這不是,請參閱我的答案。 – usr 2012-07-24 21:53:05

0

您可以在開頭添加以下行採取了寫入鎖定在數據庫:

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

而你需要環繞方法的交易。這實現了獨特性,並且是無死鎖的。

1

systemuser實體上的預創建插件可以在將每個事務傳遞到平臺上之前檢查您的約束(即電子郵件地址是唯一的),並且是推薦/支持的方式來執行此操作。

+0

服務器端解決方案的+1,我們也實現了這一點,但如果兩個'Create'請求同時使用同一個電子郵件地址發送,那麼在大容量情況下會發生什麼情況?我可以看到他們通過了「計數」/「任意」測試的情況,因爲計時,允許重複的電子郵件,而數據庫上的索引將完全不允許這樣做。不過,這絕對是僅限在線部署的最佳解決方案。 – 2012-07-25 15:11:20

+0

一個很好的問題和一個我不知道答案的問題(雖然我懷疑你的預感是正確的!)。也許在內部部署的情況下,可以在插件代碼中引入「Mutex」? – 2012-07-26 07:50:11