Dataverse 플러그 인 문제 해결
이 문서에는 플러그 인 실행 중에 발생할 수 있는 오류 또는 플러그 인과 관련된 Dataverse 오류 및 이를 방지하거나 해결하는 방법에 대한 정보가 포함되어 있습니다.
오류 "시간 변환을 완료할 수 없습니다."
오류 코드: -2147220956
오류 메시지: 제공된 DataTime에 Kind 속성이 올바르게 설정되어 있지 않아 변환을 완료할 수 없습니다. 예를 들어 Kind 속성이 DateTimeKind.Local인 경우 원본 표준 시간대는 TimeZoneInfo.Local이어야 합니다.
이 오류는 TimeZoneInfo.ConvertTimeToUtc(DateTime, TimeZoneInfo) 산티아고 또는 볼고그라드 표준 시간대의 값을 UTC(협정 세계시)로 변환 DateTime
하기 위해 플러그 인 코드에서 호출하는 동안 발생할 수 있습니다.
이 오류는 알려진 제품 제한으로 인해 발생하며 현재 해결 방법이 없습니다.
자세한 내용은 사용자의 표준 시간대 설정 지정을 참조 하세요.
오류 "샌드박스 작업자 프로세스가 충돌했습니다."
오류 코드: -2147204723
오류 메시지: 샌드박스 작업자 프로세스가 충돌하여 플러그 인 실행이 실패했습니다. 이는 일반적으로 플러그 인 코드의 오류 때문입니다.
이 오류는 플러그 인 코드를 실행하는 작업자 프로세스가 충돌했음을 의미합니다. 플러그 인이 충돌의 원인이 될 수 있지만 조직에서 동시에 실행되는 또 다른 플러그 인일 수도 있습니다. 프로세스가 충돌했기 때문에 충돌한 이유에 대한 자세한 정보를 추출할 수 없습니다. 그러나 팩트 후 크래시 덤프에서 데이터를 검사한 후 이 오류는 일반적으로 네 가지 이유 중 하나로 인해 발생합니다.
- 플러그 인에서 처리되지 않은 예외
- 스레드를 사용하여 스레드 대리자에서 try/catch 없이 작업 큐에 대기
- 플러그 인의 스택 오버플로 오류
- 작업자 프로세스가 메모리 제한에 도달함
플러그 인에서 처리되지 않은 예외
플러그 인을 작성할 때 실패할 수 있는 작업을 예측하고 try-catch 블록에 래핑해야 합니다. 오류가 발생하면 클래스를 InvalidPluginExecutionException 사용하여 사용자에게 의미 있는 오류로 작업을 정상적으로 종료해야 합니다.
자세한 내용은 플러그 인의 예외 처리를 참조 하세요.
이 예외에 대한 일반적인 시나리오는 HttpClient.SendAsync 또는 HttpClient.GetAsync 메서드를 사용하는 경우입니다. 이러한 HttpClient 메서드는 작업을 반환하는 비동기 작업입니다. 코드가 동기적이어야 하는 플러그 인에서 작업하려면 사용자가 Task<TResult>를 사용할 수 있습니다. 결과 속성입니다. 오류가 발생하면 AggregateException을 반환합니다Result
. 여러 AggregateException
오류를 단일 예외로 통합하여 처리하기 어려울 수 있습니다. 더 나은 디자인은 Task<TResult를 사용하는 것입니다>. GetAwaiter().GetResult()는 오류를 발생시킨 특정 오류로 결과를 전파하기 때문입니다.
다음 예제에서는 HttpClient.GetAsync 메서드를 사용하여 예외 및 아웃바운드 호출을 관리하는 올바른 방법을 보여줍니다. 이 플러그 인은 등록된 단계에 대해 안전하지 않은 구성에 설정된 Uri에 대한 응답 텍스트를 가져오려고 시도합니다.
using Microsoft.Xrm.Sdk;
using System;
using System.Net.Http;
namespace ErrorRepro
{
public class AsyncError : IPlugin
{
private readonly string webAddress;
public AsyncError(string unsecureConfig)
{
if (string.IsNullOrEmpty(unsecureConfig)) {
throw new InvalidPluginExecutionException("The ErrorRepro.AsyncError plug-in requires that a Url be set in the unsecure configuration for the step registration.");
}
webAddress = unsecureConfig;
}
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
tracingService.Trace($"Starting ErrorRepro.AsyncError");
tracingService.Trace($"Sending web request to {webAddress}");
try
{
string responseText = GetWebResponse(webAddress, tracingService);
tracingService.Trace($"Result: {responseText.Substring(0, 100)}");
}
catch (Exception ex)
{
tracingService.Trace($"Error: ErrorRepro.AsyncError {ex.Message}");
throw new InvalidPluginExecutionException(ex.Message);
}
tracingService.Trace($"Ending ErrorRepro.AsyncError");
}
//Gets the text response of an outbound web service call
public string GetWebResponse(string webAddress, ITracingService tracingService)
{
try
{
using (HttpClient client = new HttpClient())
{
client.Timeout = TimeSpan.FromMilliseconds(15000); //15 seconds
client.DefaultRequestHeaders.ConnectionClose = true; //Set KeepAlive to false
HttpResponseMessage response = client.GetAsync(webAddress).GetAwaiter().GetResult(); //Make sure it is synchronous
response.EnsureSuccessStatusCode();
tracingService.Trace($"ErrorRepro.AsyncError.GetWebResponse succeeded.");
string responseContent = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); //Make sure it is synchronous
tracingService.Trace($"ErrorRepro.AsyncError.GetWebResponse responseContent parsed successfully.");
return responseContent;
}
}
catch (Exception ex)
{
//Capture the inner exception message if it exists.
// It should have a more specific detail.
string innerExceptionMessage = string.Empty;
if (ex.InnerException != null) {
innerExceptionMessage = ex.InnerException.Message;
}
tracingService.Trace($"Error in ErrorRepro.AsyncError : {ex.Message} InnerException: {innerExceptionMessage}");
if (!string.IsNullOrEmpty(innerExceptionMessage))
{
throw new Exception($"A call to an external web service failed. {innerExceptionMessage}", ex);
}
throw new Exception("A call to an external web service failed.", ex);
}
}
}
}
스레드를 사용하여 스레드 대리자에서 try/catch 없이 작업 큐에 대기
플러그 인에서 병렬 실행 패턴을 사용하면 안 됩니다. 이 안티패턴은 모범 사례 문서에서 설명합니다. 플러그 인 및 워크플로 활동 내에서 병렬 실행을 사용하지 마세요. 이러한 패턴을 사용하면 동기 플러그 인에서 트랜잭션을 관리하는 데 문제가 발생할 수 있습니다. 그러나 이러한 패턴을 사용하지 않는 또 다른 이유는 스레드 대리자의 블록 외부에서 try
/catch
수행되는 모든 작업이 작업자 프로세스를 충돌할 수 있기 때문입니다.
Important
작업자 프로세스가 충돌하면 해당 프로세스에서 현재 실행 중인 플러그 인 및 기타 플러그 인의 실행이 종료됩니다. 여기에는 소유하거나 유지 관리하지 않는 플러그 인이 포함됩니다.
복구에 대한 Application Insights
이전에는 크래시된 작업자 프로세스에서 처리되지 않은 플러그 인 예외에 대한 스택 추적 또는 기타 실행 정보를 가져올 수 없었습니다. 그러나 이제 Dataverse는 Application Insights에 대한 로깅 실행 실패에 대한 지원을 제공합니다. 이 함수를 사용하도록 설정하려면 Application Insights를 플러그 인이 등록된 환경에 연결할 수 있습니다. 연결되면 플러그 인 크래시 로깅이 자동으로 발생합니다.
자세한 내용은 Application Insights로 데이터 내보내기를 참조 하세요.
Application Insights 환경이 연결되면 문제 해결을 위해 다음과 같은 작업 프로세스 크래시 데이터를 사용할 수 있습니다.
Application Insights에서 크래시 보고서로 이동하려면 다음 단계를 수행합니다.
- Application Insights를 사용자 환경에 연결합니다.
- 플러그 인 예외로 인해 작업자 프로세스의 크래시 오류가 발생할 때까지 기다립니다.
- Power Platform 관리 센터에서 Application Insights로 이동합니다.
- Application Insights 페이지의 왼쪽 패널에서 실패를 선택합니다.
- 오류 페이지에서 예외를 선택합니다.
- 예외 문제 ID 아래의 전체 목록에서 Microsoft.PowerPlatform.Dataverse.Plugin.PluginWorkerCrashException을 선택합니다.
- 페이지 오른쪽의 [전체] 아래에서 PluginWorkerCrashException을 선택합니다. 이제 기록된 모든 작업자 프로세스 충돌 예외의 세부 정보가 표시됩니다.
- 왼쪽 패널에서 원하는 예외를 검색하여 선택하면 예외 세부 정보 보고서가 페이지 오른쪽에 표시됩니다(예제는 앞의 스크린샷 참조).
- 스택 추적에 액세스하려면 보고서에서 CrashDetails를 확장 합니다 .
플러그 인의 스택 오버플로 오류
이 유형의 오류는 플러그 인 코드를 변경한 직후에 가장 자주 발생합니다. 어떤 사람들은 자체 기본 클래스 집합을 사용하여 개발 환경을 간소화합니다. 경우에 따라 이러한 오류는 특정 플러그 인이 의존하는 기본 클래스를 변경하여 발생합니다.
예를 들어 종료 조건이 없는 재귀 호출 또는 모든 시나리오를 다루지 않는 종료 조건으로 인해 이 오류가 발생할 수 있습니다. 자세한 내용은 StackOverflowException 클래스 > 비고를 참조하세요.
플러그 인 및 플러그 인 코드가 의존하는 다른 코드에 대해 최근에 적용된 코드 변경 내용을 검토해야 합니다.
예시
다음 플러그 인 코드는 제한 없이 재귀 호출로 인해 발생 StackOverflowException
합니다. 추적을 사용하고 오류를 캡처하려고 시도했음에도 불구하고 추적 및 오류는 처리할 작업자 프로세스가 종료되었기 때문에 반환되지 않습니다.
using Microsoft.Xrm.Sdk;
using System;
namespace ErrorRepro
{
public class SOError : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
tracingService.Trace($"Starting ErrorRepro.SOError");
try
{
tracingService.Trace($"Calling RecursiveMethodWithNoLimit to trigger StackOverflow error.");
RecursiveMethodWithNoLimit(tracingService); //StackOverflowException occurs here.
}
catch (Exception ex)
{
//This trace will not be written
tracingService.Trace($"Error in ErrorRepro.SOError {ex.Message}");
//This error will never be thrown
throw new InvalidPluginExecutionException($"Error in ErrorRepro.SOError. {ex.Message}");
}
//This trace will never be written
tracingService.Trace($"Ending ErrorRepro.SOError");
}
public static void RecursiveMethodWithNoLimit(ITracingService tracingService)
{
tracingService.Trace($"Starting ErrorRepro.SOError.RecursiveMethodWithNoLimit");
RecursiveMethodWithNoLimit(tracingService);
tracingService.Trace($"Ending ErrorRepro.SOError.RecursiveMethodWithNoLimit");
}
}
}
동기 플러그 인 단계에서 이전에 표시된 플러그 인 코드는 요청이 오류와 함께 추가 세부 정보를 포함하도록 구성된 경우 Web API에서 다음 오류를 반환합니다.
{
"error": {
"code": "0x8004418d",
"message": "The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.\r\nSystem.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.\r\n at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64& wcfExecInMs, Int64& initializeInMs, Int64& trackCallInMs, Int64& trackGoodReturnInMs, Int64& waitInMs, Int64& taskStartDelay) +0x330: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #8503641A",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionSourceKey": "Plugin/ErrorRepro.SOError, ErrorRepro, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c2bee3e550ec0851",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepKey": "d5958631-b87e-eb11-a812-000d3a4f50a7",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiDepthKey": "1",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiActivityIdKey": "a3028bda-73c2-4eef-bcb5-157c5a4c323e",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiPluginSolutionNameKey": "Active",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiStepSolutionNameKey": "Active",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionCategory": "SystemFailure",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionMesageName": "SandboxWorkerNotAvailable",
"@Microsoft.PowerApps.CDS.ErrorDetails.ApiExceptionHttpStatusCode": "500",
"@Microsoft.PowerApps.CDS.HelpLink": "http://go.microsoft.com/fwlink/?LinkID=398563&error=Microsoft.Crm.CrmException%3a8004418d&client=platform",
"@Microsoft.PowerApps.CDS.TraceText": "\r\n[ErrorRepro: ErrorRepro.SOError]\r\n[d5958631-b87e-eb11-a812-000d3a4f50a7: ErrorRepro.SOError: Create of account]\r\n\r\n",
"@Microsoft.PowerApps.CDS.InnerError.Message": "The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.\r\nSystem.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.\r\n at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64& wcfExecInMs, Int64& initializeInMs, Int64& trackCallInMs, Int64& trackGoodReturnInMs, Int64& waitInMs, Int64& taskStartDelay) +0x330: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #8503641A"
}
}
다음은 이 오류가 플러그 인 Tracelog에 기록되는 방법을 보여줍니다.
Unhandled exception:
Exception type: System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]
Message: The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.
System.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.
at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64& wcfExecInMs, Int64& initializeInMs, Int64& trackCallInMs, Int64& trackGoodReturnInMs, Int64& waitInMs, Int64& taskStartDelay) +0x330: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #4BC22433Detail:
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<ActivityId>48c5818e-4912-42f0-b1b6-e3bbe7ae013d</ActivityId>
<ErrorCode>-2147204723</ErrorCode>
<ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<HelpLink i:nil="true" />
<Message>The plug-in execution failed because the Sandbox Worker process crashed. This is typically due to an error in the plug-in code.
System.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.
at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64& wcfExecInMs, Int64& initializeInMs, Int64& trackCallInMs, Int64& trackGoodReturnInMs, Int64& waitInMs, Int64& taskStartDelay) +0x330: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #4BC22433</Message>
<Timestamp>2021-03-06T22:14:22.0629638Z</Timestamp>
<ExceptionRetriable>false</ExceptionRetriable>
<ExceptionSource>WorkerCommunication</ExceptionSource>
<InnerFault i:nil="true" />
<OriginalException>System.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at Microsoft.Crm.Sandbox.SandboxWorkerProcess.Execute(SandboxCallInfo callInfo, SandboxPluginExecutionContext requestContext, Guid pluginAssemblyId, Int32 sourceHash, String assemblyName, Guid pluginTypeId, String pluginTypeName, String pluginConfiguration, String pluginSecureConfig, Boolean returnTraceInfo, Int64& wcfExecInMs, Int64& initializeInMs, Int64& trackCallInMs, Int64& trackGoodReturnInMs, Int64& waitInMs, Int64& taskStartDelay)</OriginalException>
<TraceText i:nil="true" />
</OrganizationServiceFault>
작업자 프로세스가 메모리 제한에 도달함
각 작업자 프로세스에는 한정된 양의 메모리가 있습니다. 많은 양의 데이터를 포함하는 여러 동시 작업이 사용 가능한 메모리를 초과하여 프로세스 작업자가 충돌할 수 있는 조건이 있습니다.
파일 데이터를 사용하여 RetrieveMultiple
이 경우 일반적인 시나리오는 요청에 파일 데이터가 포함된 작업에 대해 RetrieveMultiple
플러그 인이 실행되는 경우입니다. 예를 들어 파일 첨부 파일이 포함된 전자 메일을 검색하는 경우입니다. 이와 같은 쿼리에서 반환될 수 있는 데이터의 양은 전자 메일이 파일 첨부 파일 수와 관련이 있을 수 있고 첨부 파일의 크기가 달라질 수 있기 때문에 예측할 수 없습니다.
비슷한 특성의 여러 요청이 동시에 실행되는 경우 필요한 메모리 양이 커집니다. 메모리 양이 한도를 초과하면 프로세스가 충돌합니다. 이 상황을 방지하는 핵심은 관련 파일 첨부 파일이 있는 엔터티를 포함하는 쿼리를 RetrieveMultiple
제한하는 것입니다. 개별 작업을 사용하여 필요에 따라 모든 관련 파일을 검색 RetrieveMultiple
Retrieve
하여 레코드를 검색합니다.
메모리 누수
덜 일반적인 시나리오는 플러그 인의 코드가 메모리를 누수하는 경우입니다. 이 상황은 플러그 인이 상태 비주비로 작성되지 않은 경우에 발생할 수 있습니다. 이는 또 다른 모범 사례입니다. 자세한 내용은 플러그 인 구현을 상태 비스테이션으로 개발을 참조하세요. 플러그 인이 상태 비스테이션이 아니고 배열과 같은 상태 저장 속성에 데이터를 지속적으로 추가하려고 하면 데이터 양이 사용 가능한 모든 메모리를 사용하는 지점까지 증가합니다.
트랜잭션 오류
트랜잭션과 관련된 두 가지 일반적인 오류 유형이 있습니다.
오류 코드: -2146893812
오류 메시지: ISV 코드는 열려 있는 트랜잭션 수를 줄입니다. 사용자 지정 플러그 인은 OrganizationService 호출에서 예외를 catch하고 처리를 계속하면 안 됩니다.
오류 코드: -2147220911
오류 메시지: 활성 트랜잭션이 없습니다. 이 오류는 일반적으로 서비스 호출의 오류를 무시하고 처리를 계속하는 사용자 지정 플러그 인으로 인해 발생합니다.
참고 항목
가장 최근에 상위 오류가 추가되었습니다. 문제가 포함된 플러그 인의 컨텍스트에서 즉시 발생해야 합니다. 아래쪽 오류는 일반적으로 사용자 지정 워크플로 활동과 관련된 다양한 상황에서도 발생할 수 있습니다. 다른 플러그 인의 문제 때문일 수 있습니다.
동기 플러그 인 내에서 데이터 작업과 관련된 오류가 발생할 때마다 전체 작업에 대한 트랜잭션이 종료됩니다.
자세한 내용은 Microsoft Dataverse의 확장 가능한 사용자 지정 디자인을 참조하세요.
일반적인 원인은 개발자가 성공할 수 있는 작업을 수행하려고 시도할 수 있다고 생각하므로 해당 작업을 블록에 try
/catch
래핑하고 실패할 때 오류를 삼켜야 한다는 것입니다.
이 패턴은 클라이언트 애플리케이션에서 작동할 수 있지만 플러그 인 실행 내에서 데이터 작업 실패로 인해 전체 트랜잭션이 롤백됩니다. 오류를 삼킬 수 없으므로 항상 .를 InvalidPluginExecutionException반환해야 합니다.
오류 "Sql 오류: 실행 시간 초과 만료됨"
오류 코드: -2147204783
오류 메시지: Sql 오류: '실행 제한 시간이 만료되었습니다. 작업이 완료되기 전에 시간 제한 기간이 경과했거나 서버가 응답하지 않습니다.'
원인
SQL 시간 제한 오류가 발생할 수 있는 다양한 이유가 있습니다. 그 중 세 가지는 여기에 설명되어 있습니다.
차단
SQL 시간 제한 오류의 가장 일반적인 원인은 작업이 다른 SQL 트랜잭션에 의해 차단된 리소스를 기다리고 있기 때문에 발생합니다. 이 오류는 Dataverse가 데이터의 무결성을 보호하고 사용자의 성능에 영향을 주는 장기 실행 요청으로부터의 결과입니다.
차단은 다른 동시 작업 때문일 수 있습니다. 코드는 테스트 환경에서 격리된 상태에서 잘 작동할 수 있으며 여러 사용자가 플러그 인에서 논리를 시작할 때만 발생하는 조건에 취약할 수 있습니다.
플러그 인을 작성할 때는 확장 가능한 사용자 지정을 디자인하는 방법을 이해해야 합니다. 자세한 내용은 Dataverse의 확장 가능한 사용자 지정 디자인을 참조하세요.
연계 작업
플러그 인에서 수행되는 특정 작업(예: 레코드 할당 또는 삭제)은 관련 레코드에 대한 연계 작업을 시작할 수 있습니다. 이러한 작업은 관련 레코드에 잠금을 적용하여 후속 데이터 작업이 차단될 수 있으며, 이로 인해 SQL 시간 제한이 발생할 수 있습니다.
이러한 연속 작업이 플러그 인의 데이터 작업에 미칠 수 있는 영향을 고려해야 합니다. 자세한 내용은 테이블 관계 동작을 참조하세요.
이러한 동작은 환경 간에 다르게 구성할 수 있으므로 환경이 동일한 방식으로 구성되지 않는 한 동작을 재현하기 어려울 수 있습니다.
새 테이블의 인덱스
플러그 인이 최근에 만든 테이블 또는 열을 사용하여 작업을 수행하는 경우 인덱스를 관리하는 일부 Azure SQL 기능이 며칠 후에 차이를 만들 수 있습니다.
사용자 권한으로 인한 오류
클라이언트 애플리케이션에서 사용자가 수행할 수 없는 명령을 사용하지 않도록 설정할 수 있습니다. 플러그 인 내에서는 이 기능이 없습니다. 코드에는 호출 사용자에게 수행할 권한이 없는 일부 자동화가 포함될 수 있습니다.
사용자의 컨텍스트에서 실행 값을 해당 사용자로 설정하여 올바른 권한이 있는 것으로 알려진 사용자의 컨텍스트에서 실행할 플러그 인을 등록할 수 있습니다. 또는 다른 사용자를 가장하여 작업을 실행할 수 있습니다. 자세한 내용은 다음을 참조하세요.
사용하지 않도록 설정된 사용자의 컨텍스트에서 실행할 때 오류 발생
플러그 인이 비활성화된 사용자의 컨텍스트에서 실행되면 다음 오류가 반환됩니다.
오류 메시지: System.ServiceModel.FaultException'1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: OrganizationContext=<Context의 SystemUserId=<User-ID>를 가진 사용자가 비활성화>되었습니다. 비활성화된 사용자는 시스템에 액세스할 수 없습니다. 이 사용자를 사용하도록 설정하는 것이 좋습니다. 또한 사용자에게 할당된 라이선스가 없는 경우 사용자가 비활성화됩니다.
이 오류를 해결하려면 쿼리를 실행하여 비활성화된 사용자에게 등록된 단계와 연결된 플러그 인 및 SdkMessage
세부 정보를 찾을 수 있습니다.
https://<env-url>/api/data/v9.2/sdkmessageprocessingsteps
?$filter=_impersonatinguserid_value eq '<disabled-userId-from-error>'&
$expand=plugintypeid($select=name,friendlyname,assemblyname;
$expand=pluginassemblyid($select=solutionid,name,isolationmode)),sdkmessageid($select=solutionid,name)&
$select=solutionid,name,stage,_impersonatinguserid_value,mode
오류 "샌드박스에 컨텍스트를 보낼 때 메시지 크기 초과"
오류 코드: -2147220970
오류 메시지: 샌드박스에 컨텍스트를 보낼 때 메시지 크기가 초과되었습니다. 메시지 크기: ### Mb
이 오류는 메시지 페이로드가 116.85MB보다 크고 메시지에 대한 플러그 인이 등록된 경우에 발생합니다. 오류 메시지에는 이 오류를 발생시킨 페이로드의 크기가 포함됩니다.
이 제한은 애플리케이션을 실행하는 사용자가 리소스 제약 조건에 따라 서로 간섭할 수 없도록 하는 데 도움이 됩니다. 이 제한은 Dataverse 플랫폼의 가용성 및 성능 특성을 위협하는 비정상적으로 큰 메시지 페이로드로부터 보호 수준을 제공하는 데 도움이 됩니다.
116.85MB는 이 경우에 드물게 발생할 수 있을 만큼 충분히 큽니다. 이 경우 발생할 가능성이 가장 큰 상황은 큰 이진 파일이 포함된 여러 관련 레코드가 있는 레코드를 검색하는 경우입니다.
이 오류가 표시되면 다음을 수행할 수 있습니다.
- 메시지에 대한 플러그 인을 제거합니다. 메시지에 등록된 플러그 인이 없으면 오류 없이 작업이 완료됩니다.
- 사용자 지정 클라이언트에서 오류가 발생하는 경우 단일 작업에서 작업을 수행하지 않도록 코드를 수정할 수 있습니다. 대신 작은 부분에서 데이터를 검색하는 코드를 작성합니다.
오류 "지정된 키가 사전에 없습니다."
Dataverse는 키 및 값 컬렉션을 나타내는 추상 DataCollection<TKey,TValue> 클래스에서 파생된 클래스를 자주 사용합니다. 예를 들어 플러그 인을 사용하면 IExecutionContext.InputParameters 속성이 ParameterCollection 클래스에서 DataCollection<TKey,TValue> 파생됩니다. 이러한 클래스는 기본적으로 키 이름을 사용하여 특정 값에 액세스하는 사전 개체입니다.
오류 코드
이 오류는 코드의 키 값이 컬렉션에 없는 경우에 발생합니다. 결과는 플랫폼 오류가 아닌 런타임 오류입니다. 플러그 인 내에서 이 오류가 발생하면 오류 코드는 오류가 발견되었는지 여부에 따라 달라집니다.
플러그 인의 핸들 예외에 설명된 대로 개발자가 예외를 catch하고 반환InvalidPluginExecutionException하는 경우 다음 오류가 반환됩니다.
오류 코드: -2147220891
오류 메시지: ISV 코드가 작업을 중단했습니다.
그러나 이 오류는 개발자가 제대로 catch하지 못하고 다음 오류가 반환되는 것이 일반적입니다.
오류 코드: -2147220956
오류 메시지: ISV 코드에서 예기치 않은 오류가 발생했습니다.
참고 항목
"ISV"는 독립 소프트웨어 공급업체를 의미합니다.
원인
이 오류는 디자인 타임에 자주 발생하며 맞춤법이 잘못되었거나 잘못된 대/소문자를 사용 중일 수 있습니다. 키 값은 대/소문자를 구분합니다.
런타임에 이 오류는 개발자가 값이 없을 때 존재한다고 가정하기 때문에 자주 발생합니다. 예를 들어 테이블 업데이트에 등록된 플러그 인에서는 변경된 값만 .Attributes 컬렉션에 Entity포함됩니다.
해결
이 오류를 해결하려면 키를 사용하여 값에 액세스하기 전에 키가 있는지 확인해야 합니다.
예를 들어 테이블 열에 액세스할 때 다음 코드와 같이 .Contains(String) 메서드를 사용하여 Entity테이블에 열이 있는지 확인할 수 있습니다.
// Obtain the execution context from the service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// The InputParameters collection contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
// Obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
//Check whether the name attribute exists.
if(entity.Contains("name"))
{
string name = entity["name"];
}
일부 개발자는 테이블 열에 Entity액세스할 때 이 오류를 방지하기 위해 .GetAttributeValue<T>(String) 메서드를 사용합니다. 이 메서드는 열이 없는 경우 형식의 기본값을 반환합니다. 기본값이 null이면 이 메서드는 예상대로 작동합니다. 그러나 기본값이 null을 반환하지 않는 경우(예: a와 DateTime
같이) 반환되는 값은 null이 1/1/0001 00:00
아닌 것입니다.
오류 "현재 트랜잭션에 이미 설정된 것과 다른 격리 수준으로 트랜잭션을 시작할 수 없습니다."
오류 코드: -2147220989
오류 메시지: 현재 트랜잭션에 이미 설정된 것과 다른 격리 수준으로 트랜잭션을 시작할 수 없습니다.
플러그 인은 비즈니스 논리를 지원하기 위한 것입니다. 동기 플러그 인 내에서 데이터 스키마의 일부를 수정하는 것은 지원되지 않습니다. 이러한 작업은 종종 더 오래 걸리며 애플리케이션에서 사용하는 캐시된 메타데이터가 동기화되지 않을 수 있습니다. 그러나 이러한 작업은 비동기적으로 실행되도록 등록된 플러그 인 단계에서 수행할 수 있습니다.
알려진 문제: Activity.RegardingObjectId 이름 값이 플러그 인으로 설정되지 않음
이 문제의 가장 일반적인 증상은 활동 레코드의 관련 필드가 기본 이름 특성 값이 아니라 표시 (No Name)
된다는 것입니다.
플러그 인 내에서 EntityReference 값을 사용하여 조회 특성을 설정할 수 있습니다. EntityReference.Name 속성은 필요하지 않습니다. 일반적으로 Dataverse가 설정하기 때문에 조회 특성 값을 설정할 때 포함할 필요가 없습니다. 이벤트 파이프라인의 사전 작업 단계에서 이와 같은 값을 설정해야 합니다. 자세한 내용은 이벤트 실행 파이프라인을 참조하세요.
이 규칙의 예외는 ActivityPointer.RegardingObjectId 조회를 설정할 때입니다. 이 조회에서 ActivityPointer
파생된 모든 엔터티 형식이 상속됩니다. 기본적으로 약속, 채팅, 전자 메일, 팩스, 편지, PhoneCall, RecurringAppointmentMaster 및 활동 유형으로 만든 사용자 지정 테이블이 포함됩니다. 자세한 내용은 작업 테이블을 참조 하세요.
PreOperation 단계에서 이 값을 설정하면 Dataverse에서 이름 값이 추가되지 않습니다. 값이 null이고 레코드를 검색할 때 이 값을 포함해야 하는 형식이 지정된 값이 없습니다.
해결 방법
두 가지 방법으로 이 문제를 해결할 수 있습니다.
- 조회 특성의 값을 설정하기 전에 올바른 기본 이름 필드 값으로 EntityReference.Name 속성 값을 설정할 수 있습니다.
- 사전 실행 단계가 아닌 PreValidation 단계에서 조회 값을 설정할 수 있습니다.