Developer forum

Forum » Development » Is the @Code data-integration expression intended to reference only System.Private.CoreLib?

Is the @Code data-integration expression intended to reference only System.Private.CoreLib?

Pedro Meias
Reply

Hi,

We're using the built-in Code script type (@Code(...)) on data-integration column mappings (Dynamicweb 10.27, .NET 10). We've hit a consistent limitation and want to confirm whether it's by design.

Any @Code expression that references a type outside System.Private.CoreLib fails to compile. For example:

@Code(Dynamicweb.Core.Converter.ToInt32(data["x"]))
→ CS0103: The name 'Dynamicweb' does not exist in the current context
The same happens for our own assemblies, and also for framework namespaces like System.Linq (e.g. .OrderBy(...), .Where(...) aren't available). Only System and System.Collections.Generic types work.

From inspecting the behavior, the code-expression compiler (Dynamicweb.Extensibility.ExpressionEvaluators.CodeExpressionEvaluator in Dynamicweb.Core) appears to build its Roslyn reference set from only the assembly containing System.Object (System.Private.CoreLib), and injects only using System; and using System.Collections.Generic;. That would explain exactly what we see.

Our questions:

Is this intended? Is @Code deliberately restricted to System.Private.CoreLib, or is it supposed to reference a broader set (e.g. the loaded application assemblies, or Dynamicweb's own assemblies)?
Is there a supported way to add assembly references or using directives to a @Code expression via configuration?
If it's restricted by design, is implementing a custom ScriptTypeProvider the recommended approach for calling into Dynamicweb or custom assemblies from a mapping? (That's what we've done as a workaround, and it works well.)
Thanks!

 


Replies

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Yes, this is intended.

It is not meant for @Code(SuperExpensiveFunMethod()) - it will break in 10 seconds because, yes - developers cannot help themselves :-).

ScriptTypeProvider is fin - be careful about performance.

I just updated the documentation for ScriptTypeProvider - see below:

Extensibility point for transforming column values during a data integration job. A script type provider is attached to a ColumnMapping, and its GetValue(object) method is called for every row. It receives the source column value and returns the value to write to the destination column.

Built-in implementations include Append, Prepend, Constant, Substring, NewGuid, CurrentTime, and Invert. These can be found in the Dynamicweb.DataIntegration.Integration.ScriptTypes namespace.

How to create a custom provider

Subclass ScriptTypeProvider<TReturnType>, which is preferred because it provides a typed contract, or subclass the base class directly.

Decorate the provider with AddInLabelAttribute to control its display name in the mapping UI. Expose configuration through public properties decorated with AddInParameterAttribute and an appropriate editor attribute.

The class derives from ConfigurableAddIn, so parameters are rendered and persisted automatically. Deploy the assembly with the solution. The provider is then discovered by the add-in system and becomes selectable on column mappings in the data integration job editor.

Things to be careful of

  • GetValue(object) runs once per row. Keep it fast and allocation-light. Avoid database or network calls for each invocation, and cache expensive lookups.
  • The input value may be null or DBNull. Handle both cases.
  • When a provider is assigned to a mapping, its return value bypasses the standard source-to-destination type conversion. The returned object must therefore be compatible with the destination column type.
  • Use the Culture property for culture-sensitive parsing or formatting instead of relying on the current thread culture.
  • Instances must be XML-serializable through the ConfigurableAddIn parameter system. Keep configuration in add-in parameter properties and provide a public, parameterless constructor.
  • The context properties Job, Mapping, SourceColumn, DestinationColumn, and Culture are populated by the framework when the provider is assigned to a mapping. They are null until then, so do not rely on them in the constructor.

Example

The following provider converts string values to uppercase:

using Dynamicweb.Extensibility.AddIns;
using Dynamicweb.Extensibility.Editors;
using Dynamicweb.DataIntegration.Integration;

[AddInLabel("Upper case")]
public class UpperCaseScriptType : ScriptTypeProvider<string>
{
    [AddInParameter("TrimValue"),
     AddInLabel("Trim value"),
     AddInParameterGroup("Scripting"),
     AddInParameterEditor(typeof(YesNoParameterEditor), "")]
    public bool TrimValue { get; set; }

    public override IEnumerable<Type> AllowedTypes { get; set; }
        = new[] { typeof(string) };

    public override string GetValueTyped(object? input)
    {
        var value = input?.ToString() ?? string.Empty;

        if (TrimValue)
            value = value.Trim();

        return value.ToUpper(
            Culture ?? System.Globalization.CultureInfo.InvariantCulture
        );
    }
}

 

You must be logged in to post in the forum