System.Collections.Generic.KeyNotFoundException:字典中不存在给定的密钥:DYNAMICS 365插件

时间:2018-07-19 18:37:24

标签: unit-testing plugins dynamics-365

当我为我的插件运行单元测试时,会抛出以下异常:

Message: Test method Plugins.Tests.UnitTest1.TestUnitPlugin threw exception: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.

以下链接将为您提供堆栈跟踪: Stacktrace

即使我将插件部署并注册到在线实例中,我也会收到相同的消息! 我的插件代码如下:

using System;
using System.Collections.Generic;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;


/// <summary>
/// This plugin takes the data provided in the contract lines and makes Unit Orders.. Inside the unit orders, an Alter Unit Orders table is present.
/// The Alter Unit Orders table describes the daily order for each day in the contract's duration. 
/// </summary>

namespace DCWIMS.Plugins
{
    [CrmPluginRegistration(MessageNameEnum.Update,
    "contract",
    StageEnum.PreOperation,
    ExecutionModeEnum.Synchronous,
    "title",
    "Post-Update Contract",
    1000,
    IsolationModeEnum.Sandbox,
    Image1Name = "PreImage",
    Image1Type = ImageTypeEnum.PreImage,
    Image1Attributes = "title")]
    public class UnitPlugin : IPlugin
    {


        public void Execute(IServiceProvider serviceProvider)
        {
            // Extract the tracing service for use in debugging sandboxed plug-ins.
            // Wil be registering this plugin, thus will need to add tracing service related code.

            ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            //obtain execution context from service provider.
            IPluginExecutionContext context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));

            // The InputParameters colletion 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"];


                //Obtain Target Entity Id
                var targetId = entity.Id.ToString();

                //verify that the target entity represents the the contract entity and is active
                if (entity.LogicalName != "contract" && entity.GetAttributeValue<OptionSetValue>("statecode").Value != 0)
                    return;

                //obtain the organization service for web service calls.

                IOrganizationServiceFactory serviceFactory =
                        (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

                try
                {



                    //Get Contract StartDate
                    DateTime startDate = (DateTime)entity["activeon"];

                    //Get Contract EndDate
                    DateTime endDate = (DateTime)entity["expireson"];

                    //Get all weekdays in the contract duration
                    Eachday range = new Eachday();
                    var weekdays = range.WeekDay(startDate, endDate);       //weekdays list

                    //Get Contract Number
                    string contractNumber = (string)entity["contractnumber"];


                    //Query and aggregate each Weekday's order for the 3 different meal times...

                    //AM SNACK
                    string unitsum_am = @"  <fetch aggregate='true' distinct='false' >
                        <entity name='contract' >
                            <link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
                                <attribute name='new_mondayunits' alias='new_mondayunits_amsum' aggregate='sum' />
                                <attribute name='new_tuesdayunits' alias='new_tuesdayunits_amsum' aggregate='sum' />
                                <attribute name='new_unitswednesday' alias='new_unitswednesday_amsum' aggregate='sum' />                       
                                <attribute name='new_unitsthursday' alias='new_unitsthursday_amsum' aggregate='sum' />                            
                                <attribute name='new_unitsfriday' alias='new_unitsfriday_amsum' aggregate='sum' />
                                <filter type='and' >
                                    <condition value='100000001' attribute='new_servingtime' operator= 'eq' />                                       
                                    <condition value='0' attribute='statecode' operator= 'eq' />
                                    <condition value='" + targetId + @"' attribute='contractid' operator= 'eq' />
                                </filter >

                            </link-entity> 
                         </entity >
                     </fetch>";



                    EntityCollection unitsum_am_result =
                        service.RetrieveMultiple(new FetchExpression(unitsum_am));


                    var am_list = new List<int>();

                    foreach(var unit in unitsum_am_result.Entities)
                    {
                        var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_amsum"]).Value);
                        am_list.Add(mondaysum);
                        var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_amsum"]).Value);
                        am_list.Add(tuesdaysum);
                        var wednesdaysum= ((int)((AliasedValue)unit["new_unitswednesday_amsum"]).Value);
                        am_list.Add(wednesdaysum);
                        var thursdaysum= ((int)((AliasedValue)unit["new_unitsthursday_amsum"]).Value);
                        am_list.Add(thursdaysum);
                        var fridaysum= ((int)((AliasedValue)unit["new_unitsfriday_amsum"]).Value);
                        am_list.Add(fridaysum);
                    }






                    //LUNCH
                    string unitsum_lunch = @"   <fetch aggregate='true' distinct='false' >
                        <entity name='contract' >
                            <link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
                                <attribute name='new_mondayunits' alias='new_mondayunits_lunchsum' aggregate='sum' />
                                <attribute name='new_tuesdayunits' alias='new_tuesdayunits_lunchsum' aggregate='sum' />
                                <attribute name='new_unitswednesday' alias='new_unitswednesday_lunchsum' aggregate='sum' />                       
                                <attribute name='new_unitsthursday' alias='new_unitsthursday_lunchsum' aggregate='sum' />                            
                                <attribute name='new_unitsfriday' alias='new_unitsfriday_lunchsum' aggregate='sum' />
                                <filter type='and' >
                                    <condition value='100000002' attribute='new_servingtime' operator= 'eq' />                                       
                                    <condition value='0' attribute='statecode' operator= 'eq' />
                                    <condition value='" + targetId + @"' attribute='contractid' operator= 'eq' />
                                </filter >                                          
                            </link-entity> 
                         </entity >
                     </fetch>";

                    EntityCollection unitsum_lunch_result =
                        service.RetrieveMultiple(new FetchExpression(unitsum_lunch));

                    var lunch_list = new List<int>();
                    foreach (var unit in unitsum_lunch_result.Entities)
                    {
                        var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_lunchsum"]).Value);
                        lunch_list.Add(mondaysum);
                        var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_lunchsum"]).Value);
                        lunch_list.Add(tuesdaysum);
                        var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_lunchsum"]).Value);
                        lunch_list.Add(wednesdaysum);
                        var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_lunchsum"]).Value);
                        lunch_list.Add(thursdaysum);
                        var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_lunchsum"]).Value);
                        lunch_list.Add(fridaysum);
                    }




                    //PM SNACK
                    string unitsum_pm = @"  <fetch aggregate='true' distinct='false' >
                        <entity name='contract' >
                            <link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
                                <attribute name='new_mondayunits' alias='new_mondayunits_pmsum' aggregate='sum' />
                                <attribute name='new_tuesdayunits' alias='new_tuesdayunits_pmsum' aggregate='sum' />
                                <attribute name='new_unitswednesday' alias='new_unitswednesday_pmsum' aggregate='sum' />                       
                                <attribute name='new_unitsthursday' alias='new_unitsthursday_pmsum' aggregate='sum' />                            
                                <attribute name='new_unitsfriday' alias='new_unitsfriday_pmsum' aggregate='sum' />
                                <filter type='and' >
                                    <condition value='100000003' attribute='new_servingtime' operator= 'eq' />                                       
                                    <condition value='0' attribute='statecode' operator= 'eq' />
                                    <condition value='" + targetId + @"' attribute='contractid' operator= 'eq' />
                                </filter >                                          
                            </link-entity> 
                         </entity >
                     </fetch>";

                    EntityCollection unitsum_pm_result =
                        service.RetrieveMultiple(new FetchExpression(unitsum_pm));

                    var pm_list = new List<int>();
                    foreach (var unit in unitsum_pm_result.Entities)
                    {
                        var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_pmsum"]).Value);
                        pm_list.Add(mondaysum);
                        var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_pmsum"]).Value);
                        pm_list.Add(tuesdaysum);
                        var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_pmsum"]).Value);
                        pm_list.Add(wednesdaysum);
                        var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_pmsum"]).Value);
                        pm_list.Add(thursdaysum);
                        var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_pmsum"]).Value);
                        pm_list.Add(fridaysum);
                    }



                    foreach(var day in weekdays)
                    {
                        var alterunit = new Entity("new_alterunitorder");
                        alterunit.Attributes.Add("new_orderdate", DateTime.Parse(day));

                        switch (day.Split(',')[0])
                        {
                            case "Monday":
                                alterunit.Attributes.Add("new_amsnack", am_list[0]);
                                alterunit.Attributes.Add("new_lunch", lunch_list[0]);
                                alterunit.Attributes.Add("new_pmsnack", pm_list[0]);
                                break;
                            case "Tuesday":
                                alterunit.Attributes.Add("new_amsnack", am_list[1]);
                                alterunit.Attributes.Add("new_lunch", lunch_list[1]);
                                alterunit.Attributes.Add("new_pmsnack", pm_list[1]);
                                break;
                            case "Wednesday":
                                alterunit.Attributes.Add("new_amsnack", am_list[2]);
                                alterunit.Attributes.Add("new_lunch", lunch_list[2]);
                                alterunit.Attributes.Add("new_pmsnack", pm_list[2]);
                                break;
                            case "Thursday":
                                alterunit.Attributes.Add("new_amsnack", am_list[3]);
                                alterunit.Attributes.Add("new_lunch", lunch_list[3]);
                                alterunit.Attributes.Add("new_pmsnack", pm_list[3]);
                                break;
                            case "Friday":
                                alterunit.Attributes.Add("new_amsnack", am_list[4]);
                                alterunit.Attributes.Add("new_lunch", lunch_list[4]);
                                alterunit.Attributes.Add("new_pmsnack", pm_list[4]);
                                break;
                            default:
                                Console.WriteLine($"An unexpected value ({day.Split(',')})");
                                break;

                        }

                    alterunit.Attributes.Add("new_name", contractNumber);                   //set the record name

                    //set the unit order record relation
                    alterunit.Attributes["new_orderlineid"] =                               
                        new EntityReference("new_alterunitorder", unitOrderId);             
                    service.Create(alterunit);        
                    }



                }

                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException("An error occured.. Phil is responsible.", ex);
                }

                catch (Exception ex)
                {
                    tracing.Trace("An Error Occured: {0}", ex.ToString());
                    throw;

                }
            }
        }
    }


}

以下是每天的代码:

using System;
using System.Collections.Generic;


namespace DCWIMS.Plugins
{
    public class Eachday
    {
        public List<string> WeekDay(DateTime from, DateTime thru)
        {
            List<string> days_list = new List<string>();
            for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
            {
                days_list.Add(day.ToLongDateString());
                if (day.DayOfWeek == DayOfWeek.Sunday || day.DayOfWeek == DayOfWeek.Saturday)
                    days_list.Remove(day.ToLongDateString());
            }

            return days_list;
        }
    }
}

我的单元测试如下:

using DCWIMS.Plugins;
using Microsoft.Crm.Sdk.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;

namespace Plugins.Tests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        [TestCategory("Unit Test")]
        public void TestUnitPlugin()
        {
            using (var pipline = new PluginPipeline(FakeMessageNames.Update, FakeStages.PreOperation, new Entity("contract")))
            {
                var plugin = new UnitPlugin();

                pipline.Execute(plugin);

            }
        }
    }
}

即使我在实际的CRM实例上注册了插件,我也收到以下错误消息:

Business Process Error

这是从CRM Online检索到的日志文件!

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.Detail: 
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
  <ActivityId>4025e0f8-eed5-4b7f-a3b1-52a9f2a6f2cc</ActivityId>
  <ErrorCode>-2147220956</ErrorCode>
  <ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
  <Message>Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.</Message>
  <Timestamp>2018-07-20T17:47:36.4061434Z</Timestamp>
  <ExceptionRetriable>false</ExceptionRetriable>
  <ExceptionSource i:nil="true" />
  <InnerFault i:nil="true" />
  <OriginalException i:nil="true" />
  <TraceText>

[Plugins: DCWIMS.Plugins.UnitPlugin]
[dbb33fa1-448c-e811-815c-480fcff4b5b1: Pre-Create Contract]

An Error Occured: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
   at Microsoft.Xrm.Sdk.AttributeCollection.get_Item(String attributeName)
   at DCWIMS.Plugins.UnitPlugin.Execute(IServiceProvider serviceProvider)


</TraceText>
</OrganizationServiceFault

>

1 个答案:

答案 0 :(得分:1)

单元测试不是我的强项。但我会尽力指导您。

首先,我对单元测试的看法。我对Dynamics不太重视,因为它是一个难以控制的环境。您可以测试您的业务逻辑是否在插件中得到了很好的实现,但是JavaScript,业务规则,工作流或操作可能会使您的逻辑脱轨。您的插件可以显示为蓝色,但是异步工作流之后会立即将其更改为红色。

相反,我更喜欢UI测试。它将测试从A到Z的过程,并强制执行上述所有组件。我建议您使用一个不错的框架:https://github.com/Microsoft/EasyRepro

话虽如此。要解决您的问题,您还需要模拟组织服务并返回假数据。我的意思是这一行:

EntityCollection unitsum_am_result = service.RetrieveMultiple(new FetchExpression(unitsum_am));

可悲的是,即使您进行了模拟,也无法真正解析fetchXml并对其进行验证。

然后,在返回伪数据之后,您应该断言这一行,创建一个具有正确总和的实体:

service.Create(alterunit);