While working on one of the requirement where we needed to generate unique ID for the Custom Entity record. We thought of using plugin code to implement Auto number generate unique ID. However when multiple records were getting created from integrations and various sources, many duplicated IDs were getting generated.
After invetsigating further and multiple search on internet, we finally decided to use lock mechanism which is provided by .net framewrok.
C# Code to Generate ID using lock
Class Helper { private static object lockObj = new object(); public static string GenerateId() { lock (Helper.lockObj) { Thread.Sleep(100); string empty = string.Empty; DateTime now = DateTime.Now; string str1 = now.Year.ToString((IFormatProvider)CultureInfo.InvariantCulture); now = DateTime.Now; int month = now.Month; string str2 = month < 10 ? "0" + month.ToString() : month.ToString(); now = DateTime.Now; int day = now.Day; string str3 = day < 10 ? "0" + day.ToString() : day.ToString(); now = DateTime.Now; int hour = now.Hour; string str4 = hour < 10 ? "0" + hour.ToString() : hour.ToString(); now = DateTime.Now; int minute = now.Minute; string str5 = minute < 10 ? "0" + minute.ToString() : minute.ToString(); now = DateTime.Now; int second = now.Second; string str6 = second < 10 ? "0" + second.ToString() : second.ToString(); now = DateTime.Now; int millisecond = now.Millisecond; string str7 = millisecond < 10 ? "0" + millisecond.ToString().Substring(0, 1) : millisecond.ToString().Substring(0, 1); int num = new Random().Next(100, 999); return empty + str1 + str2 + str3 + str4 + str5 + str6 + str7 + num.ToString(); } } }
Lock
C# Lock keyword ensures that one thread is executing a piece of code at one time. The lock keyword ensures that one thread does not enter a critical section of code while another thread is in that critical section.
Lock is a keyword shortcut for acquiring a lock for the piece of code for only one thread.
Hope this helps!
Hi Bipin,
I am faced by a similar issues, but from what I can tell this implementation would not work as plugins run in their own sandbox, likely on different servers as well. Any idea how this could be solved?
LikeLike
Hi Johan,
It can be also using using update lock concept.
Please see below article with step by step details.
https://softchief.com/2021/08/17/auto-number-in-dynamics-365-plugin-or-dataverse-plugin/
Thanks,
Bipin
LikeLike
Hi, this is simply not enough… this might work in an onprem crm where your plugin is beeing executed by one sandbox service on one machine where the appdomain of the plugin execution is simply reused by all executions. This will definitely not work in load-balanced crm backend server clusters, you dont have the certainty that your sandbox plugin execution is happening within same app domain, neither on same machine. This is simply not enough. You need some not even inter-process but iter-machine locking mechanism, thus most probably calling some hand crafted external service to hold the locks and block uppon this locking remote call.
LikeLike
Oh my god, first-hand example of how to NOT do locking in D365 plugins. The C# “lock” keyword is used for locking resources betwen threads running within the same process. – The issue is that in D365 Online this is definitely not the case, and On-Prem you cannot guarantee it (and you do NOT want to limit to 1 worker process).
Instead, you use the built-in optimistic lock mechanism with some error handling logic in case of version mismatches. – This could be implemented using Polly for .NET.
In this specific case though, a row-lock is more appropriate:
1) As the very first thing in the plugin do a bogus update on the row containing the autonumber counter. – This creates a Row Lock on database-level, so in essence a lock across all worker processes.
2) Read the autonumber counter from that row again (now that we have a lock).
3) Use the number to insert on whatever target entity.
4) Update the row with the next autonumber.
5) Plugin finishes and releases the row lock so that the next plugin in line may read from it.
Make sure to use “context.MergeOption = Microsoft.Xrm.Sdk.Client.MergeOption.NoTracking” in order to ensure that any values are not read from some cache (just in case).
There are possibilities of deadlocks using this method, so in case that happens, Polly for .NET as a resilience framework for defining retry policies is worth gold 🙂
LikeLike
Agree with you.
simple update lock would be sufficient for this requirement.
https://softchief.com/2021/08/17/auto-number-in-dynamics-365-plugin-or-dataverse-plugin/
Thanks,
Bipin
LikeLike