1

在过去的几天里,我一直在用这个来敲打我的头,我似乎无法想出一个好的解决方案。

我有一个 WCF 服务,它充当我们所有数据库交互的唯一入口点。在当前的困境中,有一个 Windows 服务针对 Microsoft Dynamics CRM 中的“AsyncOperation”表旋转。每当在 CRM 中创建实体记录时,Windows 服务都会从 AsyncOperation 表中提取记录,并使用该数据向 WCF 服务发出请求。我遇到的问题是,当该 Windows 服务同时向 WCF 服务发出多个请求时,该服务会导致 SQL 中的事务死锁。

我在 WCF 服务内的数据流中添加了一些额外的日志记录,我发现在任何给定时间,3 到 5 个请求可以在几毫秒内到达服务。似乎在这个过程中,第一个命中服务的请求是唯一进入数据库的请求,其余的最终导致 sql 死锁错误:

事务(进程 ID 95)与另一个进程在锁资源上死锁,并已被选为死锁牺牲品。重新运行事务。

我的问题是:有没有一种好的方法来实现某个时间的排队过程或 WCF 的单例方法,以确保 SQL 插入不会在完全相同的时间发生?由于我是在 MS Dynamics 平台之上构建的,因此我无法确保不会同时处理多个请求。输入系统的数据是由外部合作伙伴提供的,其中一些对我们的系统造成了相当大的影响。

我愿意接受任何可以寻找解决方案的建议。

WCF 服务中处理插入的方法是这样的:

public List<NoelGroup.Users.Core.Entity.BusinessLayer.PersonEntity> Insert(NoelGroup.Users.Core.Entity.BusinessLayer.PersonEntity businessObject)
{
    List<NoelGroup.Users.Core.Entity.BusinessLayer.PersonEntity> businessObjectList = null;

    using (SqlConnection conn = MainConnection)
    {
        int id = new Random().Next(9999);
        try
        {
            conn.Open();

            using (SqlCommand cmd = new SqlCommand("[crud].[usp_Person_InsertUpdate]", conn))
            {
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add(new SqlParameter("@iui_PersonId", businessObject.PersonId));
                cmd.Parameters.Add(new SqlParameter("@ii_PrefixId", businessObject.PrefixId));
                cmd.Parameters.Add(new SqlParameter("@ivn_FirstName", businessObject.FirstName));
                cmd.Parameters.Add(new SqlParameter("@ivn_MiddleName", businessObject.MiddleName));
                cmd.Parameters.Add(new SqlParameter("@ivn_LastName", businessObject.LastName));
                cmd.Parameters.Add(new SqlParameter("@ic_Gender", businessObject.Gender));
                cmd.Parameters.Add(new SqlParameter("@ii_SuffixId", businessObject.SuffixId));
                cmd.Parameters.Add(new SqlParameter("@ii_PersonTypeId", businessObject.PersonTypeId));
                cmd.Parameters.Add(new SqlParameter("@id_BirthDate", businessObject.BirthDate));
                cmd.Parameters.Add(new SqlParameter("@iti_PreferredContactMethodId", businessObject.PreferredContactMethodId));
                cmd.Parameters.Add(new SqlParameter("@iv_ModifiedUsername", businessObject.ModifiedUsername));

                Logger.Log(string.Format("-- PersonEntity Insert ({1}) Execute -- {0}", DateTime.Now, id));
                using (SqlDataReader rdr = cmd.ExecuteReader())
                {
                    businessObject = null;
                    businessObject = new NoelGroup.Users.Core.Entity.BusinessLayer.PersonEntity();

                    businessObjectList = PopulateObjectsFromReader(rdr);
                }
                Logger.Log(string.Format("-- PersonEntity Insert ({1}) Complete -- {0}", DateTime.Now, id));
            }
        }
        catch(Exception ex)
        {
            throw new SeverityException(500, string.Format("PersonEntity::Insert ({0})::Error occured.", id), ex);
            //throw new SeverityException(500, "PersonEntity::Insert::Error occured.", ex);
        }
    }

    return businessObjectList;
}
4

1 回答 1

3

您应该查看您使用的存储过程,以免产生死锁。Sql 旨在与多个请求并行工作。有一些方法可以审查代码并进行改进,以免陷入僵局。

如果这不可能,您可以使用服务代理将您的请求排队到 sql server(如果您使用支持它的 sql server 版本)。这意味着您必须稍后检查您的操作结果。

如果仍然不是这种情况(ms-sql <2005),那么您可以实现类似的东西。将您的请求写入表格并使用作业来处理它。稍后再检查操作结果。

如果您可以使用 CLR 存储过程,您可以使用操作结果调用您的 WCF 服务,这样您就不需要定期检查 DB。

希望这可以帮助。

作为编辑,您还可以检查 SP 中的死锁并在出现错误时重试(检查错误号 1205)

于 2013-02-28T16:04:52.810 回答