-
Notifications
You must be signed in to change notification settings - Fork 2k
/
RequestContext.cs
129 lines (118 loc) · 4.8 KB
/
RequestContext.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System;
using System.Collections.Generic;
using System.Threading;
namespace Orleans.Runtime
{
/// <summary>
/// This class holds information regarding the request currently being processed.
/// It is explicitly intended to be available to application code.
/// </summary>
/// <remarks>
/// <para>
/// The request context is represented as a property bag.
/// Some values are provided by default; others are derived from messages headers in the
/// request that led to the current processing.
/// </para>
/// <para>
/// Information stored in RequestContext is propagated from
/// Orleans clients to Orleans grains automatically
/// by the Orleans runtime.
/// </para>
/// </remarks>
public static class RequestContext
{
/// <summary>
/// Whether Trace.CorrelationManager.ActivityId settings should be propagated into grain calls.
/// </summary>
public static bool PropagateActivityId { get; set; }
internal const string CALL_CHAIN_REQUEST_CONTEXT_HEADER = "#RC_CCH";
internal const string E2_E_TRACING_ACTIVITY_ID_HEADER = "#RC_AI";
internal const string PING_APPLICATION_HEADER = "Ping";
internal static readonly AsyncLocal<Dictionary<string, object>> CallContextData = new AsyncLocal<Dictionary<string, object>>();
/// <summary>Gets or sets an activity ID that can be used for correlation.</summary>
public static Guid ActivityId
{
get { return (Guid)(Get(E2_E_TRACING_ACTIVITY_ID_HEADER) ?? Guid.Empty); }
set
{
if (value == Guid.Empty)
{
Remove(E2_E_TRACING_ACTIVITY_ID_HEADER);
}
else
{
Set(E2_E_TRACING_ACTIVITY_ID_HEADER, value);
}
}
}
/// <summary>
/// Retrieve a value from the RequestContext key-value bag.
/// </summary>
/// <param name="key">The key for the value to be retrieved.</param>
/// <returns>The value currently in the RequestContext for the specified key,
/// otherwise returns <c>null</c> if no data is present for that key.</returns>
public static object Get(string key)
{
var values = CallContextData.Value;
object result;
if ((values != null) && values.TryGetValue(key, out result))
{
return result;
}
return null;
}
/// <summary>
/// Sets a value into the RequestContext key-value bag.
/// </summary>
/// <param name="key">The key for the value to be updated / added.</param>
/// <param name="value">The value to be stored into RequestContext.</param>
public static void Set(string key, object value)
{
var values = CallContextData.Value;
if (values == null)
{
values = new Dictionary<string, object>(1);
}
else
{
// Have to copy the actual Dictionary value, mutate it and set it back.
// This is since AsyncLocal copies link to dictionary, not create a new one.
// So we need to make sure that modifying the value, we doesn't affect other threads.
var hadPreviousValue = values.ContainsKey(key);
var newValues = new Dictionary<string, object>(values.Count + (hadPreviousValue ? 0 : 1));
foreach (var pair in values)
{
newValues.Add(pair.Key, pair.Value);
}
values = newValues;
}
values[key] = value;
CallContextData.Value = values;
}
/// <summary>
/// Remove a value from the RequestContext key-value bag.
/// </summary>
/// <param name="key">The key for the value to be removed.</param>
/// <returns>Boolean <c>True</c> if the value was previously in the RequestContext key-value bag and has now been removed, otherwise returns <c>False</c>.</returns>
public static bool Remove(string key)
{
var values = CallContextData.Value;
if (values == null || values.Count == 0 || !values.ContainsKey(key))
{
return false;
}
var newValues = new Dictionary<string, object>(values);
bool retValue = newValues.Remove(key);
CallContextData.Value = newValues;
return retValue;
}
public static void Clear()
{
// Remove the key to prevent passing of its value from this point on
if (CallContextData.Value != null)
{
CallContextData.Value = null;
}
}
}
}