Behavior when loading as a plugin using `McMaster.NETCore.Plugins`
Smart.Format version: 3.6.0
Framework version: net8.0
-
Please provide a working source code example to reproduce the bug Unable to replicate
-
What is the current result? I am dynamically loading DLL-based plugins that include a Smart.Format call, and after a call where the selector fails due to missing a matching key in the data object (Dictionary<string, object>), I am unable to successfully run the plugin again.
-
What is the expected result? Smart.Format should execute consistently after failures.
-
Please post full exception details if applicable (message, stacktrace, inner exceptions)
-
Did you find a workaround? yes/no No
-
Is there a version in which it worked? Don't know
-
Can you help us by writing an unit test? Would be happy to if I could replicate.
Full Reproduction (with code)
var formatter = Smart.CreateDefaultSmartFormat(new SmartSettings() { CaseSensitivity = CaseSensitivityType.CaseInsensitive })
.AddExtensions(new SmartFormat.Extensions.NewtonsoftJsonSource());
//construct our message
var data = new Dictionary<string, object>();
foreach (var argument in context.GetParameterValue<Dictionary<string, object>>("Args") ?? new Dictionary<string, object>())
{
data.Add(argument.Key, argument.Value);
}
var messageTemplate = context.GetParameterValue<string>("MessageTemplate");
var message = formatter.Format(messageTemplate, data);
context.SetParameterValue("Message", message);
return PluginReturnState.Completed;
Execution 1: MessageTemplate = "Hello World from Plugin" Result - success (Hello World from Plugin)
Execution 2: MessageTemplate = "Hello World from {User}" Args = { { "User" : "TestMethod" } } Result - success (Hello World from TestMethod)
Execution 3: MessageTemplate = "Hello World from {NoKey}" Args = { { "User" : "TestMethod" } } Result - exception -
Error parsing format string: No source extension could handle the selector named "NoKey" at 18
Hello world from {NoKey}
------------------^
Execution 4: MessageTemplate = "Hello World from {User}" Args = { { "User" : "TestMethod" } } Result - exception -
Error parsing format string: No source extension could handle the selector named "" at 18
Hello world from {User}
------------------^
Also notable, that each execution involves loading the plugin DLL from bytes into a specific AssemblyLoadContext (handled by McMaster.NETCore.Plugins). When emulating this scenario with a WebApplicationFactory (MSTest in-memory host), the failure does not reproduce.
I have to restart my application fully in order to get any type of message with a selector in it to not result in an error similar to above - notably the lack of "Selected named <emptyString>".
The exception "No source extension could handle the selector" can mean
- the argument cannot be handled by the
NewtonsoftJsonSourceat all - although it can be handled, the variable cannot be found (like in your Execution 3 case)
Execution 2 and 4 look identical, but are the args really as expected, meaning the NewtonsoftJsonSource can deserialize them.
If NewtonsoftJsonSource fails, does SystemTextJsonSource as well?
To narrow down what's happening, you should ensure that the possible causes for an exception (see above) can be excluded.
Hi, I do understand the error, and for execution #2 it is the expected behavior - in this specific scenario, json isn't involved at all - in my code snippet I use a Dictionary<string, object> as the datasource.
I have inspected all the input data, and the state of the class that calls into the SmartFormat library, and it is identical for executions 1 and 3 - the only difference is within the SmartFormat itself, the selector name is somehow now empty (noted from the error message first). I have tried walking through the library code but have not managed to pinpoint the difference.
There seems to be some static cache or similar that fails to initialize when potentially the module has been unloaded and then is reloaded? Just grasping at straws.... I will attempt more to create a reproduction.
Now I've got it.
As you mentioned "some static cache": Caching is not directly involved for the DictionarySource, but with ObjectPools. Once the objects like Format, FormattingInfo and FormatDetails are created, it's hard to imagine where and why they should forget values, unless they're returned to the pool or get disposed. I assume you left the default SmartSettings.IsThreadSafeMode = true unchanged (just another straw).
Another 2cts: You could observe what happens to the Format with sth. like
using var formatParsed = new Parser().ParseFormat("your input string")
var smart = Smart.CreateDefaultSmartFormat(...)
var result = smart.Format(formatParsed, yourArgs);
Well, a reproducer would be helpful.
@jonmotos Any progress regarding a reproducer, or found a solution?
Closed because of missing activity