< Summary

Information
Class: Elsa.Expressions.CSharp.Services.CSharpEvaluator
Assembly: Elsa.Expressions.CSharp
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions.CSharp/Services/CSharpEvaluator.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 33
Coverable lines: 33
Total lines: 88
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 10
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
EvaluateAsync()0%2040%
GetCompiledScript(...)0%620%
Hash(...)0%2040%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions.CSharp/Services/CSharpEvaluator.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2using System.Text;
 3using Elsa.Expressions.CSharp.Contracts;
 4using Elsa.Expressions.CSharp.Models;
 5using Elsa.Expressions.CSharp.Notifications;
 6using Elsa.Expressions.CSharp.Options;
 7using Elsa.Expressions.Models;
 8using Elsa.Mediator.Contracts;
 9using Microsoft.CodeAnalysis;
 10using Microsoft.CodeAnalysis.CSharp.Scripting;
 11using Microsoft.CodeAnalysis.Scripting;
 12using Microsoft.Extensions.Caching.Memory;
 13using Microsoft.Extensions.Options;
 14
 15namespace Elsa.Expressions.CSharp.Services;
 16
 17/// <summary>
 18/// A C# expression evaluator using Roslyn.
 19/// </summary>
 20/// <remarks>
 21/// Initializes a new instance of the <see cref="CSharpEvaluator"/> class.
 22/// </remarks>
 023public class CSharpEvaluator(INotificationSender notificationSender, IOptions<CSharpOptions> scriptOptions, IMemoryCache
 24{
 025    private readonly CSharpOptions _csharpOptions = scriptOptions.Value;
 26
 27    /// <inheritdoc />
 28    public async Task<object?> EvaluateAsync(
 29        string expression,
 30        Type returnType,
 31        ExpressionExecutionContext context,
 32        ExpressionEvaluatorOptions options,
 33        Func<ScriptOptions, ScriptOptions>? configureScriptOptions = default,
 34        Func<Script<object>, Script<object>>? configureScript = default,
 35        CancellationToken cancellationToken = default)
 36    {
 037        var scriptOptions = ScriptOptions.Default.WithOptimizationLevel(OptimizationLevel.Release);
 38
 039        if (configureScriptOptions != null)
 040            scriptOptions = configureScriptOptions(scriptOptions);
 41
 042        var globals = new Globals(context, options.Arguments);
 043        var script = CSharpScript.Create("", scriptOptions, typeof(Globals));
 44
 045        if (configureScript != null)
 046            script = configureScript(script);
 47
 048        var notification = new EvaluatingCSharp(options, script, scriptOptions, context);
 049        await notificationSender.SendAsync(notification, cancellationToken);
 050        scriptOptions = notification.ScriptOptions;
 051        script = notification.Script.ContinueWith(expression, scriptOptions);
 052        var runner = GetCompiledScript(script);
 053        return await runner(globals, cancellationToken: cancellationToken);
 054    }
 55
 56    private ScriptRunner<object> GetCompiledScript(Script<object> script)
 57    {
 058        var cacheKey = "csharp:script:" + Hash(script);
 59
 060        return memoryCache.GetOrCreate(cacheKey, entry =>
 061        {
 062            if (_csharpOptions.ScriptCacheTimeout.HasValue)
 063                entry.SetSlidingExpiration(_csharpOptions.ScriptCacheTimeout.Value);
 064
 065            return script.CreateDelegate();
 066        })!;
 67    }
 68
 69    private static string Hash(Script<object> script)
 70    {
 071        var ms = new MemoryStream();
 072        using (var sw = new StreamWriter(ms, Encoding.UTF8))
 73        {
 074            for (Script current = script; current != null; current = current.Previous)
 75            {
 076                sw.WriteLine(current.Code);
 77            }
 078        }
 79
 080        if (!ms.TryGetBuffer(out var segment))
 81        {
 082            segment = ms.ToArray();
 83        }
 84
 085        var hash = SHA256.HashData(segment.AsSpan());
 086        return Convert.ToBase64String(hash);
 87    }
 88}