locked
CRM 2013. Case create event (PreImage, PostImage etc) RRS feed

  • Question

  • Hi!

    In continue of my question post.

    Now i trying to create plugin calling event on case create (after it was created and saved). Plugin gets some data from some account entity and puts it into the case entity after it created. cases are created automatically.

    And again faced with similar problem (the same error as in previous post).
    Need a recommendation about correct settings of plugin event.

                Entity postImageEntity = (context.PostEntityImages != null && context.PostEntityImages.Contains(this.postImageAlias)) ? context.PostEntityImages[this.postImageAlias] : null;
    
                if (context.PostEntityImages.Contains("PostImage") && context.PostEntityImages["PostImage"] is Entity && context.Depth < 2)
                {
                    try
                    {
    //do something
    }}}
    Thanks for suggestions.

    Thursday, May 29, 2014 8:34 PM

Answers

  • Also Crm does not lock the records itself. But plugin is a transaction so that , for example you are in Post Update Message in any entity.During plugin is working , you can not update the record from outside plugin ( Wcf service , referenced dll etc..) .  While your plugin is running , try to select the record from database , you will see , you can not get the record , because your plugin transaction is not finished;)

    Thinking of below code works is CASE POST UPDATE message. It will work with no error.Believe me , i used these code hundreds of times in the projects:)

     string customerName = ((EntityReference)entity.Attributes["customerid"]).Name;
     entity["description"] = customerName;
     service.Update(entity);

    Also you can do this;

    Thinking of below code works is CASE PRE UPDATE message

     string customerName = ((EntityReference)entity.Attributes["customerid"]).Name;
     entity["description"] = customerName; entity.Attributes.Add("description",customerName 

    You don not need to update the entity.Because you are in the pre stage.

    I hope you get the point;)


    Friday, May 30, 2014 12:56 PM
  • Yes you are in right way : ) Be careful your fetchxml is not true! Pls create your FetchXml from Advancedfind.

     ((EntityReference)postImageEntity.Attributes["customerid"]).Name  can be null but  ((EntityReference)postImageEntity.Attributes["customerid"]).Id can not be null , I am sure about that. You can go through from the Id.

    string customerid= ((EntityReference)postImageEntity.Attributes["customerid"]).Id; 
                        string fetch = @"
                            <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                <entity name='contact'> 
                                    <attribute name='parentcustomerid' /> 
                                    <filter type='and'> 
                                        <condition attribute='contactid' operator='eq' uitype='contact' value={'" + customerid+ "'} />
                                    </filter> 
                                </entity>
                            </fetch>";
                        EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetch));

    • Marked as answer by Dismantled Monday, June 2, 2014 3:13 PM
    Monday, June 2, 2014 2:10 PM
  • You give your Image entity a name. That's all. You are using developer toolkit so crm do the work for you.You can see that from your plugin class.

    Also i suggest you to not filter the image attributes. Because if you do not select the attribute , it will be null in your Image. You do not need the field for now , but in the future you may need it. 
    • Marked as answer by Dismantled Monday, June 2, 2014 3:14 PM
    Monday, June 2, 2014 2:39 PM

All replies

  • Actually... I can't find any scenario for your code. But the general answer - that would not work because operation (i.e. record) is in transaction and you are trying to update records that is locked with transaction. Pretty sure that you will get exception or something like.

    Dynamics CRM MVP/ Technical Evangelist at SlickData LLC
    My blog

    Thursday, May 29, 2014 9:25 PM
    Moderator
  • Hi dismantled,

    i always use Pre messages for these scenarios. You can get the data that you want and easily add  your context.

    Here is a life time project example that i wrote before. This code block is running on Pre Case Create.


    Entity entity = (Entity)localContext.PluginExecutionContext.InputParameters["Target"];

    IOrganizationService service = (IOrganizationService)localContext.OrganizationService;

    MyContext context = new MyContext(service);

    List<SystemUser> user = (from u in context.SystemUserSet                                       

      u.TerritoryId.Id.Equals(((EntityReference)caseEntity["trd_territoryid"]).Id) 

                             select new SystemUser     

                                {                     

                                SystemUserId = u.SystemUserId == null ? Guid.Empty : u.SystemUserId.Value 

                                  }                         

          ).ToList<SystemUser>();

    if (user.Count > 0)     

            {                      

                           if(user [0].SystemUserId  != Guid.Empty)     

                   {                    

       caseEntity.Attributes.Add("trd_relatedbosid", new EntityReference(SystemUser.EntityLogicalName.ToString(), user[0].SystemUserId ));

    }                

       }



    Friday, May 30, 2014 12:40 AM
  • Hi Polat,

    "Pre" it is pre-validation or pre-operation?

    I try your scenarion on Pre-Val - it gives me exception error.

    What config do you use?

    Friday, May 30, 2014 6:55 AM
  • Here is the full code

    namespace MyPlgin.Plugins
    {
        using System;
        using System.ServiceModel;
        using Microsoft.Xrm.Sdk;
        using Microsoft.Xrm.Sdk.Query;
        using Microsoft.Xrm.Sdk.Messages;
        using Microsoft.Xrm.Sdk.Metadata;
    
        public class PostCaseUpdate: Plugin
        {
            private readonly string postImageAlias = "PostImage";
    
            public PostCaseUpdate()
                : base(typeof(PostCaseUpdate))
            {
                base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "Update", "incident", new Action<LocalPluginContext>(ExecutePostCaseUpdate)));
            }
    
            protected void ExecutePostCaseUpdate(LocalPluginContext localContext)
            {
                if (localContext == null)
                {
                    throw new ArgumentNullException("localContext");
                }
    
                IPluginExecutionContext context = localContext.PluginExecutionContext;
                IOrganizationService service = localContext.OrganizationService;
                
                Entity postImageEntity = (context.PostEntityImages != null && context.PostEntityImages.Contains(this.postImageAlias)) ? context.PostEntityImages[this.postImageAlias] : null;
    
                if (context.PostEntityImages.Contains("PostImage") && context.PostEntityImages["PostImage"] is Entity && context.Depth < 2)
                {
                    try
                    {
                        string customerName = ((EntityReference)postImageEntity.Attributes["customerid"]).Name;
                        string fetch = @"
                            <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                <entity name='contact'> 
                                    <attribute name='parentcustomerid' /> 
                                    <filter type='and'> 
                                        <condition attribute='fullname' operator='eq' uitype='account' value='" + customerName + @"' />
                                    </filter> 
                                </entity>
                            </fetch>";
                        EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetch));
                        string accountName = ((EntityReference)result[0].Attributes["parentcustomerid"]).Name;
                        fetch = @"
                            <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                <entity name='account'> 
                                    <attribute name='accountcategorycode' /> 
                                    <filter type='and'> 
                                        <condition attribute='name' operator='eq' uitype='account' value='" + accountName + @"' />
                                    </filter> 
                                </entity>
                            </fetch>";
                        result = service.RetrieveMultiple(new FetchExpression(fetch));
                        int categoryCodeValue = ((OptionSetValue)result[0].Attributes["accountcategorycode"]).Value;
    
                        RetrieveAttributeRequest retrieveAttributeRequest = new RetrieveAttributeRequest
                        {EntityLogicalName = "account", LogicalName = "accountcategorycode", RetrieveAsIfPublished = true};
                        RetrieveAttributeResponse retrieveAttributeResponse = (RetrieveAttributeResponse)service.Execute(retrieveAttributeRequest);
                        Microsoft.Xrm.Sdk.Metadata.PicklistAttributeMetadata retrievedPicklistAttributeMetadata = (Microsoft.Xrm.Sdk.Metadata.PicklistAttributeMetadata)retrieveAttributeResponse.AttributeMetadata;
                        OptionMetadata[] optionList = retrievedPicklistAttributeMetadata.OptionSet.Options.ToArray();
    
                        string selectedOptionLabel = string.Empty;
                        foreach (OptionMetadata optionMD in optionList)
                        {
                            if (optionMD.Value == categoryCodeValue)
                            {
                                selectedOptionLabel = optionMD.Label.UserLocalizedLabel.Label;
                            }
                        }
    
                        postImageEntity["new_cat"] = selectedOptionLabel;
                        service.Update(postImageEntity);
                    }
                    catch (FaultException ex)
                    {
                        throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
                    }
                }
            }
        }
    }


    And config

    • Edited by Dismantled Friday, May 30, 2014 6:59 AM
    Friday, May 30, 2014 6:57 AM
  • that would not work because operation (i.e. record) is in transaction and you are trying to update records that is locked with transaction.

    I don't need to update record in transaction. i need to update it after transaction is finished. Could you show any code examples, how to update record after it was created? Thank you.

    Friday, May 30, 2014 7:36 AM
  • Attempt #2371

    Trying to put Customer name (customerid) to the description field on Case create.

    namespace CrmPackage1.Plugins1
    {
        using System;
        using System.ServiceModel;
        using Microsoft.Xrm.Sdk;
        
        public class PreCaseCreate: Plugin
        {
    
            public PreCaseCreate()
                : base(typeof(PreCaseCreate))
            {
                base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Create", "incident", new Action<LocalPluginContext>(ExecutePreCaseCreate)));
            }
    
            protected void ExecutePreCaseCreate(LocalPluginContext localContext)
            {
                if (localContext == null)
                {
                    throw new ArgumentNullException("localContext");
                }
    
                IPluginExecutionContext context = localContext.PluginExecutionContext;
                IOrganizationService service = localContext.OrganizationService;
    
                Entity entity = (Entity)context.InputParameters["Target"];
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity && context.Depth < 2)
                {
                    try
                    {
                        string customerName = ((EntityReference)entity.Attributes["customerid"]).Name;
                        entity["description"] = customerName;
                        service.Update(entity);
                    }
                    catch (FaultException ex)
                    {
                        throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
                    }
                }
            }
        }
    }
    

    Friday, May 30, 2014 9:40 AM
  • Hi,

    In the example above , you do not need to  update the entity.You are in the pre event stage and your record is not saved in db yet. 

    try                {                                     

    string customerName = ((EntityReference)entity.Attributes["customerid"]).Name; 

      entity["description"] = customerName; or entity.Attributes.Add("description",customerName )             

          }         

          catch (FaultException ex)             

    {               

        throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex); 

     }


      if you change your plugin to Post Event ( Post Create ) then you have to update your entity  because your record is already saved in db , but i do not prefer using Post message for these scenarios.

     




    Friday, May 30, 2014 12:40 PM
  • Also Crm does not lock the records itself. But plugin is a transaction so that , for example you are in Post Update Message in any entity.During plugin is working , you can not update the record from outside plugin ( Wcf service , referenced dll etc..) .  While your plugin is running , try to select the record from database , you will see , you can not get the record , because your plugin transaction is not finished;)

    Thinking of below code works is CASE POST UPDATE message. It will work with no error.Believe me , i used these code hundreds of times in the projects:)

     string customerName = ((EntityReference)entity.Attributes["customerid"]).Name;
     entity["description"] = customerName;
     service.Update(entity);

    Also you can do this;

    Thinking of below code works is CASE PRE UPDATE message

     string customerName = ((EntityReference)entity.Attributes["customerid"]).Name;
     entity["description"] = customerName; entity.Attributes.Add("description",customerName 

    You don not need to update the entity.Because you are in the pre stage.

    I hope you get the point;)


    Friday, May 30, 2014 12:56 PM
  • Many thanks Polat, your posts are very helpfull.

    CASE POST UPDATE and CASE PRE UPDATE must be setted in the plugin config window?

    CASE POST UPDATE - like on the pic below?

    CASE PRE UPDATE - like below?

    What about Alias Parameters?

    Friday, May 30, 2014 1:17 PM
  • Dismantled ,

    Yes , your configurations are absolutely true.

    I think you are asking İmage Aliases. You are using Crm developer toolkit , and tool automatically adds these to your plugin class .Is it your question?

    Friday, May 30, 2014 1:36 PM
  • I am sorry Polat, but nothing happens. Using this:

               IPluginExecutionContext context = localContext.PluginExecutionContext;
                IOrganizationService service = localContext.OrganizationService;
    
                //Entity entity = (Entity)context.InputParameters["Target"];
                //Entity entity = (context.PreEntityImages != null && context.PreEntityImages.Contains(this.preImageAlias)) ? context.PreEntityImages[this.preImageAlias] : null;
                Entity entity = (context.PostEntityImages != null && context.PostEntityImages.Contains(this.postImageAlias)) ? context.PostEntityImages[this.postImageAlias] : null;
    
                //if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity && context.Depth < 2)
                //if (context.PreEntityImages.Contains("PreImage") && context.PreEntityImages["PreImage"] is Entity && context.Depth < 2)
                if (context.PostEntityImages.Contains("PostImage") && context.PostEntityImages["PostImage"] is Entity && context.Depth < 2)
                {
                    try
                    {
                        string customerName = ((EntityReference)entity.Attributes["customerid"]).Name;
                        entity["description"] = customerName;
                        service.Update(entity);
                    }
                    catch (FaultException ex)
                    {
                        throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
                    }
                }
            }

    And i tried all three variants of Entity initialization (marked as comment in the code).

    This solution works perfect on record Update (open early created case and save) but does not work on event of new record create and save it.

    I am stuck :(.


    • Edited by Dismantled Friday, May 30, 2014 1:49 PM
    Friday, May 30, 2014 1:47 PM
  •  Your plugin can not be Update.

    Because when you create the record , update message is not triggered.If you change the message To POST CREATE CASE , this code will work.


    Friday, May 30, 2014 2:12 PM
  • Thanks Polat, but if you take a look on my first post you will see - this is those, from what i began. Could you provide any full example of plugin code, which update case entity record after it created? And it is must contain the event to call related object, some like case "customerid"? I begin to believe, what this is impossible.

    MVP wrote:

    Actually... I can't find any scenario for your code.



    • Edited by Dismantled Friday, May 30, 2014 11:40 PM
    Friday, May 30, 2014 11:40 PM
  • But the general answer - that would not work because operation (i.e. record) is in transaction and you are trying to update records that is locked with transaction. 

    1. Is it possible to wait condition while  transaction is finished and then start the plugin?

    2. Any workaround to get case customerid of the newly created cases?

    Saturday, May 31, 2014 12:32 PM
  • Dismantled,

    I think u did not understand the logic of the crm plugins.

    The code below When you create the case , it also update the case entity. This plugin work on Post Create Message of CASE. I tried this code and it is working perfectly.

    if (localContext == null)         

      {               

    throw new ArgumentNullException("localContext");       

      }           

    IPluginExecutionContext context = localContext.PluginExecutionContext;     

          if (context.Depth > 1)               

    return;           

    Entity caseentity = (Entity)localContext.PluginExecutionContext.InputParameters["Target"];

          IOrganizationService service = (IOrganizationService)localContext.OrganizationService; 

          MyContext context = new MyContext (service);         

      var account = (from a in context.AccountSet 

                         where a.AccountId.Value.Equals( "your account id") 

                          select a).ToList();       

    if (account.Count > 0)       

        {             

      if(caseentity.Attributes.Contains("description") 

                  {                   

    caseentity["description"] = account[0].Name; 

                  }             

      else               

    {                 

      caseentity.Attributes.Add("description",account[0].Name); 

                  }               

    service.Update(caseentity);         

      }



    Saturday, May 31, 2014 2:54 PM
  • Thanks Polat. The code below starts fine On Case Post Create. But it does not return correct "customerid" value (bold line).

            protected void ExecutePostCaseCreate(LocalPluginContext localContext)
            {
                if (localContext == null)
                {
                    throw new ArgumentNullException("localContext");
                }
    
                IPluginExecutionContext context = localContext.PluginExecutionContext;
                IOrganizationService service = (IOrganizationService)localContext.OrganizationService;
                if (context.Depth > 1)
                    return;
                //Entity caseentity = (context.PostEntityImages != null && context.PostEntityImages.Contains(this.postImageAlias)) ? context.PostEntityImages[this.postImageAlias] : null;
                Entity caseentity = (Entity)localContext.PluginExecutionContext.InputParameters["Target"];
                string customerName = ((EntityReference)caseentity.Attributes["customerid"]).Name;   
                caseentity["description"] = customerName;  
                service.Update(caseentity);          
            }

    Monday, June 2, 2014 7:00 AM
  • 

    Dismantled,

    What do you mean correct customerid. It returns null ? Sometime EntityReference name attribute can come null. Therefore ı suggest you to get it from POST IMAGE or select from the account as my example. 

    Monday, June 2, 2014 11:18 AM
  • 

     It returns null ?  

    Yes

    

     Therefore ı suggest you to get it from POST IMAGE

    It always returns null on POST IMAGE CREATE. Today I tried it again.

    In my condition it works fine only on POST IMAGE UPDATE.

    I think to find some workaround.

    Monday, June 2, 2014 11:51 AM
  • If POST image is also returns null , solution is simple. Build a query to get the related account.

      var account = (from a in context.AccountSet 

                         where a.AccountId.Value.Equals( "your account id") 

                          select a).ToList();       

    If you do not prefer using LINQ , you can use queryexpression to get the data.

    Monday, June 2, 2014 12:05 PM
  • I successfully apply the RetrieveMultiple method to get related data:

                        string customerName = ((EntityReference)postImageEntity.Attributes["customerid"]).Name; //always null on POST Create
                        string fetch = @"
                            <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                <entity name='contact'> 
                                    <attribute name='parentcustomerid' /> 
                                    <filter type='and'> 
                                        <condition attribute='fullname' operator='eq' uitype='account' value='" + customerName + @"' />
                                    </filter> 
                                </entity>
                            </fetch>";
                        EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetch));

    But before! Plugin needs to retrive the value of the Customer (or Account, or Contact etc) name (guid or fullname) to use it in the query :). Right?

    I can't use any constant id or name in my solution algorithm.


    • Edited by Dismantled Monday, June 2, 2014 1:55 PM
    Monday, June 2, 2014 1:29 PM
  • Yes you are in right way : ) Be careful your fetchxml is not true! Pls create your FetchXml from Advancedfind.

     ((EntityReference)postImageEntity.Attributes["customerid"]).Name  can be null but  ((EntityReference)postImageEntity.Attributes["customerid"]).Id can not be null , I am sure about that. You can go through from the Id.

    string customerid= ((EntityReference)postImageEntity.Attributes["customerid"]).Id; 
                        string fetch = @"
                            <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
                                <entity name='contact'> 
                                    <attribute name='parentcustomerid' /> 
                                    <filter type='and'> 
                                        <condition attribute='contactid' operator='eq' uitype='contact' value={'" + customerid+ "'} />
                                    </filter> 
                                </entity>
                            </fetch>";
                        EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetch));

    • Marked as answer by Dismantled Monday, June 2, 2014 3:13 PM
    Monday, June 2, 2014 2:10 PM
  • Thank you. I'll try your example at the nearest time.

    Could you tell me a little bit more about the Image Alias Parameters? Which needed to be checked? Those which will be return the data? Or those which will be updated? Or both? I want to understand this.


    • Edited by Dismantled Monday, June 2, 2014 2:27 PM
    Monday, June 2, 2014 2:26 PM
  • You give your Image entity a name. That's all. You are using developer toolkit so crm do the work for you.You can see that from your plugin class.

    Also i suggest you to not filter the image attributes. Because if you do not select the attribute , it will be null in your Image. You do not need the field for now , but in the future you may need it. 
    • Marked as answer by Dismantled Monday, June 2, 2014 3:14 PM
    Monday, June 2, 2014 2:39 PM
  • Yes! Yes! Yes! It's works! :-D

     string customerName = ((EntityReference)postImageEntity.Attributes["customerid"]).Name;

    Changing Name to Id (and Guid instead String) helped me. Correct:

    Guid customerId = ((EntityReference)postImageEntity.Attributes["customerid"]).Id;

    And  PostImage sure.

    Thank you Polat!

    BTW:

    Why "Name" retrieves correct (not null) on Update Message, but not on Create?



    • Edited by Dismantled Monday, June 2, 2014 3:15 PM
    Monday, June 2, 2014 3:12 PM
  • Dismantled,

    On create message , name attribute of entityreference can come null. I think it is a bug. So that in the project ı have never used name attibute of entity reference from  codebehind.

    Monday, June 2, 2014 4:05 PM