C# - Create unique one-of-a-kind numbers, like GUID

Markus Freitag 3,786 Reputation points
2020-12-18T08:51:33.06+00:00

Hello,

<message>
   <header>
      <to>companyReceiver</to>
      <from>companySender</from>
      <type>saveInvoice</type>
   </header>
   <body>
     <saveInvoice>
      **<invoice eventid="100000222"** date="12-18-2000" number="123">
         <address country="German">
         <name>Jürgen Bauer</name>

I need to create unique one-of-a-kind numbers.
How to do it correctly? Not that two threads may increase twice. This must not be done.

Alternative would be maybe an ID like a GUID from Windows.
How is this ensured?

<invoice eventid="100000222"

or similar liks this.

    <ID value="de1bc7af-ggg3-4c6c-a287-c11592732679" />
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,006 questions
0 comments No comments
{count} votes

Accepted answer
  1. Karen Payne MVP 35,436 Reputation points
    2020-12-19T11:54:12.033+00:00

    Hello @Markus Freitag

    You can use a thread-safe singleton e.g.

    Note that this works with numbers and alpha numerics. All you need to know is the last event id which can be remembered in say a variable.
    49628-a1.png

    Base class

    using System;  
    using System.Text.RegularExpressions;  
      
    namespace UtilityLibrary  
    {  
        public class StringHelpers  
        {  
      
            public static string IncrementAlphaNumericValue(string value)  
            {  
                if (string.IsNullOrWhiteSpace(value))  
                {  
                    return "";  
                }  
      
                if (Regex.IsMatch(value, "^[a-zA-Z0-9]+$") == false)  
                {  
                    throw new Exception("Invalid Character: Must be a-Z or 0-9");  
                }  
      
                var characterArray = value.ToCharArray();  
      
                for (var characterIndex = characterArray.Length - 1; characterIndex >= 0; characterIndex--)  
                {  
                    var characterValue = Convert.ToInt32(characterArray[characterIndex]);  
      
                    if (characterValue != 57 && characterValue != 90 && characterValue != 122)  
                    {  
                        characterArray[characterIndex]++;  
      
                        for (int resetIndex = characterIndex + 1; resetIndex < characterArray.Length; resetIndex++)  
                        {  
                            characterValue = Convert.ToInt32(characterArray[resetIndex]);  
                            if (characterValue >= 65 && characterValue <= 90)  
                            {  
                                characterArray[resetIndex] = 'A';  
                            }  
                            else if (characterValue >= 97 && characterValue <= 122)  
                            {  
                                characterArray[resetIndex] = 'a';  
                            }  
                            else if (characterValue >= 48 && characterValue <= 57)  
                            {  
                                characterArray[resetIndex] = '0';  
                            }  
                        }  
      
                        return new string(characterArray);  
      
                    }  
                }  
      
                return null;  
            }  
        }  
    }  
    

    Singleton

    using System;  
    namespace UtilityLibrary  
    {  
        public sealed class EventIncrementer  
        {  
            private static readonly Lazy<EventIncrementer> Lazy =  
                new Lazy<EventIncrementer>(() =>  
                    new EventIncrementer());  
      
            public static EventIncrementer Instance => Lazy.Value;  
      
            public string EventSequence(string value)  
            {  
                return StringHelpers.IncrementAlphaNumericValue(value);  
            }  
        }  
    }  
    

    Usage example

    class Program  
    {  
        static void Main(string[] args)  
        {  
            var currentEventIdentifier = "000000000";  
      
            for (int index = 0; index < 5; index++)  
            {  
                currentEventIdentifier = EventIncrementer.Instance.EventSequence(currentEventIdentifier);  
                Console.WriteLine(currentEventIdentifier);  
            }  
      
            Console.WriteLine();  
            currentEventIdentifier = "AS0000000";  
            for (int index = 0; index < 5; index++)  
            {  
                currentEventIdentifier = EventIncrementer.Instance.EventSequence(currentEventIdentifier);  
                Console.WriteLine(currentEventIdentifier);  
            }  
      
      
            Console.ReadLine();  
        }  
    }  
    
    1 person found this answer helpful.

2 additional answers

Sort by: Most helpful
  1. Karen Payne MVP 35,436 Reputation points
    2020-12-18T11:09:58.17+00:00

    Hello @Markus Freitag

    One option

    public class StringHelpers  
    {  
        public static string InvoiceGuid() => Guid.NewGuid().ToString();  
    }  
    

    Usage

    for (int index = 0; index < 10; index++)  
    {  
        Debug.WriteLine(StringHelpers.InvoiceGuid());  
    }  
    

    Results

    2557ec2a-6634-4a4c-b4b7-fdf720dfa1c1  
    e5c3f891-3dc5-44c8-9f75-7316e28c9e77  
    15d634e9-376b-4b53-9f9e-61bf90f985f8  
    e72841f5-4a8a-4b2c-b9cb-95a8d48b198f  
    a71e284c-c1f6-40c3-91a9-80da8dbf8955  
    0c2f011b-3b33-4cb5-8d48-3099e916bfc0  
    a0ed9e40-bc43-48fb-a920-3ff14391489d  
    4241683d-c6e8-4b46-a9b2-3c04fb8612c7  
    aa3ec5a7-8989-40af-a446-5e553889cf5f  
    a90d15b5-6e95-4723-88fd-9f9a3dd58983  
    95e3cc50-8433-4b9a-b5a7-191177095c69  
    38f936c3-386e-4494-b3e9-41d10e3a4406  
    f72f26a7-fd04-4058-8ae9-f60067757a7d  
    3bc4fca9-2ddb-4aad-a864-015390269217  
    a376a002-23de-4c47-9307-a6c1de66d2cc  
    4043ace6-752c-478a-8c5f-bb9228f4d2b7  
    00412a87-9f7a-4880-ab07-c48888b1cdf5  
    3e0ec65f-f5e2-4ec0-9b2e-405f01c123c3  
    6943c57a-a0ed-4417-a311-03a1f929a1ac  
    552ec7f8-24d1-4a57-9348-b612355a30b5  
      
    
    2 people found this answer helpful.

  2. YASER SHADMEHR 781 Reputation points
    2020-12-19T04:26:43.303+00:00

    @Markus Freitag I strongly suggest don't use counter up if uniqueness is important for you. It will cause a lot of headaches down the road including multi-threading, multi-processing, application failure, ... For example, think about if your app crash or get closed, then you need to save/load it which will be hard on multi-process.

    If you have a DB, you have two options:

    1. AUTO-INCREMENT Field: Define a unique identifier field in DB and set the field to AUTO INCREMENT. See example for SQL here.
    2. Guid

    Otherwise, you have one option which is Guid. Here are my thoughts:

    1. Use Guid in multi-threading or multi-processing: If you need to limit duplication to an absolute minimum, an incremental counter works too. But, If your app is using multiple threads or processes, it will so hard to implement correctly.
    2. Use Guid for external Id: An external ID is an ID that has meaning for an external resource such as a customer or other services. By using Guid, you don't need to worry about any future business requirements such as Import/export and ...

      Best practice while using Guid as a Unique Identifier:

    3. Use System.Guid type instead of a string: If you are going to use Guid, keep the ID type as Guid Not String! Not only converting Guid to the string will cause performance issue, but you may run the risk of comparing incompatible format. Remember, there is not a single standard format for representing a Guid as a string, and also you need to take care of case sensitivity and default culture. These days even DB support Guid type, so keep it as System.Guid.
    4. Use Guid.Empty for an empty GUID: Guid.Empty is clearer. (RSPEC-4581)
    5. Make difference between Guid.New() vs new Guid(): Use Guid.New() for creating a random guide and use Guid's constructors(e.g. new Guid(byte[]) for creating a Guid for specific initialization.
    6. Don't use Guid as clustering key in SQL server: See why here
    7. Specify format for Guid.ToString("D"): Here is different format supported in .Net: Guid.ToString("N"): cd26ccf675d64521884f1693c62ed303
      Guid.ToString("D"): cd26ccf6-75d6-4521-884f-1693c62ed303
      Guid.ToString("B"): {cd26ccf6-75d6-4521-884f-1693c62ed303}
      Guid.ToString("P"): (cd26ccf6-75d6-4521-884f-1693c62ed303)
      Guid.ToString("X"): {0xcd26ccf6,0x75d6,0x4521,{0x88,0x4f,0x16,0x93,0xc6,0x2e,0xd3,0x03}}

    So in your case, I will define invoice class like below:

    [XmlType("invoice")]  
    public class Invoice  
    {  
     public Invoice()  
     {  
     this.EventId = Guid.NewGuid();  
     }  
    
     public Invoice(Guid eventId, System.Int64 number)  
     {  
     this.EventId = eventId;  
     this.Number = number;  
     }  
    
     [XmlAttribute("eventid")]  
     public System.Guid EventId { get; set; }  
    
     [XmlAttribute("number")]  
     public System.Int64 Number { get; set; }  
    
     [XmlElement("name")]  
     public string Name { get; set; }  
    
     // Other Code //  
    }  
    

    and here's XML serializer:

    public static class XMLHelper  
    {  
     public static string ToXML<T>(this T obj)  
     {  
     using (var stringwriter = new System.IO.StringWriter())  
     {  
     var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));  
     serializer.Serialize(stringwriter, obj);  
     return stringwriter.ToString();  
     }  
     }  
    }  
    

    finally, here is the example:

    var invoice = new Invoice(Guid.NewGuid(), 123) { Name = "Jürgen Bauer"};  
    var xml = invoice.ToXML();  
    

    I create a full example in dotnetfiddle too.

    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.