58

I have to convert Convert Int32 into Guids and this is what I came up with.

public static class IntExtensions
{
    public static Guid ToGuid(this Int32 value)
    {
        if (value >= 0) // if value is positive
            return new Guid(string.Format("00000000-0000-0000-0000-00{0:0000000000}", value));
        else if (value > Int32.MinValue) // if value is negative
            return new Guid(string.Format("00000000-0000-0000-0000-01{0:0000000000}", Math.Abs(value)));
        else //if (value == Int32.MinValue)
            return new Guid("00000000-0000-0000-0000-012147483648");  // Because Abs(-12147483648) generates a stack overflow due to being > 12147483647 (Int32.Max)
    }
}

But it’s somehow ugly. Has anybody a better idea?

Update:

Yes I know the whole thing is ugly but I am a loss of Ideas. The problem is. I am getting data and have to store it into a Table I cannot change. The sending data primary key is a Int and the table primary key I have to store it is a Guid. The problem is I have to understand what object the sender is talking about but can only store it as a Guid.

Update 2:

Okay I see I have to provide more info here. I am a Webservice receiving data and have to pass along data to an Interface I also can not control. So I neither can model the data received nor the (Interface)database where I have to send the data. Additionally, I have somehow have to map these two things in a way so I somehow can update an item. sigh

7
  • 2
    One "INT" is 4 byte - a GUID is 16 byte - how is this even remotely going to work!?!?!
    – marc_s
    Commented Jan 28, 2011 at 7:37
  • 3
    This is just going to be ugly. That's what happens when you try to convert a tricycle into a motorcycle. Commented Jan 28, 2011 at 7:42
  • 1
    You'd need to tell us what your requirements are. Do you have to be able to reverse this process? Do these IDs need to be globally or locally unique? Is this a short term solution, or are these IDs used in your database forever? =)
    – Jens
    Commented Jan 28, 2011 at 7:43
  • 1
    You are not creating guids, they are not valid.
    – MW_dev
    Commented Jan 28, 2011 at 7:47
  • 2
    Wouldn't creating a mapping table containing the two keys be more reliable?
    – Filburt
    Commented Jan 28, 2011 at 8:14

5 Answers 5

93

Here is a simple way to do it:

public static Guid ToGuid(int value)
{
    byte[] bytes = new byte[16];
    BitConverter.GetBytes(value).CopyTo(bytes, 0);
    return new Guid(bytes);
}

You can change where the copy will happen (vary the index from 0 to 12). It really depends on how you want to define this unusual "int to Guid" conversion.

1
  • 3
    Thank you very much Simon I was looking for something cleaner like this even though I'll have to ponder about options to my problem. I lack so much controll that I might realy have to to this. Thank you again.
    – psibot
    Commented Jan 28, 2011 at 8:32
58

Had the same issues, needed a Int to Guid then back to Int. Old data that used int ID's but the processing function required a Guid. It was alot less code to write extra functions plus DB changes. Just easier to pass the Int iD in Guid form, knowing that it was not going to use this as its final saving Guid. The save is an Insert, so it was getting a new Guid at the end.

Heres's the code from above and an idea from another post about Guids to ints and getting the Int back out.

public static Guid Int2Guid(int value)
{
    byte[] bytes = new byte[16];
    BitConverter.GetBytes(value).CopyTo(bytes, 0);
    return new Guid(bytes);
}

public static int Guid2Int(Guid value)
{
    byte[] b = value.ToByteArray();
    int bint = BitConverter.ToInt32(b, 0);
    return bint;
}

This Guid2Int should only be passed a Guid that came from an Int.

1
  • string version: public static Guid String2Guid(string value) { byte[] bytes = new byte[16]; Encoding.ASCII.GetBytes(value).CopyTo(bytes, 0); return new Guid(bytes); } public static string Guid2String(Guid value) { byte[] b = value.ToByteArray(); ASCIIEncoding encoder = new ASCIIEncoding(); string s = encoder.GetString(b).Trim('\0'); return s; }
    – user326608
    Commented Mar 13, 2015 at 10:58
7

You can take the digits from an int and format them so that they look like a GUID, but that doesn't make the result a GUID. A GUID is basically a 16 byte number that is computed using an algorithm that guarantees that number is unique. You can generate GUIDs all day long on every computer in the world and not get a duplicate (at least that's the theory). A reformatted int is not unique and it's definitely not a GUID.

2
  • +1, only thing remaining is: en.wikipedia.org/wiki/Globally_unique_identifier, to explore the wonderful variety of algorithms for generating them. Commented Jan 28, 2011 at 8:03
  • Yes but, In the specification of GUID is there a way to convert a classic identifier to a GUID. I'm thinking of a reserved character. Let says something like when GUID start with 0 you can do want you want but you have no guarantee. So all GUID starting by 0 means they have been generated manually with no algorithm. Commented Sep 20, 2018 at 3:25
3

I too needed to encode a primary key (a uint) of a database into a GUID. There is much discussion around whether to use a unit as your Primary key or a Guid, I won't get into that here, but I used a uint as primary key, with a guid as column and I wanted to encode the primary key into the guid, and get it back from the guid.

The below solution encodes a 24bit Primary key (a uint) to a Guid, and decodes it back. It uses DES encryption on the uint. I recognize that the question wanted a 32bit int, this works for 24bit uint, but 32 could be achieved by concatenating two GUIDs, on with the most significant 24bit, and one with the least significant 24 bit to get 48 bits in total. The algorithm is fully tested, and could be used for an int, but is not tested for negative numbers.

GUID Format

A GUID format is 32 digits and may be separated by hyphens, and include braces: 00000000-0000-0000-0000-000000000000 (D) which is 8-4-4-12 characters which is 4-2-2-6 bytes as one byte is coded into 2 characters. Other formats of GUIDs exist.

The 3 byte uint encrypted to an 8 byte code, which is written into the guid beginning at the 5th byte (which is the 10th character of the Guid ignoring brackets and '-'delimiters). Let 'z' represent the coded int, and 0 any other hex character. The resultant coded GUID is

00000000-zzzz-zzzz-zzzz-zzzz00000000

It is noted that the GUID may not be globally unique after this. However, given the GUID encodes your primary key, it WILL be unique for your primary key, and will be very difficult to extract the uint from the GUID without the encryption key....as much as DES encryption is secure.

public void Test()
{  
    Guid NewG = Guid.NewGuid();
    Guid EnryptedGuid = Utility.EncodeInt24InGUID(NewG, Num);
    uint RestoredUint = Utility.DecodeInt24FromGUID(EnryptedGuid);
}

ENCRYPTION

public static Guid EncodeInt24InGUID(Guid guid, uint x)
{
    if (x >= Math.Pow(2, 24))
            throw new ArgumentOutOfRangeException("Unsigned integer is greater than 24bit");

    string strGuid = guid.ToString();
    strGuid = Utility.RemoveChars(strGuid, "-{}");//Remove any '-' and '{}' characters
    byte[] bytes = BitConverter.GetBytes(x);
    var encryptedarray = Cypher.EncryptDES(bytes, Cypher.Key);
    string EncryptedGuid = WriteBytesToString(strGuid, encryptedarray, 9);
    Guid outg;
    Guid.TryParse(EncryptedGuid, out outg);
    return outg;
}

The RemoveChars is copied/adapted from here Removing characters from strings with LINQ

WriteBytesToString is as below

public static string WriteBytesToString(string Input, byte[] bytes, int start)
{
    StringBuilder g = new StringBuilder(Input);
    string temp;
    int ByteNum = 0;
    int CharPos = start;
    int NumBytes = (int)bytes.LongLength; 
    for (int i = 0; i < NumBytes; i++)
    {
        temp = string.Format("{0:x2}", bytes[ByteNum++]);
        g[CharPos++] = (temp.ToCharArray())[0];
        g[CharPos++] = (temp.ToCharArray())[1];
    }
    return g.ToString();
}

DECRYPTION

public static uint DecodeInt24FromGUID(Guid guid)
{
    string strGuid = guid.ToString();
    strGuid = Utility.RemoveChars(strGuid, "-{}");
    byte[] EncryptedBytes = GetBytesFromString(strGuid, 9,8);
    var decrypted = Cypher.DecryptDES(EncryptedBytes, Cypher.Key);
    uint DecryptedUint = BitConverter.ToUInt32(decrypted, 0);
    return DecryptedUint;
}

GetBytesFromString. Note that the bytes begin at the 9th index and are 8 bytes long as hard oded in the calling function. See note on the GUID format

public static byte[] GetBytesFromString(string Input, int start, int NumBytes)
{
    StringBuilder g = new StringBuilder(Input);
    byte[] Bytes = new byte[NumBytes];
    string temp;
    int CharPos = start;
    for (int i = 0; i < NumBytes; i++)
    {
        temp = g[CharPos++].ToString();
        temp += g[CharPos++].ToString();
        Bytes[i] =  byte.Parse(temp, System.Globalization.NumberStyles.HexNumber);
    }
    return Bytes;
}

The cyber class is copied from here https://www.codeproject.com/Articles/5719/Simple-encrypting-and-decrypting-data-in-C, and adapted to DES encryption. It is pasted below in full

using System.IO;
using System.Security.Cryptography;
namespace UtilityLib
{
    // This class is adapted from https://www.codeproject.com/Articles/5719/Simple-encrypting-and-decrypting-data-in-C
    public static class Cypher
    {
        public static string Key = "Asd9847Fg85ihkn52s";
        // Encrypt a byte array into a byte array using a key and an IV 
        public static byte[] EncryptDES(byte[] clearData, byte[] Key, byte[] IV)
        {
            // Create a MemoryStream to accept the encrypted bytes 
            MemoryStream ms = new MemoryStream();
            TripleDES alg = TripleDES.Create();
            alg.Key = Key;
            alg.IV = IV;
            CryptoStream cs = new CryptoStream(ms,alg.CreateEncryptor(), CryptoStreamMode.Write);
            cs.Write(clearData, 0, clearData.Length);
            cs.Close();
            byte[] encryptedData = ms.ToArray();
            return encryptedData;
        }

        public static byte[] EncryptDES(byte[] clearData, string Password)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            return EncryptDES(clearData, pdb.GetBytes(24), pdb.GetBytes(8));
        }
        public static byte[] DecryptDES(byte[] cipherData, byte[] Key, byte[] IV)
        {
            MemoryStream ms = new MemoryStream();
            TripleDES alg = TripleDES.Create(); 
            alg.Key = Key;
            alg.IV = IV;
            CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
            cs.Write(cipherData, 0, cipherData.Length);
            cs.Close();
            byte[] decryptedData = ms.ToArray();
            return decryptedData;
        }

        public static byte[] DecryptDES(byte[] cipherData, string Password)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password,
                new byte[] {0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d,
            0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76});
            return DecryptDES(cipherData, pdb.GetBytes(24), pdb.GetBytes(8));
        }
    }
}
2

I will go even one step further.

Sometimes one needs to come up with a way to go back and forth between int and Guid. Adding a composite id of two or even four ints to mix.

I came up with this:

[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct GuidToInt32 :
    IEquatable<GuidToInt32>
{
    public static readonly GuidToInt32 Empty;

    [FieldOffset(0)]
    private readonly Guid guidValue;

    [FieldOffset(sizeof(int) * 0)]
    private readonly int int32Value1;

    [FieldOffset(sizeof(int) * 1)]
    private readonly int int32Value2;

    [FieldOffset(sizeof(int) * 2)]
    private readonly int int32Value3;

    [FieldOffset(sizeof(int) * 3)]
    private readonly int int32Value4;

    public GuidToInt32(Guid guidValue)
    {
        this.int32Value1 = default;
        this.int32Value2 = default;
        this.int32Value3 = default;
        this.int32Value4 = default;

        this.guidValue = guidValue;
    }

    public GuidToInt32(int int32Value1, int int32Value2 = default, int int32Value3 = default, int int32Value4 = default)
    {
        this.guidValue = default;

        this.int32Value1 = int32Value1;
        this.int32Value2 = int32Value2;
        this.int32Value3 = int32Value3;
        this.int32Value4 = int32Value4;
    }

    public Guid GuidValue => this.guidValue;

    public int Int32Value1 => this.int32Value1;

    public int Int32Value2 => this.int32Value2;

    public int Int32Value3 => this.int32Value3;

    public int Int32Value4 => this.int32Value4;

    public static bool operator ==(GuidToInt32 leftValue, GuidToInt32 rightValue)
    {
        return leftValue.guidValue == rightValue.guidValue;
    }

    public static bool operator !=(GuidToInt32 leftValue, GuidToInt32 rightValue)
    {
        return leftValue.guidValue != rightValue.guidValue;
    }

    public static bool Equals(GuidToInt32 valueA, GuidToInt32 valueB)
    {
        return valueA.guidValue == valueB.guidValue;
    }

    public bool Equals(GuidToInt32 other)
    {
        return this.guidValue == other.guidValue;
    }

    public override bool Equals(object obj)
    {
        if (obj is GuidToInt32 color)
        {
            return GuidToInt32.Equals(this, color);
        }

        return false;
    }

    public override int GetHashCode()
    {
        return this.guidValue.GetHashCode();
    }
}

This can easily be adapted to be used with other types such as uint, long, ulong, float, double ...

I would NOT use that in a green field project, but it is perfect for a revertible migration process.

Not the answer you're looking for? Browse other questions tagged or ask your own question.