在过去的几天里,我一直在用这个来敲打我的头,我似乎无法想出一个好的解决方案。
我有一个 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;
}