Convert Int to Guid
C#C# Problem Overview
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
C# Solutions
Solution 1 - C#
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.
Solution 2 - C#
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.
Solution 3 - C#
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.
Solution 4 - C#
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 https://stackoverflow.com/questions/449513/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));
}
}
}
Solution 5 - C#
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 int
s 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.