14

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?

tereško
  • 57,247
  • 24
  • 95
  • 149
Ian Vink
  • 63,888
  • 100
  • 326
  • 544
  • Possible duplicate of [log4net unique request id in ASP.NET](http://stackoverflow.com/questions/15842321/log4net-unique-request-id-in-asp-net) – Akira Yamamoto May 18 '16 at 21:07

3 Answers3

9

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

Ian Vink
  • 63,888
  • 100
  • 326
  • 544
TGH
  • 37,937
  • 11
  • 96
  • 131
  • 1
    But how do I attached it to a Request so that it is accessible in the entire lifecycle of the request. – Ian Vink Oct 09 '14 at 22:16
7

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) => new HttpContextWrapper(ctx).GetId();

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

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().

Ronnie Overby
  • 43,601
  • 70
  • 265
  • 343
  • why are you using `string.Intern` for the mutex here? – robbymurphy May 09 '16 at 13:57
  • Because it produces an object references that I don't have to keep track of. I talk more about it here: http://stackoverflow.com/a/29878495/64334 – Ronnie Overby Jul 07 '16 at 15:22
  • 1
    @RonnieOverby won't interning so many strings (one per request) have a negative impact on memory usage, since interned strings are not garbage collected and will remain in the pool until it is restarted? – Luiso Jan 22 '18 at 18:57
1

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.

Sergio Cabral
  • 5,588
  • 33
  • 35