Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.Net MVC Unique id per request

In our MVC 5 site, no session, we need to get/generate a unique ID per request. THhis will be used as an ID for logging all activity in the request.

Is there a way to assign/get a value to a Request to enable this?

like image 534
Ian Vink Avatar asked Oct 09 '14 21:10

Ian Vink


3 Answers

Add it to the request item collection odetocode.com/articles/111.aspx

Guid.NewGuid()

Will generate a unique id.

http://msdn.microsoft.com/en-us/library/system.guid.newguid(v=vs.110).aspx

like image 171
TGH Avatar answered Nov 19 '22 02:11

TGH


Here's some extension methods that I created for this purpose:

public static Guid GetId(this HttpContext ctx) => new HttpContextWrapper(ctx).GetId();

public static Guid GetId(this HttpContextBase ctx)
{
    const string key = "tq8OuyxHpDgkRg54klfpqg== (Request Id Key)";

    if (ctx.Items.Contains(key))
        return (Guid) ctx.Items[key];

    var mutex = string.Intern($"{key}:{ctx.GetHashCode()}");
    lock (mutex)
    {
        if (!ctx.Items.Contains(key))
            ctx.Items[key] = Guid.NewGuid();

        return (Guid) ctx.Items[key];
    }
}

#Update

@Luiso made a comment about interned strings not being garbage collected. This is a good point. In a very busy web app that doesn't recycle app pools frequently enough (or ever) this would be a serious issue.

Thankfully, recent versions of .NET have ephemeron support via the ConditionalWeakTable<TK,TV> class. Which gives a convenient and memory safe way of tackling this problem:

       private static readonly ConditionalWeakTable<object, ConcurrentDictionary<string, object>>
        Ephemerons = new ConditionalWeakTable<object, ConcurrentDictionary<string, object>>();

    public static Guid GetId(this HttpContext ctx)
    {
        var dict = Ephemerons.GetOrCreateValue(ctx);
        var id = (Guid)dict.GetOrAdd(
            "c53fd485-b6b6-4da9-9e9d-bf30500d510b",
            _ => Guid.NewGuid());
        return id;
    }

    public static Guid GetId(this HttpContextBase ctx)
    {
        // this is slow and subject to breaking change; TODO: smart things to mitigate those problems
        var innerContext = (HttpContext)ctx.GetType().GetField("_context", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(ctx);
        return innerContext.GetId();
    }

My Overby.Extensions.Attachments package has an extension method that simplify things.

/// <summary>
/// Unique identifier for the object reference.
/// </summary>
public static Guid GetReferenceId(this object obj) =>
    obj.GetOrSetAttached(() => Guid.NewGuid(), RefIdKey);

Using that extension method, you would just call httpContext.GetReferenceId().

like image 21
Ronnie Overby Avatar answered Nov 19 '22 00:11

Ronnie Overby


If you need a single value without having to implement something like Guid.NewGuid(), perhaps the HttpContext timestamp will work for you.

int requestId = System.Web.HttpContext.Current.Timestamp.Ticks;

A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond, or 10 million ticks in a second.

Then this solution should be evaluated according to their circumstances because there is the possibility that two requests fall on the same tick of time.

like image 2
Sergio Cabral Avatar answered Nov 19 '22 02:11

Sergio Cabral