We were unable to load Disqus. If you are a moderator please see our troubleshooting guide.

Brett Smith • 9 years ago

Hey Dave, love this - especially coming from a kiwi(I presume from the domain?) I use this regularly and have done so for a long time. I hope one day i'll get the chance to buy you a beer or two in real life. I've just come across a situation where I need something like this to be all lower case but
ShortGuid.Decode(ShortGuid.Encode(b5b35c4d-2b29-4773-b8ba-18ccab5e5bc1).ToLowercase())=b6b3fcb6-2bc9-4773-b8ba-18e6ab5e5bc2... i.e b5b35c4d-2b29-4773-b8ba-18ccab5e5bc1= b6b3fcb6-2bc9-4773-b8ba-18e6ab5e5bc2 (2 char difference).

Any thoughts on how this might be possible?

Dave Transom • 9 years ago

Actually, you can't transform (lowercase, uppercase) the characters on a `ShortGuid`, because they're essentially base64, just with well known special characters replaced (/, +) or removed (==). Base64 needs the entire range of chars to correctly represent the data within (https://en.wikipedia.org/wi...

Running the lowercase on the `ShortGuid` as you've shown will result in a different byte array when `Decode` calls `Convert.FromBase64String()`

Dave Transom • 9 years ago

Glad you've found it helpful, Bret!

Dang, that's an interesting one though - thanks for letting me know - I'll write some unit tests for it and come back to you soon.

Matthew Abbott • 14 years ago

Nice piece of work. The only thing I would change on it, is to make the ShortGuid an immutable struct. That being, remove the setters for the properties.

Structs ideally represent values that don't change. It is easy enough to create a new instance of ShortGuid, you don't really need to change the value of it.

Fred • 6 years ago

You should put this on GitHub, and make a NuGet package.

Dave Transom • 6 years ago

Well, I guess it's time eh Fred Here you go:
GitHub
NuGet

chriszo111 • 7 years ago

Hey Dave, could you make this a NuGet package? I'd love to use it, but my project wouldn't allow me to use this code snippet due to possible maintenance that could come up. I think this would be awesome if you could install it as a package, even though it is very simple!

Dave Transom • 6 years ago

I finally got around to doing this chriszo111 - my, how the years fly [post updated with details if you're still interested]

lordrak • 8 years ago

Hi,
very nice piece of code. I have a problem. When deserializing shortguid and string value is null Decode method fail. It may stay null as like as never assigned. Here is update:
public static Guid Decode(string value)
{
if (string.IsNullOrEmpty(value))
return new Guid();
value = value
.Replace("_", "/")
.Replace("-", "+");
byte[] buffer = Convert.FromBase64String(value + "==");
return new Guid(buffer);
}

Gall Anonim • 8 years ago

How to make same code for VB.net?

dasjestyr • 8 years ago

The strings are not unique, it seems. I noticed to following strings (off by 1) actually result in the same Guid which makes me nervous...

mgD4GvCOZE2MtZWw1vIqvr
mgD4GvCOZE2MtZWw1vIqvg

Dave Transom • 8 years ago

Indeed, they produce the same byte array when appending "==" and running through `Convert.FromBase64String()`. I would expect the *same input* to produce a *consistent output* in this case.

I'm curious, can you share some code that reproduces this dasjestyr?

dasjestyr • 8 years ago

Seems pretty consistent. I just genated a new guid, made a short guid, changed the last character, made a new short guid from it and compared to the old guid and they were equal but shouldn't be.

https://gist.github.com/das...

Dave Transom • 8 years ago

Okay, that makes more sense that it's been tempered with.

If you step through a base64 decoder, you'll see the maths for this. The v (47) is shifted left, yielding 188, and the r (32) and g (43) are shifted right, yielding 2 - giving the same byte (190) as the last byte. The remainder is used if the base64 string is longer.

You won't get clashes. Anyone using base64 to encode the guid will yield the same string.

Jaime Olivares • 9 years ago

This seems like a huge implementation for a simple problem. We can just use:

var shortUid = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Trim('=');

dasjestyr • 8 years ago

There's a spec for UrlEncodedBase64 that you'd wanna use

Dave Transom • 9 years ago

If you want URL compatibility, you'll also want to replace plus and slash characters as per Mad's original En/Decode methods (which aren't huge at all), that's all you need.

But sure, this has more code, mainly aimed around convenience and casting between types.

I find it handy to mark variables as this type, which to me indicates a differing incoming/outgoing value (a fudged string) verses the value I want to use (the guid).

Dallas Beek • 9 years ago

I ran into this issue that Nicolas mentioned 4 years ago "Unfortunately it won't work with lower case only URLs (often used to optimize SEO)." I think maybe the solutions is to use Base32Encoding instead. I've been working out some tests with the code Shane posted here http://stackoverflow.com/qu.... The resulting strings aren't quite as short but they do work.

f35cfe5c-1c8a-4e17-be7f-b37196538eaf = "lt7fz44kdqlu5pt7wnyzmu4ov4"

public static string Encode ( Guid guid ) {
var encoded = Base32Encoding.ToString( guid.ToByteArray() );
return encoded.Substring( 0, 26 ).ToLower();
}

public static Guid Decode ( string value ) {
try {
var buffer = Base32Encoding.ToBytes( value.ToUpper() + "======" );
return new Guid( buffer );
} catch {
return Guid.Empty;
}
}

Le Vu • 9 years ago

Thanks for sharing.

w4n • 10 years ago

Thanks for sharing Dave! This will come in real handy for a project I'm doing.

Albert Arul Prakash • 10 years ago

Is it ok to use this code in a commercial work?

Dave Transom • 10 years ago

Hi Albert - yes indeed!

I've intended to add a license for sometime now - I'll update my blog to use the MIT (e.g. https://github.com/csharpvi...

Thanks for reminding me.

Steven Rasmussen • 10 years ago

Just thought I'd share a SQL function that converts a UNIQUEIDENTIFIER field to the ShortGuid string representation. I derived this using this C# library so thanks to the author:

/****** Object: UserDefinedFunction [dbo].[GetShortId] Script Date: 7/15/2015 1:08:45 PM ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE FUNCTION [dbo].[GetShortId] ( @Input UNIQUEIDENTIFIER )

RETURNS VARCHAR(50)

BEGIN

DECLARE @InputBinary VARBINARY(MAX)

SET @InputBinary = CAST(@Input AS VARBINARY(MAX))

DECLARE @EncodedGuid VARCHAR(38)

SET @EncodedGuid = CAST(N'' as xml).value('xs:base64Binary(sql:variable("@InputBinary"))', 'VARCHAR(max)')

DECLARE @StrippedGuid VARCHAR(32)

SET @StrippedGuid = REPLACE(REPLACE(@EncodedGuid, '/', '_'), '+', '-')

RETURN SUBSTRING(@StrippedGuid, 1, 22) -- Removes the '==' from the end

END
GO

Dave Transom • 10 years ago

Thanks Steven - that's handy.

Steven Rasmussen • 10 years ago

I also ended up writing the converse to the above function to convert a ShortID back to a uniqueidentifier. Here it is for anyone that needs it:

/****** Object: UserDefinedFunction [dbo].[ConvertFromShortId] Script Date: 7/27/2015 10:30:29 AM ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE FUNCTION [dbo].[ConvertFromShortId] ( @Input NVARCHAR(22) )

RETURNS UNIQUEIDENTIFIER

BEGIN

DECLARE @InputConverted NVARCHAR(24)

SET @InputConverted = REPLACE(REPLACE(@Input, '_', '/'), '-', '+') + '=='

DECLARE @Decoded varbinary(max)

set @Decoded = cast('' as xml).value('xs:base64Binary(sql:variable("@InputConverted"))', 'varbinary(max)')

RETURN CAST(@decoded AS uniqueidentifier)

END

GO

teapeng • 11 years ago

Good job! Thanks a million. :)

Rob de Voer • 11 years ago

Hi Dave,

Your code convinced me to switch my standard code for token generation to use ShortGuids.

Saved me time and it works well.

Thanks for sharing this!

Regards,
Rob

Anon • 11 years ago

Updated link to Mads Kristensen's post:

http://madskristensen.net/p...

Nicholas Rogoff • 12 years ago

Great stuff. Been hacking my own, but this is much better than my efforts. Much appreciated.

new links are not working either... i dont know if its me

Wobble • 12 years ago

Mads has changed his domain so the links on this article are wrong now.

Please change Mads Kristensen's links to :
http://madskristensen.net/p...

tester • 12 years ago

How to convert to shortGUID without hyphen?

Nikhil • 13 years ago

Hi,

Nice post. But I am looking for short unique string Maximum of 10 alpha numeric characters. the above post generate unique but length is more. Could you please suggest any post you have come across for the same?

Regards
Nikhil

Dave Transom • 13 years ago

@Thiago Lemos:

The problem is that is doesn't _look_ as well structured as a Guid with groups and separators
e.g. 00000000-0000-0000-0000-000000000000

It has the following consistency properties.
1. it's 22 characters long
2. the characters can be any of A-Z, a-z, 0-9, a dash (-) and an underscore (_)

So a regex should use the following pattern to match exactly.
new Regex(@"^[A-Za-z0-9-_]{22}$")

I haven't actually tried it in a routing environment as yet. Might have a look at see the best way to handle this.

Remember you can pass a string (or a Guid) to ShortGuid and it will implicitly convert it.

Thiago Lemos • 13 years ago

Is possible to match this shortguid with a regex? I'm asking this because I want use ShortGuid in my URLs, but i need the regex to define the routes in my MVC project! original GUID has a pattern who give me a way to validate, thanks for share this code, this is very helpful

Dave Transom • 13 years ago

@Boris: It depends.

When comparing a ShortGuid to another ShortGuid, the underlying Guid is used to compared. So case doesn't matter.

When comparing a string or Guid to a ShortGuid, the string (or Guid) will be implicitly converted to a ShortGuid, then the underlying Guids will be compared as above.

If you've compare a string representation of a ShortGuid to something that is _not_ a ShortGuid, case will definitively matter.

HTH

Thanks everyone else for the kind words and contributions.

Boris • 13 years ago

Hi Dave,

Very good work.

Just a question about the short GUID comparisons. Is the string representation of the short GUID case insensitive (as it is for the normal GUID)?

Najeeb • 13 years ago

Hi Dave,

Thanks a lot for this! It's really helpful for something that I am working on currently. :)

/Najeeb/

Nicolas • 13 years ago

Unfortunately it won't work with lower case only URLs (often used to optimize SEO).

Spyros • 13 years ago

Will definitely use it . Thnx a lot for this clever lib!
Cheers from Greece

ILICH • 13 years ago

in case you need just binder:

public class ShortGuidModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
var isShort = value.Length == 22;
var shortGuidValue = isShort ? new ShortGuid(value) : new ShortGuid(Guid.Parse(value));

if (bindingContext.ModelType == typeof(ShortGuid))
return shortGuidValue.Value;

if (bindingContext.ModelType == typeof(Guid))
return shortGuidValue.Guid;

throw new AnelosophyException("can handle guids only");
}
}

Laurynas • 13 years ago

I would chage the property setters from:

if (value != _guid)
{
_guid = value;
_value = Encode(value);
}
if (value != _value)
{
_value = value;
_guid = Decode(value);
}

to:

if (value != _guid)
{
_value = Encode(value);
_guid = value;
}
if (value != _value)
{
_guid = Decode(value);
_value = value;
}

to avoid corrupting object state in case Encode or Decode would throw an exception.

Thanks for the code.

Saeed Neamati • 14 years ago

If I were you, I would recommend my code to Microsoft, to include in future versions of .NET.

Once we wanted to create random coupons for our products, and we wished for such a utility to produce shorter random strings. But we couldn't find anything, and the .NET's Guid was inefficient (too long) in our case.

Great thought buddy. Thanks.

Jon • 14 years ago

Thanks very much for this, it has come in very handy.

I added a TryParse method for my purposes:

public static bool TryParse(string input, out ShortGuid result)
{
try
{
result = new ShortGuid(input);
}
catch (Exception)
{
result = ShortGuid.Empty;
}
return (result != ShortGuid.Empty);
}

I'm being lazy and relying on an exception from the constructor so I'm sure there are better ways (also it would treat an empty ShortGuid as invalid) - but it works for me :)

Thanks again

Mike • 14 years ago

@Dave,

Thanks. I'm in a highly regulated industry right now and the lawyers tend to bristle at code without an explicit license. I'll deal with them though.

Thanks again!

Dave Transom • 14 years ago

@Mike: Thanks, you raise an interesting point - I don't currently publish a licence under which snippets and work on this blog can be re-used.

I've intended it to be freely available, customisable and distributable.

I'll choose a license shortly, however, please feel free to use it as you will until I've specified a license.

HTH

Mike • 14 years ago

I don't see anywhere that you've specified a license for this work. Would you consider releasing it under the MIT license? Thanks.

paco • 14 years ago

Thank you! This is great stuff!

Dave Transom • 14 years ago

@Kieran: Appreciate the feedback - though I'm unsure why you'd think that provides a gain in efficiency. It's more of a change in coding style isn't it?


If you look at the IL generated between your version and mine, the only difference is "equality" verses "inequality".

IL for original version:
L_0008: call bool [mscorlib]System.Guid::op_Inequality(valuetype [mscorlib]System.Guid, valuetype [mscorlib]System.Guid)

IL for your version:
L_0008: call bool [mscorlib]System.Guid::op_Equality(valuetype [mscorlib]System.Guid, valuetype [mscorlib]System.Guid)


Even decompiling your method (using Reflector, ILSpy may give a different result) to C# it turns out like this:

if (!(value == this._guid))
{
this._guid = value;
this._value = Encode(value);
}

Kieran • 14 years ago

Thanks for sharing this, very useful.

If I could suggest a little refactoring on two properties just for a small efficency gain.

Setters for Guid and Value:

public Guid Guid
{
get { return _guid; }
set
{
// Code change
if (value == _guid) return;
_guid = value;
_value = Encode(value);
}
}


public string Value
{
get { return _value; }
set
{
// Code change
if (value == _value) return;
_value = value;
_guid = Decode(value);
}
}

Thanks again