Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Tuesday, June 2, 2015

Auto Number: Record Locking in MSCRM Plugin

    We came across the scenario where custom Auto Numbering plugin generates the duplicate numbers for an entity when two users create the records simultaneously. The reason behind this is because dynamics only locks a record inside a transaction if we update it. However what we are doing here is retrieving the record and then updating it. So effectively the record will be locked just at the end of the plugin execution which is not very helpful indeed.
Below are the steps to illustrate this:

1.   First we are retrieving the config record which contains the prefix, last generated number and other details

2.   Then we are reading the last updated number value and other config details

3.   Then we are applying the logic to build the number

4.   Finally, we are updating both the config(to maintain the last generated number) and the entity record in context with the required values.

So it is possible that two plugins is being executed and both those plugins performed step 1 above (so they both retrieved a config record). So they both will have the same value for the “Last Number” field. Then they will both apply the logic in step 2 and 3. Finally the first plugin will execute step 4 and update the config record (this will lock the config record)  but it will finish execution immediately which will unlock the config record. So now the second plugin is executing step 4 but since it already has retrieved the old value so the update logic will be the same and it will override the config record with the same number instead of incrementing it.

So, we should update the logic in the plugin above to the following:

1.   First, do a fake update to the required config record. We don’t have to change  any values and we can just do       service.update(configRecord);

2.   Then we apply step 1, 2, 3 and 4 from the above logic.

Below is the sample code to show how to lock  the configuration record:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Get the record for which Auto Number to be generated
Entity  targetEntityRecord = (Entity)context.InputParameters["Target"];

// Fetch the configuration record
Entity configRecord = GetConfigurationRecord(service);

// If configuration record found, then do a fake update
if (configRecord.Id != Guid.Empty)
  service.Update(configRecord);
else
  throw new  InvalidPluginExecutionException("Config record not found.");

// Get the last generated number from configuration record
int  lastNumber =  Int32.Parse(configRecord.Attributes["new_lastnumber"].ToString());

// Increment the last generated number
lastNumber++;

// Build a number with your own logic
string autoNumber = string.Format("{0}-{1}", "PREFIX" , lastNumber);

// Set new generated number to both target entity and Configuration record
targetEntityRecord.Attributes.Add("new_name", autoNumber);
configRecord.Attributes["new_lastnumber"] = lastNumber;

// Update the Configuration record
service.Update(configRecord);


I would like to thank my friend Shakarchi Ethra for describing this Dynamics behaviour.

Thursday, August 23, 2012

Read CSV file in C#

Below is the C# code to read the data from CSV file to DataTable.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
using Microsoft.VisualBasic.FileIO;


public DataTable ReadCSVData(StreamReader reader)
{
    DataTable dtReadFileData =  null;
    try
    {
         TextFieldParser parser = new TextFieldParser(reader);
         dtReadFileData = new DataTable();
         parser.TextFieldType = FieldType.Delimited;
         parser.SetDelimiters(",");
         String[] stringRow = parser.ReadFields();

         foreach(String field in stringRow)
         {
             dtReadFileData.Columns.Add(field, Type.GetType("System.String"));
         }

         while(!parser.EndOfData)
         {
             stringRow = parser.ReadFields();
             dtReadFileData.Rows.Add(stringRow);
         }

         return dtReadFileData;
    }

    catch(Exception ex)
    {
         //Handle the exception
    }
}