2012-07-24 93 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