locked
Plugin Development Generic.KeyNotFoundException Error RRS feed

  • Question

  • Hello Everyone,

    This is my first attempt at developing a Plugin as well as using C#, I'm just trying to create a simple calculation plugin with the plan to eventually make it more complex. I currently have this working as javascript on save. I am getting the follow error when I attempt to create the record by saving it. "System.Collections.Generic.KeyNotFoundException: the given key was not present in the dictionary"

    The variables used below are "new_totalsale" which is mapped over to the new record from the parent record on create. "new_splitpercentage" which the user would enter in the value for. And "new_actualsalesfromsplit" which would be filled in by the plugin running. I have it set up on post create because I'm thinking the mapped over value and input value wouldn't be saved to the crm yet so by creating it first it would allow those values to be submitted and thus calculated but perhaps that logic is wrong.

    Could someone please help me with what I'm doing wrong here? I've pieced most of this together by reading other blogs and forums but I think I'm confusing myself as everyone seems to do it a little differently and I don't have much C# knowledge.

    Thanks.

    namespace CalcSalesSplit.Plugins1 { using System; using System.ServiceModel; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Query; public class PostSalesSplitCreate: Plugin { public PostSalesSplitCreate() : base(typeof(PostSalesSplitCreate)) { base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "Create", "new_salescommissions", new Action<LocalPluginContext>(ExecutePostSalesSplitCreate))); } protected void ExecutePostSalesSplitCreate(LocalPluginContext localContext) { IPluginExecutionContext context = localContext.PluginExecutionContext; IOrganizationService service = localContext.OrganizationService; if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity) { Entity _Split = new Entity("new_salescommissions"); _Split = service.Retrieve("new_salescommissions", ((EntityReference)_Split["new_salescommssions"]).Id, new ColumnSet(true)); try { Money total = (Money)_Split.Attributes["new_totalsale"]; decimal dtotal = total.Value; decimal percentage = ((decimal)_Split["new_splitpercentage"]); decimal calculation = dtotal * percentage; _Split["new_actualsalesfromsplit"] = new Money(calculation);
    service.Update(_Split); } catch (FaultException ex) { throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex); } } } } }




    Thursday, October 24, 2013 5:01 PM

Answers

  • [Edit] I end up solving this myself.

    So I think I have solved most of the troubleshooting issues. I had several things but now the code runs but nothing happens. The field that should get populated remains blank. Below is my code. I have two thoughts on what might be the issue but can't seem to pin point them.

    1. The plugin is running but it isn't submitting the value to the CRM to be saved to the database. This would be the final line of code after all the calculations? Something like the submitMode("always") in javascript?

    2. I'm using the wrong execution mode, pre/post operation, etc when I set up the workflow (which can you change this once set up? I just keep recreating a new plugin each time because I can't seem to figure out where this might be to change?)

    Below is the code. Again I greatly appreciate any help. I feel like I'm so close and now that I get no more errors but the field isn't populated I'm stuck. I've tried just about every combination of pre/post operation and execution mode and same results each time. Nothing happens but no errors.

    // <copyright file="PreSalesSplitUpdate.cs" company="Microsoft">
    // Copyright (c) 2013 All Rights Reserved
    // </copyright>
    // <author>Microsoft</author>
    // <date>10/25/2013 10:42:09 AM</date>
    // <summary>Implements the PreSalesSplitUpdate Plugin.</summary>
    // <auto-generated>
    //     This code was generated by a tool.
    //     Runtime Version:4.0.30319.1
    // </auto-generated>
    namespace CrmPackage2.Plugins1
    {
        using System;
        using System.ServiceModel;
        using Microsoft.Xrm.Sdk;
        using Microsoft.Xrm.Sdk.Query;
    
        /// <summary>
        /// PreSalesSplitUpdate Plugin.
        /// Fires when the following attributes are updated:
        /// All Attributes
        /// </summary>    
        public class PreSalesSplitUpdate: Plugin
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="PreSalesSplitUpdate"/> class.
            /// </summary>
            public PreSalesSplitUpdate()
                : base(typeof(PreSalesSplitUpdate))
            {
                base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Update", "new_salescommissions", new Action<LocalPluginContext>(ExecutePreSalesSplitUpdate)));
    
                // Note : you can register for more events here if this plugin is not specific to an individual entity and message combination.
                // You may also need to update your RegisterFile.crmregister plug-in registration file to reflect any change.
            }
    
            /// <summary>
            /// Executes the plug-in.
            /// </summary>
            /// <param name="localContext">The <see cref="LocalPluginContext"/> which contains the
            /// <see cref="IPluginExecutionContext"/>,
            /// <see cref="IOrganizationService"/>
            /// and <see cref="ITracingService"/>
            /// </param>
            /// <remarks>
            /// For improved performance, Microsoft Dynamics CRM caches plug-in instances.
            /// The plug-in's Execute method should be written to be stateless as the constructor
            /// is not called for every invocation of the plug-in. Also, multiple system threads
            /// could execute the plug-in at the same time. All per invocation state information
            /// is stored in the context. This means that you should not use global variables in plug-ins.
            /// </remarks>
            protected void ExecutePreSalesSplitUpdate(LocalPluginContext localContext)
            {
    
                IPluginExecutionContext context = localContext.PluginExecutionContext;
                IOrganizationService service = localContext.OrganizationService;
                ITracingService tracingService = localContext.TracingService;
    
    
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
    
                    Entity _Split = (Entity)context.InputParameters["Target"];
    
                    _Split = service.Retrieve("new_salescommissions", _Split.Id, new ColumnSet("new_totalsaleamount", "new_splitpercentage", "new_actualsalesfromsplit"));
                    Money total = ((Money)_Split.Attributes["new_totalsaleamount"]);
                    decimal dtotal = total.Value;
                    decimal percentage = ((decimal)_Split["new_splitpercentage"]);
                    decimal calculation = dtotal * percentage;
                    _Split.Attributes["new_actualsalesfromsplit"] = new Money(calculation);
                }
            }
        }
    }


    Friday, October 25, 2013 4:49 PM

All replies

  • Your best option is to debug the plugin to work out which line causes the error.

    Looking at the code, your most likely problem is that you're using AllColumns on the Retrieve, then trying to Update the same entity. CRM will treat this as if you're trying to update all attributes, including those that are not ValidForUpdate (e.g. modifiedby). To resolve this, either:

    • In your Retrieve, only specify the columns that you need to retrieve
    • Or, use a different entity instance for the update, with the just the Id and the attribute you want to update set

    Microsoft CRM MVP - http://mscrmuk.blogspot.com/ http://www.excitation.co.uk

    Friday, October 25, 2013 5:07 AM
    Moderator
  • [Edit] I end up solving this myself.

    So I think I have solved most of the troubleshooting issues. I had several things but now the code runs but nothing happens. The field that should get populated remains blank. Below is my code. I have two thoughts on what might be the issue but can't seem to pin point them.

    1. The plugin is running but it isn't submitting the value to the CRM to be saved to the database. This would be the final line of code after all the calculations? Something like the submitMode("always") in javascript?

    2. I'm using the wrong execution mode, pre/post operation, etc when I set up the workflow (which can you change this once set up? I just keep recreating a new plugin each time because I can't seem to figure out where this might be to change?)

    Below is the code. Again I greatly appreciate any help. I feel like I'm so close and now that I get no more errors but the field isn't populated I'm stuck. I've tried just about every combination of pre/post operation and execution mode and same results each time. Nothing happens but no errors.

    // <copyright file="PreSalesSplitUpdate.cs" company="Microsoft">
    // Copyright (c) 2013 All Rights Reserved
    // </copyright>
    // <author>Microsoft</author>
    // <date>10/25/2013 10:42:09 AM</date>
    // <summary>Implements the PreSalesSplitUpdate Plugin.</summary>
    // <auto-generated>
    //     This code was generated by a tool.
    //     Runtime Version:4.0.30319.1
    // </auto-generated>
    namespace CrmPackage2.Plugins1
    {
        using System;
        using System.ServiceModel;
        using Microsoft.Xrm.Sdk;
        using Microsoft.Xrm.Sdk.Query;
    
        /// <summary>
        /// PreSalesSplitUpdate Plugin.
        /// Fires when the following attributes are updated:
        /// All Attributes
        /// </summary>    
        public class PreSalesSplitUpdate: Plugin
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="PreSalesSplitUpdate"/> class.
            /// </summary>
            public PreSalesSplitUpdate()
                : base(typeof(PreSalesSplitUpdate))
            {
                base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Update", "new_salescommissions", new Action<LocalPluginContext>(ExecutePreSalesSplitUpdate)));
    
                // Note : you can register for more events here if this plugin is not specific to an individual entity and message combination.
                // You may also need to update your RegisterFile.crmregister plug-in registration file to reflect any change.
            }
    
            /// <summary>
            /// Executes the plug-in.
            /// </summary>
            /// <param name="localContext">The <see cref="LocalPluginContext"/> which contains the
            /// <see cref="IPluginExecutionContext"/>,
            /// <see cref="IOrganizationService"/>
            /// and <see cref="ITracingService"/>
            /// </param>
            /// <remarks>
            /// For improved performance, Microsoft Dynamics CRM caches plug-in instances.
            /// The plug-in's Execute method should be written to be stateless as the constructor
            /// is not called for every invocation of the plug-in. Also, multiple system threads
            /// could execute the plug-in at the same time. All per invocation state information
            /// is stored in the context. This means that you should not use global variables in plug-ins.
            /// </remarks>
            protected void ExecutePreSalesSplitUpdate(LocalPluginContext localContext)
            {
    
                IPluginExecutionContext context = localContext.PluginExecutionContext;
                IOrganizationService service = localContext.OrganizationService;
                ITracingService tracingService = localContext.TracingService;
    
    
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
    
                    Entity _Split = (Entity)context.InputParameters["Target"];
    
                    _Split = service.Retrieve("new_salescommissions", _Split.Id, new ColumnSet("new_totalsaleamount", "new_splitpercentage", "new_actualsalesfromsplit"));
                    Money total = ((Money)_Split.Attributes["new_totalsaleamount"]);
                    decimal dtotal = total.Value;
                    decimal percentage = ((decimal)_Split["new_splitpercentage"]);
                    decimal calculation = dtotal * percentage;
                    _Split.Attributes["new_actualsalesfromsplit"] = new Money(calculation);
                }
            }
        }
    }


    Friday, October 25, 2013 4:49 PM