Skip to content

Commit dbb9acb

Browse files
authored
Allow setting the context in a temporary using scope for IEncoder instances (#3044)
* Allow setting the context in a temporary using scope for IEncoder instances * make the setter of Current property private
1 parent 95d162e commit dbb9acb

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed

Stack/Opc.Ua.Core/Types/BuiltIn/MessageContextExtension.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
1111
*/
1212

13+
using System;
14+
using System.Threading;
15+
1316
namespace Opc.Ua
1417
{
1518
#region MessageContextExtension Class
@@ -29,7 +32,11 @@ public MessageContextExtension(IServiceMessageContext messageContext)
2932
/// <summary>
3033
/// Returns the message context associated with the current operation context.
3134
/// </summary>
32-
public static MessageContextExtension Current => null;
35+
public static MessageContextExtension Current
36+
{
37+
get => s_current.Value;
38+
private set => s_current.Value = value;
39+
}
3340

3441
/// <summary>
3542
/// Returns the message context associated with the current operation context.
@@ -53,6 +60,40 @@ public static IServiceMessageContext CurrentContext
5360
/// The message context to use.
5461
/// </summary>
5562
public IServiceMessageContext MessageContext { get; private set; }
63+
64+
/// <summary>
65+
/// Set the context for a specific using scope
66+
/// </summary>
67+
/// <param name="messageContext"></param>
68+
/// <returns></returns>
69+
public static IDisposable SetScopedContext(IServiceMessageContext messageContext)
70+
{
71+
var previousContext = Current;
72+
Current = new MessageContextExtension(messageContext);
73+
74+
return new DisposableAction (() => Current = previousContext);
75+
}
76+
77+
/// <summary>
78+
/// Disposable wrapper for reseting the Current context to
79+
/// the previous value on exiting the using scope
80+
/// </summary>
81+
private class DisposableAction : IDisposable
82+
{
83+
private readonly Action action;
84+
85+
public DisposableAction(Action action)
86+
{
87+
this.action = action;
88+
}
89+
90+
public void Dispose()
91+
{
92+
action?.Invoke();
93+
}
94+
}
95+
96+
private static readonly AsyncLocal<MessageContextExtension> s_current = new AsyncLocal<MessageContextExtension>();
5697
}
5798
#endregion
5899
}

Tests/Opc.Ua.Client.ComplexTypes.Tests/Types/EncoderTests.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,57 @@ StructureFieldParameter structureFieldParameter
168168
baseType[structureFieldParameter.Name] = null;
169169
EncodeDecodeComplexType(EncoderContext, memoryStreamType, encoderType, jsonEncodingType, StructureType.Union, nodeId, emittedType);
170170
}
171+
172+
/// <summary>
173+
/// Verify serialize/encode of a structured type initiated from outside of an IEncoder instance.
174+
/// </summary>
175+
[Theory]
176+
[Category("ComplexTypes")]
177+
public void ReEncodeComplexTypeScopedContext(
178+
MemoryStreamType memoryStreamType,
179+
StructureType structureType
180+
)
181+
{
182+
ExpandedNodeId nodeId;
183+
Type complexType;
184+
(nodeId, complexType) = TypeDictionary[structureType];
185+
object emittedType = Activator.CreateInstance(complexType);
186+
var baseType = emittedType as BaseComplexType;
187+
FillStructWithValues(baseType, true);
188+
189+
ExtensionObject extensionObject = new ExtensionObject(emittedType);
190+
191+
Opc.Ua.KeyValuePair keyValuePair = new Opc.Ua.KeyValuePair();
192+
keyValuePair.Key = "AKEY";
193+
keyValuePair.Value = extensionObject;
194+
195+
ServiceMessageContext localCtxt = (ServiceMessageContext)EncoderContext;
196+
197+
// Serialize/Encode a Variant fails without a context available
198+
Assert.Throws(
199+
typeof(Newtonsoft.Json.JsonSerializationException),
200+
() => Newtonsoft.Json.JsonConvert.SerializeObject(keyValuePair));
201+
202+
// Serialize/Encode an ExtensionObject fails without a context available
203+
var extObjToEncode = new ExtensionObject(keyValuePair);
204+
Assert.Throws(
205+
typeof(Newtonsoft.Json.JsonSerializationException),
206+
() => Newtonsoft.Json.JsonConvert.SerializeObject(extObjToEncode));
207+
208+
// Serialize/Encode a Variant succeeds with a context available
209+
using (MessageContextExtension.SetScopedContext(localCtxt))
210+
{
211+
_ = Newtonsoft.Json.JsonConvert.SerializeObject(keyValuePair);
212+
}
213+
214+
// Serialize/Encode an ExtensionObject succeeds with a context available
215+
using (MessageContextExtension.SetScopedContext(localCtxt))
216+
{
217+
_ = Newtonsoft.Json.JsonConvert.SerializeObject(extensionObject);
218+
}
219+
220+
221+
}
171222
#endregion Test Methods
172223
}
173224
}

0 commit comments

Comments
 (0)