Skip to content

Script Tag ​

SmartSql's built-in XML tags (<IsNotEmpty>, <IsEqual>, <Switch>, etc.) handle most conditional SQL generation, but sometimes you need more expressive logic. The SmartSql.ScriptTag package adds a <Script> tag that evaluates JavaScript expressions using the Jint engine, giving you full programmatic control over SQL generation directly in your XML maps.

At a Glance ​

FeatureDescription
PackageSmartSql.ScriptTag
JS EngineJint (pure C# JavaScript interpreter)
Tag Name<Script>
AttributeTest -- JavaScript expression that evaluates to boolean
Operator MappingAutomatic translation of text operators to JS operators

How It Works ​

The Script tag extends SmartSql's Tag base class. When the middleware pipeline processes a statement, the Script tag evaluates its Test expression against the current request parameters:

mermaid
sequenceDiagram
autonumber
    participant Pipeline as Middleware Pipeline
    participant Tag as Script Tag
    participant Jint as Jint Engine
    participant Context as RequestContext

    Pipeline->>Tag: IsCondition(requestContext)
    Tag->>Jint: new Engine()
    Tag->>Context: Read Parameters
    loop Each parameter
        Tag->>Jint: engine.SetValue(key, value)
    end
    Tag->>Jint: Execute(Test expression)
    Jint-->>Tag: boolean result
    Tag-->>Pipeline: true (include SQL) or false (skip)

Operator Mapping ​

The Script tag automatically translates human-friendly text operators to JavaScript operators. This makes the XML more readable:

mermaid
flowchart LR
    subgraph Mapping["Operator Mapping"]
        style Mapping fill:#161b22,stroke:#30363d,color:#e6edf3
        A["and"] --> A2["&&"]
        B["or"] --> B2["||"]
        C["gt"] --> C2[">"]
        D["gte"] --> D2[">="]
        E["lt"] --> E2["<"]
        F["lte"] --> F2["<="]
        G["eq"] --> G2["=="]
        H["neq"] --> H2["!="]
    end

    style A fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style A2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style B fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style B2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style C fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style C2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style D fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style D2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style E fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style E2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style F fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style F2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style G fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style G2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style H fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style H2 fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

The mapping is applied via regex replacement at construction time, so you write:

xml
<Script Test="age gt 18 and status neq 'inactive'">

and it is internally converted to:

javascript
age > 18 && status != 'inactive'

Tag Class Hierarchy ​

mermaid
classDiagram
    class ITag {
        <<interface>>
        +Prepend string
        +Property string
        +Statement Statement
        +IsCondition(context) bool
        +BuildSql(context) void
    }

    class Tag {
        <<abstract>>
        +Prepend string
        +Property string
        +Statement Statement
        +ChildTags List~ITag~
        +IsCondition(context)* bool
        +BuildSql(context) void
    }

    class Script {
        -Dictionary~string,string~ _operatorMappings
        -Regex _operatorRegex
        +Test string
        +IsCondition(context) bool
        -ParseOperator(test) string
    }

    class ScriptBuilder {
        +Build(xmlNode, statement) ITag
    }

    class AbstractTagBuilder {
        <<abstract>>
        +Build(xmlNode, statement)* ITag
        #GetPrepend(xmlNode) string
        #GetProperty(xmlNode) string
    }

    ITag <|.. Tag
    Tag <|-- Script
    AbstractTagBuilder <|-- ScriptBuilder
    ScriptBuilder ..> Script : creates

    style ITag fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Tag fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style Script fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style ScriptBuilder fill:#2d333b,stroke:#6d5dfc,color:#e6edf3
    style AbstractTagBuilder fill:#2d333b,stroke:#6d5dfc,color:#e6edf3

Usage in XML Maps ​

Basic Conditional Clause ​

xml
<Statement Id="QueryUsers">
  SELECT * FROM Users
  <Where>
    <Script Test="age gt 0">
      AND Age > ?Age
    </Script>
    <IsNotEmpty Prepend="And" Property="Name">
      AND Name LIKE CONCAT('%', ?Name, '%')
    </IsNotEmpty>
  </Where>
</Statement>

Complex Expressions ​

The Script tag supports any JavaScript expression that Jint can evaluate:

xml
<Script Test="minPrice neq null and maxPrice neq null and minPrice lt maxPrice">
  AND Price BETWEEN ?MinPrice AND ?MaxPrice
</Script>

Combining with Other Tags ​

Script tags can be nested inside <Where>, <Switch>, or other dynamic tags just like any other SmartSql tag:

xml
<Statement Id="AdvancedQuery">
  SELECT * FROM Products
  <Where>
    <Script Test="categoryIds.length gt 0">
      AND CategoryId IN
      <For Property="categoryIds" Open="(" Close=")" Separator=",">
        ?categoryIds
      </For>
    </Script>
    <IsEqual Property="Status" CompareValue="1">
      AND IsActive = 1
    </IsEqual>
  </Where>
</Statement>

Tag Builder Registration ​

The ScriptBuilder class registers the <Script> tag with SmartSql's tag builder system. It extends AbstractTagBuilder and creates Script instances from XML nodes:

MethodDescription
Build(XmlNode, Statement)Creates a Script tag from the XML node's Test attribute

To enable the Script tag, register the tag builder in your configuration:

xml
<SmartSqlMapConfig>
  <TagBuilders>
    <TagBuilder Name="Script"
      Type="SmartSql.ScriptTag.ScriptBuilder, SmartSql.ScriptTag"/>
  </TagBuilders>
</SmartSqlMapConfig>

Or via Options pattern:

json
{
  "TagBuilders": [
    {
      "Name": "Script",
      "Type": "SmartSql.ScriptTag.ScriptBuilder, SmartSql.ScriptTag"
    }
  ]
}

Jint Engine Configuration ​

The Script tag creates Jint engines with these settings:

SettingValuePurpose
StrictfalseAllow non-strict JavaScript
DebugModefalseNo debugging support
AllowDebuggerStatementfalsePrevent debugger statements

When context.Parameters is null, the expression is evaluated without any variables set.

Cross-References ​

References ​

Released under the MIT License.