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 的主要性能问题。

显然,用户得到了巨大的延迟,并且经常三次点击触发这段代码的按钮。

问题是,在 .SaveChanges() 在第一个请求中完成之前,.Count() 在不同的 Http 请求中同时触发。结果,我们看到了具有相同电子邮件地址的联系人。

虽然我已经从客户端添加了一个修复程序,但我想看看这是否也可以在服务器端完成。

什么是使这个线程安全的好策略?


编辑:

虽然在 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()应由锁保护。我建议您从锁定一个静态对象开始——这意味着它将是一个防止同时注册的全局锁。如果这被证明是一个问题,您可以修改锁定策略。

关于花费大量时间的通话 - 这是您应该解决的问题。向电子邮件列添加唯一约束将强制对其进行索引,这可能会大大提高性能。如果可能(同样,我不知道 CRM),您应该使用 linq 的Any()运算符而不是Count()检查是否存在。前者可以在第一次命中时中断,而后者则必须继续扫描。

于 2012-07-24T21:17:19.050 回答
1

实体上的预创建插件systemuser可以在将每笔交易传递到平台之前检查您的约束(即电子邮件地址是唯一的),并且是推荐/支持的方式来执行此操作。

于 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 回答