15

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
  • 58,060
  • 25
  • 98
  • 150
Ian Vink
  • 66,960
  • 104
  • 341
  • 555
  • 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

8

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
  • 66,960
  • 104
  • 341
  • 555
TGH
  • 38,769
  • 12
  • 102
  • 135
  • 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
8

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

Ronnie Overby
  • 45,287
  • 73
  • 267
  • 346
  • 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
  • The post is old, but why do you wrap the context in an HttpContextWrapper for getting the id? – Sam Sep 28 '22 at 09:21
  • 1
    @Sam No idea what I was thinking because that obviously gives you a different instance of the wrapper class and therefore a different Id. – Ronnie Overby Sep 29 '22 at 12:56
  • 1
    @Sam I updated the code so that it's not as busted as before, though I would cache the delegate that get's the private field value and make an automated test to prove that the reflection works at runtime. – Ronnie Overby Sep 29 '22 at 13:02
2

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
  • 6,490
  • 2
  • 35
  • 37