2

Time to leave the shy mode behind and make my first post on stackoverflow. After doing loads of research (plugins, performance, indexes, types of update, friends) and after trying several approaches I was unable to find a proper answer/solution.

So if possible I would like to get your feedback/help in a Microsoft Dynamics CRM 2013/2015 plugin performance issue (or coding technique)

Scenario:

Microsoft Dynamics CRM 2013/2015
2 Entities with Relationship 1:N
EntityA
EntityB

EntityB has the following columns:
Id | EntityAId | ColumnDemoX (decimal) | ColumnDemoY (currency)

Entity A has: 500 records
Entity B has: 150 records per each Entity A record. So 500*150 = 75000 records.

Objective:

Create a Post Entity A Plugin Update to "mimic" the following SQL command

Update EntityB
Set ColumnDemoX = (some quantity), ColumnDemoY = (some quantity) * (some value)
Where EntityAId = (some id)

One approach could be:

using (var serviceContext = new XrmServiceContext(service)) 
{
  var query = from a in serviceContext.EntityASet
              where a.EntityAId.Equals(someId)
              select a;

  foreach (EntityA entA in query)
  {
    entA.ColumnDemoX = (some quantity);
    serviceContext.UpdateObject(entA);
  }

  serviceContext.SaveChanges();
}

Problem:

The foreach for 150 records in the post plugin update will take 20 secs or more.
While the
Update EntityB Set ColumnDemoX = (some quantity), ColumnDemoY = (some quantity) * (some value) Where EntityAId = (some id)
it will take 0.00001 secs

Any suggestion/solution?


Thank you all for reading.
H

Hugo L.
  • 21
  • 4

3 Answers3

1

You can use the ExecuteMultipleRequest, when you iterate the 150 entities, save the entities you need to update and after that call the request. If you do this, you only call the service once, that's very good for the perfomance.

If your process could be bigger and bigger, then you should think making it asynchronous as a plug-in or a custom activity workflow.

This is an example:

// Create an ExecuteMultipleRequest object.
requestWithResults = new ExecuteMultipleRequest()
{
    // Assign settings that define execution behavior: continue on error, return responses. 
    Settings = new ExecuteMultipleSettings()
    {
        ContinueOnError = false,
        ReturnResponses = true
    },
    // Create an empty organization request collection.
    Requests = new OrganizationRequestCollection()
};

// Add a UpdateRequest for each entity to the request collection.
foreach (var entity in input.Entities)
{
    UpdateRequest updateRequest = new UpdateRequest { Target = entity };
    requestWithResults.Requests.Add(updateRequest);
}

// Execute all the requests in the request collection using a single web method call.
ExecuteMultipleResponse responseWithResults =
    (ExecuteMultipleResponse)_serviceProxy.Execute(requestWithResults);
Sxntk
  • 845
  • 14
  • 29
  • 1
    could you please check this thread [link](http://stackoverflow.com/questions/28414718/is-it-beneficial-to-call-executemultiplerequest-from-within-a-plugin) Short summary: So from these results, there is no need to execute an ExecuteMultipleRequest from within a plugin. You're already on the server, having to execute more code to perform the same operation. Thank you for reply Sxntk – Hugo L. Jun 04 '15 at 14:38
  • Note: Apologies for the several edits on the previous comment but I'm just reading the stackoverflow Markdown help at the same time :) – Hugo L. Jun 04 '15 at 14:44
  • That's pretty interesting, thanks for sharing, my thoughs are that you should try yourself, Create and Update may change its behavior, try it and see the results yourself. – Sxntk Jun 04 '15 at 14:49
  • 2
    After running some tests I can confirm that the theory in this [link](http://stackoverflow.com/questions/28414718/is-it-beneficial-to-call-executemultiplerequest-from-within-a-plugin) is correct. Using the executemultiplerequest inside a plugin isn't beneficial and in fact adds more time. – Hugo L. Jun 05 '15 at 09:51
  • Is it possible to make that process asynchronous? – Sxntk Jun 05 '15 at 13:23
  • well it wasn't the desired solution but I ended up splitting the plugin code. Now I have a synch plugin and another one to run the asynch part. Thank you for your help @Sxntk – Hugo L. Jun 17 '15 at 11:50
0

Few solutions comes to mind but I don't think they will please you...

  1. Is this really a problem ? Yes it's slow and database update can be so much faster. However if you can have it as a background process (asynchronous), you'll have your numbers anyway. Is it really a "I need this numbers in the next second as soon as I click or business will go down" situation ?

  2. It can be a reason to ditch 2013. In CRM 2015 you can use a calculated field. If you need this numbers only to show up in forms (eg. you don't use them in reporting), you could also do it in javascript.

  3. Warning this is for the desesperate call. If you really need your update to be synchronous, immediate, you can't use calculated fields, you really know what your doing etc... Why not do it directly in the database? I know this is a very bad advice. There are a lot of reason not to do it this way (you can read a few here). It's unsupported and if you do something wrong it could go really bad. But if your real situation is as simple as your example (just a calculated field, no entity creation, no relation modification), you could do it this way. You'll have to consider many things: you won't have any audit on the fields, no security, caching issues, no modified by, etc. Actually I pretty much advise against this solution.

Nicolas C
  • 752
  • 5
  • 18
  • 1. Yes, from a user perspective it's a problem. Simple requirement: change something on a form, press "confirm/save/execute", that should update the related records so that the user can now update a grid which contains the related information. I have now 2 plugins (one synch and another asynch) but it's the best of the non ideal solution if you know what I mean.

    2. Calculated fields in CRM. You can’t use values in calculated attributes that reference a related entity so I guess it wouldn't work. Remember I'm trying to update EntityB when Field1 on EntityA is updated.
    – Hugo L. Jun 17 '15 at 12:02
  • 3. Yes I know what I'm doing, I know what I loose by bypassing the CRM API but I tend to find a supported workaround, change the business requirement logic or explain the customer the software limitations.
    Nicolas thank you very much for your help. Really appreciated.
    – Hugo L. Jun 17 '15 at 12:05
0

1 - Put it this logic to async workflow. OR 2 - Don't use
serviceContext.UpdateObject(entA);

serviceContext.SaveChanges();.

Get all the records (150) from post stage update the fields and ExecuteMultipleRequest to update crm records in one time. Don't send update request for each and every record

user1662380
  • 87
  • 1
  • 8
  • That was the decision to split the code, but it doesn't really implement the required feature.
    I tried the batch update and the ExecuteMultipleRequest but the problem persists. Read my comments to Sxntk and check the link to another Stackoverflow thread where they detail all the tests including the one that you suggest. Thanks for your help
    – Hugo L. Jun 17 '15 at 12:06