BPM runtime caught an unexpected exception of 'UpdateException' type. Violation of PRIMARY KEY constraint 'PK_CustAttr'

I’m building a standard data directive on Customer. The area where I’m having a problem is when I want to update a field in Customer. My first step in the update process is to get the Customer tableset into variable dsCustomer.

I then update the dsCustomer.Customer table by query. All fields are bound automatically in the mapping with the exception of RowMod and SalesforceID_c. I set the value of RowMod to “U” so it will update and set a value for SalesforceID_c. I don’t touch anything else in the tableset.D

My final step is to call Erp.Customer.Update BO method binding the dsCustomer tableset. In my mind the tableset is exactly what I just retrieved except for the 2 changes in dsCustomer.Customer.

To my surprise the BO choked on the dataset with this error.

Ice.Common.EpicorServerException: BPM runtime caught an unexpected exception of 'UpdateException' type.
See more info in the Inner Exception section of Exception Details. ---> System.Data.Entity.Core.UpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: Violation of PRIMARY KEY constraint 'PK_CustAttr'. Cannot insert duplicate key in object 'Erp.CustAttr'. The duplicate key value is (IRP, 1042, Corp).
The statement has been terminated.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)
   at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.Reader(DbCommand command, DbCommandInterceptionContext interceptionContext)
   at System.Data.Entity.Core.Mapping.Update.Internal.DynamicUpdateCommand.Execute(Dictionary`2 identifierValues, List`1 generatedValues)
   at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
   --- End of inner exception stack trace ---
   at System.Data.Entity.Core.Mapping.Update.Internal.UpdateTranslator.Update()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(SaveOptions options, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction)
   at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options, Boolean executeInExistingTransaction)
   at Ice.IceDataContext.SaveChanges(SaveOptions options) in C:\_Releases\ICE\ICE4.1.100.24\Source\Server\Framework\Epicor.System\Data\IceDataContext.cs:line 352
   at Ice.IceDataContext.Validate[TLinqRow](TLinqRow row) in C:\_Releases\ICE\ICE4.1.100.24\Source\Server\Framework\Epicor.System\Data\IceDataContext.cs:line 313
   at Erp.Services.BO.CustomerSvc.updateAttributes() in C:\_releases\ERP\ERP11.1.100.24\Source\Server\Services\BO\Customer\Customer.cs:line 9566
   at Erp.Services.BO.CustomerSvc.CustomerBeforeUpdate() in C:\_releases\ERP\ERP11.1.100.24\Source\Server\Services\BO\Customer\Customer.cs:line 3849
   at Ice.Services.Trace.TablesetProfilingCollector.DoRowEventTrace(String tableName, String methodName, Int32 rowCount, Action action) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Framework\Epicor.Ice\Services\TablesetProfilingCollector.cs:line 140
   at Ice.TablesetBound`3.UpdateRow(IceDataContext dataContext, Int32 tableNum, IIceTable table, IceRow updatedRow, IceRow originalRow, IColumnUncensor uncensor, TablesetProfilingCollector parentTraceCollector) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Framework\Epicor.Ice\Services\TablesetBound.cs:line 1277
   at Ice.TablesetBound`3.WriteTable(IceDataContext dataContext, Int32 tableIndex, IIceTable table, TablesetProfilingCollector parentTraceCollector) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Framework\Epicor.Ice\Services\TablesetBound.cs:line 974
   at Ice.TablesetBound`3.InnerUpdate[TUpdater](IceDataContext dataContext, TFullTableset tableset) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Framework\Epicor.Ice\Services\TablesetBound.cs:line 866
   at Erp.Services.BO.CustomerSvc.Update(CustomerTableset& ds) in C:\_releases\ERP\ERP11.1.100.24\Source\Server\Services\BO\Customer\Customer.Designer.cs:line 6823
   at Epicor.Customization.Bpm.MethodCustomizationBase2`3.RunDirectives(TParam parameters) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Internal\Lib\Epicor.Customization.Bpm\MethodCustomizationBase2.cs:line 202
   at Epicor.Customization.Bpm.CustomizationBase2`3.Execute(TParam parameters) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Internal\Lib\Epicor.Customization.Bpm\CustomizationBase2.cs:line 87
   at Epicor.Customization.Bpm.BO.CustomerSvcCustomization.Update(CustomerTableset& ds)
   at Erp.Services.BO.CustomerSvcFacade.Update(CustomerTableset& ds) in C:\_releases\ERP\ERP11.1.100.24\Source\Server\Services\BO\Customer\CustomerSvcFacade.cs:line 6418
   at Epicor.Customization.Bpm.DB.PostTranDirective_Salesforce_Sync_D2D9581C22FA465C99ADBFD1D8C112B3.A008_InvokeBOMethodAction2()
   at Epicor.Customization.Bpm.DB.PostTranDirective_Salesforce_Sync_D2D9581C22FA465C99ADBFD1D8C112B3.ExecuteCore()
   at Epicor.Customization.Bpm.DirectiveBase`3.Execute(TParam parameters) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Internal\Lib\Epicor.Customization.Bpm\DirectiveBase.Generic.cs:line 146
   --- End of inner exception stack trace ---
   at Epicor.Customization.Bpm.DirectiveBase`3.Execute(TParam parameters) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Internal\Lib\Epicor.Customization.Bpm\DirectiveBase.Generic.cs:line 162
   at System.Linq.Enumerable.All[TSource](IEnumerable`1 source, Func`2 predicate)
   at Epicor.Customization.Bpm.CustomizationBase2`3.Execute(TParam parameters) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Internal\Lib\Epicor.Customization.Bpm\CustomizationBase2.cs:line 87
   at Epicor.Customization.Bpm.Standard.MonitoringSessionManager.ProcessCollectedData(IceDataContext db, IRowChangesCollection data, IDirectiveEnabler enabler) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Internal\Lib\Epicor.Customization.Bpm\Standard\MonitoringSessionManager.cs:line 147
   at Epicor.Customization.Bpm.Standard.MonitoringSessionManager.FinalizeSession(String sessionId, IceDataContext db) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Internal\Lib\Epicor.Customization.Bpm\Standard\MonitoringSessionManager.cs:line 100
   at Epicor.Customization.Bpm.SvcFacadeBase`3.Epicor.Hosting.IBpmReadyService.FinalizeCall(Object state, Boolean fail) in C:\_Releases\ICE\ICE4.1.100.0\Source\Server\Internal\Lib\Epicor.Customization.Bpm\SvcFacadeBase.Generic.cs:line 227
   at Epicor.Hosting.OperationBoundInvoker.Invoke(Object instance, Func`2 func) in C:\_Releases\ICE\ICE4.1.100.24\Source\Server\Framework\Epicor.System\Hosting\OperationBoundInvoker.cs:line 32

Puzzled…

In your error I see this:

Violation of PRIMARY KEY constraint 'PK_CustAttr'. Cannot insert duplicate key in object 'Erp.CustAttr'. The duplicate key value is (IRP, 1042, Corp).

Importantly there are key values at the very end. This may help you identify the underlying issue.
I suspect that the way you identified your customer record to update might be the problem. Can you use GetByID, instead of GetBySysRowID? I almost always use GetByID unless I have a good reason to use the GUID.

Thanks for the suggestion. I changed the BO, but still got the same result. After some trial and error, I found a solution. The customer tableset doesn’t directly include CustAttr. Instead the entries in the table are in Customer.AttrCodeList. Apparently the Update method thinks that anything that is in this field should be added to erp.CustAttr. So, my solution is to set that field to “” when I update dsCustomer.

Fortunately, the BO doesn’t appear to think that is should remove entries in erpCustAttr when if the AttrCodeList is empty. :slight_smile: Now the data directive works as expected.

2 Likes