System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键:DYNAMICS 365 Plugin [英] System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary: DYNAMICS 365 Plugin

查看:88
本文介绍了System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键:DYNAMICS 365 Plugin的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

未处理的异常: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: 意外异常来自插件(执行):DCWIMS.Plugins.UnitPlugin:System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键.详细信息:<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><错误代码>-2147220956</错误代码><ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic"/><Message>来自插件的意外异常(执行):DCWIMS.Plugins.UnitPlugin:System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键.</Message><时间戳>2018-07-20T17:47:36.4061434Z</时间戳><ExceptionRetriable>false</ExceptionRetriable><ExceptionSource i:nil="true"/><InnerFault i:nil="true"/><OriginalException i:nil="true"/><跟踪文本>[插件:DCWIMS.Plugins.UnitPlugin][dbb33fa1-448c-e811-815c-480fcff4b5b1:预创建合约]发生错误:System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键.在 System.Collections.Generic.Dictionary`2.get_Item(TKey 键)在 Microsoft.Xrm.Sdk.AttributeCollection.get_Item(String attributeName)在 DCWIMS.Plugins.UnitPlugin.Execute(IServiceProvider serviceProvider)</TraceText></组织服务故障

>

解决方案

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

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

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

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

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

遗憾的是,即使您模拟,您也无法真正解析 fetchXml 并对其进行验证.

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

service.Create(alterunit);

When I run a Unit Test for my plugin I get the following Exception being Thrown:

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

The following link will give you the stack trace: Stacktrace

Even when I deploy & register my plugin to the online instance, I would get the same message!! My Plugin code looks like this:

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;

                }
            }
        }
    }


}

Here is the code for Eachday:

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;
        }
    }
}

My unit test looks like this:

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);

            }
        }
    }
}

Even when I registered the plugin on the actual CRM instance I got this error message:

Here is the log file retrieved from 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

>

解决方案

Unit testing ain't my forte. But I will try to guide you.

First, my opinion on unit testing. I don't value it much in Dynamics because it's an environment that is hard to control. You can test that your business logic is well implemented in the plugin but a javascript, a business rule, a workflow or an action can derail your logic. Your plugin can say blue but an async workflow change it to red right after.

Instead, I prefer UI testing. It will test the process from A to Z and force the execution of all the components mentioned above. Here's a nice framework I recommend: https://github.com/Microsoft/EasyRepro

That being said. To solve your problem you also need to mock the Organization Service and return fake data. By that, I mean this line:

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

Sadly, even if you mock, you can't really parse the fetchXml and validate it.

Then, after returning the fake data you should assert that this line, create an entity with the right sums:

service.Create(alterunit);

这篇关于System.Collections.Generic.KeyNotFoundException:字典中不存在给定的键:DYNAMICS 365 Plugin的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆