locked
CRM 2011 - (Plugins) Update parent lookup record - Error (please double check my code) RRS feed

  • Question

  • I have been working on CRM for last 2 months, i am still learning and each passing day my skill set is getting better. Special thanks to you all for guiding me through whenever i am stuck. For this issue, I have already wasted enough hours on hit/try, can please review my code and suggest if that is the correct way of updating lookup fields in parent record.

    Here is my scenario

    1.     I have Parent table "Account" and a child table "Account Address"
    2.     One Account can have multiple addresses, but only one address can be primary (i am already done with that part)
    3.     On the Account table i have a lookup field that will show me primary account address
    4.     My plugin is registered on the Update event for the AccountAddress entity, i have a PreImage for AccountAddress

    After i am done with Step 2, i have to update the data in the Lookup field in Account table, but i am getting an error at service.udpate(...) statement

    Here is my code for step 3 above:

                           // field new_account in the line below holds the account ID (i.e. Parent record)
                            Entity account = service.Retrieve("account", ((EntityReference)preMessageImage["new_account"]).Id, new ColumnSet(true));
                            tracer.Trace("Account address ID: " + preMessageImage.Id);
                            if (account.Contains("new_primaryaddress"))
                            {
                                tracer.Trace("within IF contains block");
                                account.Attributes["new_primaryaddress"] = preMessageImage.Id;
                            }
                            else
                            {
                                tracer.Trace("within else contains block");   // this block is executed
                                account.Attributes.Add("new_primaryaddress", preMessageImage.Id);   // preMessageImage.id has the account address with primary flag
                            }

                            tracer.Trace("before the account update");
                            service.Update(account);
                            tracer.Trace("after the account update");


    Error i am getting at service.udpate (trace log):

    Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: An error occurred in the AccountAddress_Update plug-in.System.InvalidCastException: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #AF74F16B

    Server stack trace:
       at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
       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.SandboxOrganizationService.Execute(String operation, Byte[] serializedRequest)
       at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
       at System.Runtime.Remoting.Messaging.StackBuilderSink.SyncProcessMessage(IMessage msg, Int32 methodPtr, Boolean fExecuteInContext)

    Exception rethrown at [1]:
       at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
       at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
       at Microsoft.Crm.Sandbox.ISandboxOrganizationService.Execute(String operation, Byte[] serializedRequest)
       at Microsoft.Crm.Sandbox.SandboxOrganizationServiceWrapper.ExecuteInternal(OrganizationRequest request)
       at TestingCRM.AccountAddress_Update.Execute(IServiceProvider serviceProvider)</Message>
      <Timestamp>2011-07-29T14:42:01.6101106Z</Timestamp>
      <InnerFault i:nil="true" />
      <TraceText>
    Friday, July 29, 2011 3:27 PM

Answers

  • Hey Daniel,

    We got everything working now, FINALLY................. thank you so much for guiding me all the way through...

    And i am sure must be curious about the solution. So the solution is very strange and i doesn't make any logical sense but i will tell what has happened:

    • As you know i was using the lookup field for AccountAddress as new_primaryaddress
    • I created a new lookup fields called new_primaryaddressid and placed the field on the form, so basically now i have two lookups
    • In the code i changed my field name from new_primaryaddress to new_primaryaddressid (new field) and it worked (strange)
    • Then i uncommented out my code for the old address and it worked for both (strange again):

                            if (account.Contains("ser_primaryaddress"))
                            {
                                tracer.Trace("within IF contains block");
                                account.Attributes["ser_primaryaddressid"] = new EntityReference(targetEntity.LogicalName, preMessageImage.Id);
                                account.Attributes["ser_primaryaddress"] = new EntityReference(targetEntity.LogicalName, preMessageImage.Id);
                            }
                            else
                            {
                                tracer.Trace("within else contains block");
                                account.Attributes.Add("ser_primaryaddressid", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
                                account.Attributes.Add("ser_primaryaddress", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
                            }

     

    • Then i went ahead and deleted the new field new_primaryaddressid from table and form and deleted related line in the code and i worked (this is very very very strange, becasue the field name has stayed the same, is that a bug with the current executable?)

    So long story short from Friday, the only thing we changed that got this working is the assignment part: new EntityReference(targetEntity.LogicalName, preMessageImage.Id);

    I am really very happy that we finally got this working but at the same time wondering why not in first shot, its like adding the looup field then deleting it did some trick.

    So for anybody apart from Daniel who doesn't want to go through the thread, execute function will look like as following (please note for PreImage, Postimage, targetentity and other object referenceses etc i have posted the functions in the thread above), so here is the execute function:

     

    Execute function
     
            public void Execute(IServiceProvider serviceProvider)
             {
                 IPluginExecutionContext context = CRMGlobal.GetPluginExecutionContext(serviceProvider);
                 IOrganizationServiceFactory factory = CRMGlobal.GetOrganizationServiceFactory(serviceProvider);
                 ITracingService tracer = CRMGlobal.GetTracingService(serviceProvider);
     
                Entity targetEntity = CRMGlobal.GetTargetEntity(context, typeof(AccountAddress_Update).Name, EntityNames.AccountAddress);
                 Entity preMessageImage = CRMGlobal.GetPreImageEntity(context, typeof(AccountAddress_Update).Name);
                 // this will get me the Account ID that is on the Account address record
                 string accountid = ((EntityReference)preMessageImage.Attributes["new_account"]).Id.ToString();
                
                try
                 {
                     IOrganizationService service = CRMGlobal.CreateOrganizationService(context, factory);
                     if (targetEntity.Attributes.Contains("new_primaryaddress"))
                     {
                         Boolean primaryaddress = (Boolean)targetEntity["new_primaryaddress"];
                         if (primaryaddress)
                         {
                             QueryExpression userQuery = new QueryExpression
                             {
                                 EntityName = targetEntity.LogicalName,
                                 ColumnSet = new ColumnSet("new_accountaddressid", "new_account", "new_primaryaddress"),
                                 Criteria =
                                 {
                                     Conditions =
                                         {
                                             new ConditionExpression("new_account",ConditionOperator.Equal,accountid)
                                         }
                                 }
                             };
     
                            DataCollection<Entity> AccountAddresses = service.RetrieveMultiple(userQuery).Entities;
                             foreach (Entity accountaddress in AccountAddresses)
                             {
                                 accountaddress.Attributes["new_primaryaddress"] = false;
                                 service.Update(accountaddress);
                             }
                             tracer.Trace("before the acount update");
     
                            Entity account = service.Retrieve("account", ((EntityReference)preMessageImage["new_account"]).Id, new ColumnSet("accountid", "primarycontactid"));
                             tracer.Trace("Account ID: " + ((EntityReference)preMessageImage["new_account"]).Id);
                             tracer.Trace("Account address ID: " + preMessageImage.Id);
                             if (account.Contains("new_primaryaddress"))
                             {
                                 tracer.Trace("within IF contains block");
                                 account.Attributes["new_primaryaddress"] = new EntityReference(targetEntity.LogicalName, preMessageImage.Id);
                             }
                             else
                             {
                                 tracer.Trace("within else contains block");
                                 account.Attributes.Add("new_primaryaddress", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
                             }
     
                            tracer.Trace("before the acount update");
                             service.Update(account);
                             tracer.Trace("after the acount update");
                         }
                    }
     
                }
                 catch (FaultException<OrganizationServiceFault> ex)
                 {
                     throw new InvalidPluginExecutionException(String.Concat("An error occurred in the AccountAddress_Update plug-in.", ex.Message, Environment.NewLine, ex.StackTrace));
                 }
                 catch (Exception ex)
                 {
                     throw new InvalidPluginExecutionException(String.Concat("An error occurred in the AccountAddress_Update plug-in.", ex.Message, Environment.NewLine, ex.StackTrace));
                 }
     
            }

     



    • Marked as answer by itsgonabeme Tuesday, August 2, 2011 6:56 PM
    • Edited by itsgonabeme Tuesday, August 2, 2011 7:02 PM
    Tuesday, August 2, 2011 6:55 PM

All replies

  • A few things:

    1. (EntityReference)preMessageImage["new_account"] won't work if it is an image. It would be (Entity)preMessageImage["new_account"]. preMessageImage["new_account"] might need to be preMessageImage["new_accountid"].
    2. preMessageImage.Id won't work if I understand correctly.
    3. You have a few references of "new_primaryaddress" field, which I believe should be "new_primaryaddressid", that's how a lookup field looks like in CRM.

    You should have shown us how you get preMessageImage.

    Hope this helps.


    Daniel Cai | http://danielcai.blogspot.com
    • Proposed as answer by vasubabuk Thursday, March 21, 2013 2:17 PM
    Friday, July 29, 2011 3:42 PM
  • Thanks for the tip Daniel, i will try that right away and get back to you.

    Just from learning perspective, We use EntityReference for Target Entity and Entity for PreImage/PostImage?

    Friday, July 29, 2011 3:48 PM
  • EntityReference is a special CRM class that represents a lookup field. It's different from Target entity or PreImage/PostImage Entity. You can NOT cast from EntityReference to an Entity or vice versa.


    Daniel Cai | http://danielcai.blogspot.com
    Friday, July 29, 2011 3:52 PM
  • Thanks for the clarification Daniel, if that's the case then EntityReference is correct in my scenario (what a shame for me, i am using EntityReference and don't know the difference :( )

    Okay so here is my updated code, i am still getting the same error and i am using EntityReference because new_account is my lookup field on AccountAddress form for Account table (also plugin is binded with Update trigger on AccountAddress Entity) + also i have confirmed the lookup field is new_primaryaddress, Here is the code:

     

                            Entity account = service.Retrieve("account", ((EntityReference)preMessageImage["new_account"]).Id, new ColumnSet(true));

                           // I have compared the new_account with what we have in Account table (through SQL Management studio) - It's the same
                           // So for sure we have the correct account + also have run the debugger too
                            tracer.Trace("Account ID: " + ((EntityReference)preMessageImage["new_account"]).Id);  

                           // same as above comments, did compare through SQL Management studio and its the same   
                            tracer.Trace("Account address ID: " + preMessageImage.Id);


                            if (account.Contains("primarycontactid"))
                            {
                                tracer.Trace("within IF contains block");
                                account.Attributes["new_primaryaddress"] = preMessageImage.Id;
                            }
                            else
                            {
                                tracer.Trace("within else contains block");
                                account.Attributes.Add("new_primaryaddress", preMessageImage.Id);
                            }

                            tracer.Trace("before the acount update");
                            service.Update(account);   
                            tracer.Trace("after the acount update");

     

    TRACE LOG

    before the acount update
    Account ID: 91c27ad1-eab9-e011-9a9b-00155d0adf01
    Account address ID: 41a5badc-eab9-e011-9a9b-00155d0adf01
    within else contains block
    before the acount update


    Friday, July 29, 2011 4:39 PM
  • You may not want to use new ColumnSet(true), as it will retrieve all columns, and consequently it will send all columns to CRM server when you try to update.

    Try to use new ColumnSet("accountid", "primarycontactid"), I think it's sufficient enough for your case.

    BTW, why has "new_primaryaddress" field become "primarycontactid" in your new code? Was that your intention?


    Daniel Cai | http://danielcai.blogspot.com
    Friday, July 29, 2011 4:47 PM
  • Sorry typo on my end, intention is still the same have to update the lookup field: new_primaryaddress

    For columnset i was going to do it at the end after i get it working and was part of my cleanup but its taking forever :(

    Any chance we can do a LiveMeeting or GoToMeeting, i can send you a G2M right away, please let me know if you could spare sometime for this?

    Thanks Daniel...

    Friday, July 29, 2011 4:53 PM
  • I think we are close enough to fix the problem. Try it out and let us know your progress.

    Sorry, I cannot do LiveMeeting or GotoMeeting on a private basis. Hope you understand.

    Cheers,


    Daniel Cai | http://danielcai.blogspot.com
    Friday, July 29, 2011 5:07 PM
  • No problem Daniel i understand what you are saying, i have tried and still the same error :(

    I will post the entire code, the first part is where we update the primaryaddress on the all the account address records, that part was working all fine.

    The second part is where we have to update the main account record, see maybe can find any missing link here..

    Code

                    IOrganizationService service = CRMGlobal.CreateOrganizationService(context, factory);
                    if (targetEntity.Attributes.Contains("new_primaryaddress"))
                    {
                        Boolean primaryaddress = (Boolean)targetEntity["new_primaryaddress"];
                        if (primaryaddress)
                        {
                            QueryExpression userQuery = new QueryExpression
                            {
                                EntityName = targetEntity.LogicalName,
                                ColumnSet = new ColumnSet("new_accountaddressid", "new_account", "new_primaryaddress"),
                                Criteria =
                                {
                                    Conditions =
                                        {
                                            new ConditionExpression("new_account",ConditionOperator.Equal,accountid)
                                        }
                                }
                            };

                            DataCollection<Entity> AccountAddresses = service.RetrieveMultiple(userQuery).Entities;
                            foreach (Entity accountaddress in AccountAddresses)
                            {
                                accountaddress.Attributes["new_primaryaddress"] = false;
                                service.Update(accountaddress);
                            }
                            tracer.Trace("before the acount update");

                            Entity account = service.Retrieve("account", ((EntityReference)preMessageImage["new_account"]).Id, new ColumnSet("accountid", "primarycontactid"));
                            tracer.Trace("Account ID: " + ((EntityReference)preMessageImage["new_account"]).Id);
                            tracer.Trace("Account address ID: " + preMessageImage.Id);
                            if (account.Contains("new_primaryaddress"))
                            {
                                tracer.Trace("within IF contains block");
                                account.Attributes["new_primaryaddress"] = preMessageImage.Id;
                            }
                            else
                            {
                                tracer.Trace("within else contains block");
                                account.Attributes.Add("new_primaryaddress", preMessageImage.Id);
                            }

                            tracer.Trace("before the acount update");
                            service.Update(account);
                            tracer.Trace("after the acount update");
                        }
                    }


    Friday, July 29, 2011 5:19 PM
  • I read some contradicts in your code. Did you literally copy/paste your code or you typed in?

    • Boolean primaryaddress = (Boolean)targetEntity["new_primaryaddress"];
      suggestes new_primaryaddress suggests that "new_primaryaddress" be a boolean field;
      but
      account.Attributes["new_primaryaddress"] = preMessageImage.Id;
      suggests it's another type of field.
    • new ConditionExpression("new_account",ConditionOperator.Equal,accountid)
      line doesn't make sense. it might need to be
      new ConditionExpression("new_accountid",ConditionOperator.Equal,accountid)

    You still haven't provided the code that you used to get preMessageImage.

     

     


    Daniel Cai | http://danielcai.blogspot.com
    Friday, July 29, 2011 5:33 PM
  • Yes code is correct, i understand your confusion here (the DEV who actually designed the table has modified the default naming convention when we create lookups and fields)

    • new_primaryaddress in TargetEntity (i.e. Account Address) is a boolean flag
    • new_primaryaddress in Account Entity is a lookup field (the DEV who created the lookup in Account table changed the default naming mechanism and put his own.

    The scenario is

    1. If the user sets the Primary flag = TRUE on any Account Address record thn we have to make sure flag on all the other Account Address records is FALSE
    2. Also we have to update that Account table (i.e. the account address with Primary = TRUE, has to be update on the lookup

     

    • new ConditionExpression("new_account",ConditionOperator.Equal,accountid) is correct because i want to loop through all the Address records for that account. Will set the flag to FALSE, since my Plugin is PRE so the record i am on will have YES at the end. So in a nutshell New_account is the lookup field and it has AccountId

     

    Here is my complete code with everything:

    I have kept the definition of PreImage/PostImage and Target in a separate class, so that i could reuse those functions in all the plugins, so first i will copy paste those functions then i will copy paste the Execute function, also for entity names i have created a separate class so i dont have to hardcode the same name all the time.

            public static Entity GetPreImageEntity(IPluginExecutionContext context,string PluginName)
            {
                if (context.PreEntityImages.Contains("PreImage") && context.PreEntityImages["PreImage"] is Entity)
                {
                    return ((Entity)context.PreEntityImages["PreImage"]);
                }
                else
                {
                    throw new Exception("No PreImage Entity found for Plugin: " + PluginName);
                }
            }

            public static Entity GetPostImageEntity(IPluginExecutionContext context, string PluginName)
            {
                if (context.PostEntityImages.Contains("PostImage") && context.PostEntityImages["PostImage"] is Entity)
                {
                    return ((Entity)context.PostEntityImages["PostImage"]);
                }
                else
                {
                    throw new Exception("No PostImage Entity found for Plugin: " + PluginName);
                }
            }

            public static Entity GetTargetEntity(IPluginExecutionContext context, string PluginName,string EntityName)
            {
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    Entity targetEntity = (Entity)context.InputParameters["Target"];
                    if (targetEntity.LogicalName == EntityName)
                    {
                        return targetEntity;
                    }
                    else
                    {
                        throw new Exception("Target Entity " + EntityName + "not found for plugin: " + PluginName);
                    }
                }
                else
                {
                    throw new Exception("Target Entity not found for plugin: " + PluginName);
                }
            }

    Execute function

            public void Execute(IServiceProvider serviceProvider)
            {
                IPluginExecutionContext context = CRMGlobal.GetPluginExecutionContext(serviceProvider);
                IOrganizationServiceFactory factory = CRMGlobal.GetOrganizationServiceFactory(serviceProvider);
                ITracingService tracer = CRMGlobal.GetTracingService(serviceProvider);

                Entity targetEntity = CRMGlobal.GetTargetEntity(context, typeof(AccountAddress_Update).Name, EntityNames.AccountAddress);
                Entity preMessageImage = CRMGlobal.GetPreImageEntity(context, typeof(AccountAddress_Update).Name);
                // this will get me the Account ID that is on the Account address record
                string accountid = ((EntityReference)preMessageImage.Attributes["new_account"]).Id.ToString();
               
                try
                {
                    IOrganizationService service = CRMGlobal.CreateOrganizationService(context, factory);
                    if (targetEntity.Attributes.Contains("new_primaryaddress"))
                    {
                        Boolean primaryaddress = (Boolean)targetEntity["new_primaryaddress"];
                        if (primaryaddress)
                        {
                            QueryExpression userQuery = new QueryExpression
                            {
                                EntityName = targetEntity.LogicalName,
                                ColumnSet = new ColumnSet("new_accountaddressid", "new_account", "new_primaryaddress"),
                                Criteria =
                                {
                                    Conditions =
                                        {
                                            new ConditionExpression("new_account",ConditionOperator.Equal,accountid)
                                        }
                                }
                            };

                            DataCollection<Entity> AccountAddresses = service.RetrieveMultiple(userQuery).Entities;
                            foreach (Entity accountaddress in AccountAddresses)
                            {
                                accountaddress.Attributes["new_primaryaddress"] = false;
                                service.Update(accountaddress);
                            }
                            tracer.Trace("before the acount update");

                            Entity account = service.Retrieve("account", ((EntityReference)preMessageImage["new_account"]).Id, new ColumnSet("accountid", "primarycontactid"));
                            tracer.Trace("Account ID: " + ((EntityReference)preMessageImage["new_account"]).Id);
                            tracer.Trace("Account address ID: " + preMessageImage.Id);
                            if (account.Contains("new_primaryaddress"))
                            {
                                tracer.Trace("within IF contains block");
                                account.Attributes["new_primaryaddress"] = preMessageImage.Id;
                            }
                            else
                            {
                                tracer.Trace("within else contains block");
                                account.Attributes.Add("new_primaryaddress", preMessageImage.Id);
                            }

                            tracer.Trace("before the acount update");
                            service.Update(account);
                            tracer.Trace("after the acount update");
                        }
                    }

                }
                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException(String.Concat("An error occurred in the AccountAddress_Update plug-in.", ex.Message, Environment.NewLine, ex.StackTrace));
                }
                catch (Exception ex)
                {
                    throw new InvalidPluginExecutionException(String.Concat("An error occurred in the AccountAddress_Update plug-in.", ex.Message, Environment.NewLine, ex.StackTrace));
                }

            }


    Friday, July 29, 2011 5:58 PM
  • Still waiting for your reply, would really appreciate if anybody can suggest anything?

    I am pretty sure i am not the first one who came across this.

    PLEASE HELP!!!!!!!!!!!!!!!1

    Sunday, July 31, 2011 6:19 AM
  • Sorry, I didn't intend to ignore your question, but just quite busy on Friday. 

    Can you try this? 

    if (account.Contains("new_primaryaddress"))
    {
     tracer.Trace("within IF contains block");
     account.Attributes["new_primaryaddress"] = new EntityReference(targetEntity.LogicalName, preMessageImage.Id);
    }
    else
    {
     tracer.Trace("within else contains block");
     account.Attributes.Add("new_primaryaddress", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
    }
    
    

    I believe new_primaryaddress should really new_primaryaddressid, but you know your customization better than I do.

    Also you may want to make sure that preMessageImage.Id has value.

    One more thing, I don't think that new ConditionExpression("new_account",ConditionOperator.Equal,accountid) would work, it should be new ConditionExpression("new_accountid",ConditionOperator.Equal,accountid). But again, you know your customization better than me.

    Are you working with CRM online? Even so, you should create a development environment that you can step into plugin code, which you can evaluate any expressions that you may want to.

    Hope this helps.

    Cheers,


    Daniel Cai | http://danielcai.blogspot.com



    • Proposed as answer by vasubabuk Thursday, March 21, 2013 2:16 PM
    Sunday, July 31, 2011 10:37 PM
  • Hey Thanks Daniel for getting back to me on this.

    I have tried the new EntityReference trick and it didn't work.

    I can assure you that, new_primaryaddress is the lookup field name in Account table. I have run the debugger and trace on this code tons of times now and have compared the ID's with what we have in the SQL table level (did it through SQL Management studio), its all good.

    I have 3 addresses for the Account and query expression is returning me 3 address, i have confirmed thr ID's with SQL. preMessageImage.Id has the data which is basically the ID of the address i am on, again i have confirmed this through trace and debugger.

    Also if you could see my code, can you please confirm that retrieve is not the problem here:

    Entity account = service.Retrieve("account", ((EntityReference)preMessageImage["new_account"]).Id, new ColumnSet("accountid", "primarycontactid"));

    In visual studio debugger the RESULTS VIEW for the account object is showing me only the AccountID which is correct, but no other fields. I hope that is not the reason for all this.

    I am really getting frustrated with this, fields are good, we have data in the fields, we are setting the values correctly then why it is blowing up on service.udpate :(


    • Edited by itsgonabeme Tuesday, August 2, 2011 1:09 AM
    Monday, August 1, 2011 11:59 PM
  • Can you inspect account.Attributes property bag to see what you have? 

    Also can you try to comment out the following two lines to see if you can update? 

    account.Attributes["new_primaryaddress"] = new EntityReference(targetEntity.LogicalName, preMessageImage.Id);
    account.Attributes.Add("new_primaryaddress", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
    
    

    Where comes a ser_account? Are you using different customization prefix? 


    Daniel Cai | http://danielcai.blogspot.com
    • Proposed as answer by vasubabuk Friday, March 22, 2013 5:29 AM
    Tuesday, August 2, 2011 12:59 AM
  • Hi Daniel,

    Thanks again, i really appreciate your help and support, this is an example for everybody.

    Update on your three questions:

    • Within account.Attributes, i get to see two fields, at index[0] i have accountid, at index[1]  have primarycontactid (i have a update plugin for Account table but that only works for primary contact lookup field. In our case we have to set the value on new_primaryaddress (lookup field))
    • After commenting out those two lines in both IF/ELSE blocks, i get no error
    • ser_account, ser is the prefex. Before posting the code here i will always do find and replace on ser_ with new_ but this time i missed out

    I think we are close!

     

    Tuesday, August 2, 2011 1:32 AM
  • Then I am relatively sure that the following code will fix the problem. 

    account.Attributes.Add("new_primaryaddressid", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
    

     

    Can you make sure that the Primary Address lookup field's name in account entity is new_primaryaddressid? 


    Daniel Cai | http://danielcai.blogspot.com

    Tuesday, August 2, 2011 2:20 AM
  • Thanks for the quick update Daniel.

    I have closed everything and heading out right away. I will try this tonight and will get back to you.

    Appreciate your help.

    Tuesday, August 2, 2011 2:26 AM
  • Hey Daniel,

    We got everything working now, FINALLY................. thank you so much for guiding me all the way through...

    And i am sure must be curious about the solution. So the solution is very strange and i doesn't make any logical sense but i will tell what has happened:

    • As you know i was using the lookup field for AccountAddress as new_primaryaddress
    • I created a new lookup fields called new_primaryaddressid and placed the field on the form, so basically now i have two lookups
    • In the code i changed my field name from new_primaryaddress to new_primaryaddressid (new field) and it worked (strange)
    • Then i uncommented out my code for the old address and it worked for both (strange again):

                            if (account.Contains("ser_primaryaddress"))
                            {
                                tracer.Trace("within IF contains block");
                                account.Attributes["ser_primaryaddressid"] = new EntityReference(targetEntity.LogicalName, preMessageImage.Id);
                                account.Attributes["ser_primaryaddress"] = new EntityReference(targetEntity.LogicalName, preMessageImage.Id);
                            }
                            else
                            {
                                tracer.Trace("within else contains block");
                                account.Attributes.Add("ser_primaryaddressid", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
                                account.Attributes.Add("ser_primaryaddress", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
                            }

     

    • Then i went ahead and deleted the new field new_primaryaddressid from table and form and deleted related line in the code and i worked (this is very very very strange, becasue the field name has stayed the same, is that a bug with the current executable?)

    So long story short from Friday, the only thing we changed that got this working is the assignment part: new EntityReference(targetEntity.LogicalName, preMessageImage.Id);

    I am really very happy that we finally got this working but at the same time wondering why not in first shot, its like adding the looup field then deleting it did some trick.

    So for anybody apart from Daniel who doesn't want to go through the thread, execute function will look like as following (please note for PreImage, Postimage, targetentity and other object referenceses etc i have posted the functions in the thread above), so here is the execute function:

     

    Execute function
     
            public void Execute(IServiceProvider serviceProvider)
             {
                 IPluginExecutionContext context = CRMGlobal.GetPluginExecutionContext(serviceProvider);
                 IOrganizationServiceFactory factory = CRMGlobal.GetOrganizationServiceFactory(serviceProvider);
                 ITracingService tracer = CRMGlobal.GetTracingService(serviceProvider);
     
                Entity targetEntity = CRMGlobal.GetTargetEntity(context, typeof(AccountAddress_Update).Name, EntityNames.AccountAddress);
                 Entity preMessageImage = CRMGlobal.GetPreImageEntity(context, typeof(AccountAddress_Update).Name);
                 // this will get me the Account ID that is on the Account address record
                 string accountid = ((EntityReference)preMessageImage.Attributes["new_account"]).Id.ToString();
                
                try
                 {
                     IOrganizationService service = CRMGlobal.CreateOrganizationService(context, factory);
                     if (targetEntity.Attributes.Contains("new_primaryaddress"))
                     {
                         Boolean primaryaddress = (Boolean)targetEntity["new_primaryaddress"];
                         if (primaryaddress)
                         {
                             QueryExpression userQuery = new QueryExpression
                             {
                                 EntityName = targetEntity.LogicalName,
                                 ColumnSet = new ColumnSet("new_accountaddressid", "new_account", "new_primaryaddress"),
                                 Criteria =
                                 {
                                     Conditions =
                                         {
                                             new ConditionExpression("new_account",ConditionOperator.Equal,accountid)
                                         }
                                 }
                             };
     
                            DataCollection<Entity> AccountAddresses = service.RetrieveMultiple(userQuery).Entities;
                             foreach (Entity accountaddress in AccountAddresses)
                             {
                                 accountaddress.Attributes["new_primaryaddress"] = false;
                                 service.Update(accountaddress);
                             }
                             tracer.Trace("before the acount update");
     
                            Entity account = service.Retrieve("account", ((EntityReference)preMessageImage["new_account"]).Id, new ColumnSet("accountid", "primarycontactid"));
                             tracer.Trace("Account ID: " + ((EntityReference)preMessageImage["new_account"]).Id);
                             tracer.Trace("Account address ID: " + preMessageImage.Id);
                             if (account.Contains("new_primaryaddress"))
                             {
                                 tracer.Trace("within IF contains block");
                                 account.Attributes["new_primaryaddress"] = new EntityReference(targetEntity.LogicalName, preMessageImage.Id);
                             }
                             else
                             {
                                 tracer.Trace("within else contains block");
                                 account.Attributes.Add("new_primaryaddress", new EntityReference(targetEntity.LogicalName, preMessageImage.Id));
                             }
     
                            tracer.Trace("before the acount update");
                             service.Update(account);
                             tracer.Trace("after the acount update");
                         }
                    }
     
                }
                 catch (FaultException<OrganizationServiceFault> ex)
                 {
                     throw new InvalidPluginExecutionException(String.Concat("An error occurred in the AccountAddress_Update plug-in.", ex.Message, Environment.NewLine, ex.StackTrace));
                 }
                 catch (Exception ex)
                 {
                     throw new InvalidPluginExecutionException(String.Concat("An error occurred in the AccountAddress_Update plug-in.", ex.Message, Environment.NewLine, ex.StackTrace));
                 }
     
            }

     



    • Marked as answer by itsgonabeme Tuesday, August 2, 2011 6:56 PM
    • Edited by itsgonabeme Tuesday, August 2, 2011 7:02 PM
    Tuesday, August 2, 2011 6:55 PM
  • I won't use a name of ser_primaryaddress for a lookup field. It would have caused much less confusion if you have used ser_primaryaddressid as the lookup field name in the first place.

    IMO, It's generally a better practice to have lookup field named with a "id" suffix, which is what CRM does by default when you create a 1:N relationship. 


    Daniel Cai | http://danielcai.blogspot.com

    • Proposed as answer by vasubabuk Thursday, March 21, 2013 2:16 PM
    Tuesday, August 2, 2011 7:01 PM
  • Yes Daniel you are right it has caused a lot of confusion.

    From here on will definately follow the naming convention correctly and will make sure i convey the message accross the board.

    For above i will delete old field replace it with id in the form and code.

    Again thank you Daniel for helping me get through with this. Appreciated.

    PS: I really like you latest blog on sdk 5.0.5 


    • Edited by itsgonabeme Tuesday, August 2, 2011 7:09 PM
    Tuesday, August 2, 2011 7:06 PM
  • No problem. Glad that you have got everything working now. 

    Cheers,


    Daniel Cai | http://danielcai.blogspot.com
    Tuesday, August 2, 2011 7:08 PM
  • One last point, you shouldn't need the if (account.Contains("new_primaryaddress")) branch, as you are not retrieving the this field, neither are you checking its value. From performance perspective, you should do so. 

    Otherwise, you have got pretty solid code case. 


    Daniel Cai | http://danielcai.blogspot.com
    Tuesday, August 2, 2011 7:24 PM
  • Abs. spot on Daniel, no point of that if/else block.

    I can just do Attributes.add.

    Thank you so much.

    Tuesday, August 2, 2011 7:56 PM
  • Hey Daniel,

    Help me understand this concept.

    What is the difference between Attributes.Add V/s the normal assignmnet Attributes["fieldname"]?

     

    Thanks

    Tuesday, August 2, 2011 9:27 PM
  • .Attributes is a property bag, which contains all the properties that have been returned by CRM when a fetch or retrieve service call is made to CRM server. .Attributes["fieldname"] will fail if the field doesn't have value. But .Attributes.Add will not. 
    Daniel Cai | http://danielcai.blogspot.com
    Tuesday, August 2, 2011 9:41 PM
  • Perfect Daniel, so the best bet is to use Attributes.Add always.

    Thanks

     

    Tuesday, August 2, 2011 9:46 PM
  • Very much so, since .Attributes.Add won't fail even the property bag has already got the value for that particular field. 
    Daniel Cai | http://danielcai.blogspot.com
    Tuesday, August 2, 2011 9:47 PM
  • Hey Daniel,

    Everything is all good and so far the Account Address flow is working perfect but there is one glitch.

    I am not sure if there is a way around for this or not.

    • So we were calling Account address from Account form
    • On Account Address we were making one of the address as primary
    • And then we were updating the lookup on Account Form

    Now the glitch is system does the update but i have to close that account record and have to re-open it.

    Any way i can refresh it through plugin code or any work around you can think of?

     

    Thanks, will look forward to hear from you.

    Tuesday, August 9, 2011 1:52 AM
  • You cannot refresh client window in plugin code, as it runs at the server side.

    But you can possibly use JS code to refresh the parent window. I haven't tried, but the following code might work.

    window.top.opener.top.location.href = window.top.opener.top.location.href;
    

    Hope this helps.


    Daniel Cai | http://danielcai.blogspot.com
    Tuesday, August 9, 2011 3:23 PM
  • Hey Daniel thanks for the quick reply.

    Please help me understand this,

    • I will create a webresource JScript
    • Create a function with the above line of code
    • But then where shall i bind this function (i am calling that account address from the left navigation)

    Do i have to export the solution and have to do it in customizations.xml? or anyway around.

    ------------

    Daniel, one more question. Can i use dialogs in conjunction with plugins.

    So on my Account Address when the user click the Primary Address = yes, through Jscript we will verify if there is already a primary address and if there is one thn we will prompt them with this dialog "Are you sure you want to...." but that question is my plugin is already binded with the entity how do i intervene that process if the user says NO.

     

    Thanks in advance..

     

    Tuesday, August 9, 2011 11:23 PM
  • The js code should be called in acccount address entity form onload event (pls note the change that i just made). For your other question, crm dialog is designed for different business scenario, what you have described can be achieved using js code.
    Daniel Cai | http://danielcai.blogspot.com
    Wednesday, August 10, 2011 5:32 AM
  • Hey Daniel, that Jscript snippet is not letting me loading "Account Address" form. Every time i click "Account Address" from the Navigation the system will take be back/reload the page.
    Thursday, August 11, 2011 1:56 AM
  • Hmm, in this case, there isn't a perfect solution for your requirements. You will probably have to educate your CRM users to refresh the Account form when they have made changes in Account Address form. 

    Another possible implementation is, when the account form receives focus, you can check if there is any changes made in its associated account address records. If yes, you may use the same script to refresh the account form. One thing that I should have mentioned before presenting you the script is, the script will refresh the form, if the form has any unsaved changes, it will not work unless the user agrees to lose all changes. 

    I would probably keep it simple, and let the users do what they need to do. 


    Daniel Cai | http://danielcai.blogspot.com

    Thursday, August 11, 2011 2:04 AM
  • Thanks for the quick reply Daniel, i will do some R&D on this to see if there is any other workaround.

     

    Thursday, August 11, 2011 2:19 AM
  • Daniel, on that JScript dialog, my question was "Can my dialog Yes/No intervene with Plugin code".

    So basically If the users say i don't want to execute the plugin code, is that scenario possible?

    Thursday, August 11, 2011 2:20 AM
  • To intervene plugin execution (I shouldn't say intervene, it's rather just pre-set a condition), we usually use an additional field and set a special value before submitting the change to the CRM server. Within Plugin, we check the field value, and execute different business rules accordingly. This kind of special value can be easily set using JS code in the form if you have identified the business logic. 

    Dialog in your case is a little overkill. It's designed for complex business process. In fact, you can try CRM dialog, but you will very likely need to do the same thing that I just mentioned, which can be easily accomplished using a couple line of JS code. 


    Daniel Cai | http://danielcai.blogspot.com


    Thursday, August 11, 2011 4:44 AM
  • Excellent, i am exactly following you, so just to re-confirm what you are suggesting

    • I create a dummy field on table level for data manipulation
    • With the JScript dialog or whatever business logic set the value (lets say onChange trigger for that field on any of the field lets say "Primary Address")
    • Read that field value in plugins code and execute business logic accordingly

    Great idea, just want to re-confirm we are on same page here.

    Thanks Daniel for your support!

    Thursday, August 11, 2011 8:58 AM
  • That is very correct. You are on the right track. 

    Cheers,


    Daniel Cai | http://danielcai.blogspot.com
    Thursday, August 11, 2011 1:27 PM