ASTTemplateParser 2.0.0
See the version list below for details.
dotnet add package ASTTemplateParser --version 2.0.0
NuGet\Install-Package ASTTemplateParser -Version 2.0.0
<PackageReference Include="ASTTemplateParser" Version="2.0.0" />
<PackageVersion Include="ASTTemplateParser" Version="2.0.0" />
<PackageReference Include="ASTTemplateParser" />
paket add ASTTemplateParser --version 2.0.0
#r "nuget: ASTTemplateParser, 2.0.0"
#:package ASTTemplateParser@2.0.0
#addin nuget:?package=ASTTemplateParser&version=2.0.0
#tool nuget:?package=ASTTemplateParser&version=2.0.0
AST Template Parser
A high-performance, security-hardened template parser for .NET with HTML-like syntax. Works on .NET Standard 2.0, .NET Framework 4.8, .NET 6.0, and .NET 8.0.
✨ Features
- 🚀 High Performance - 90,000+ renders/second with compiled property accessors
- 🔒 Enterprise Security - Built-in XSS, injection, and DoS protection
- 🧩 Component System - Reusable components with dynamic parameters
- 📐 Layout System - Master page layouts with sections
- 📝 HTML-like Syntax - No special directives needed, pure HTML tags
- 🔄 Auto Cache - Component files automatically reload when modified
- 🎯 Pre-Render Extraction - Extract Include names before rendering for cache lookup
- 📁 Folder Components - Support
block/projects/default.htmlstructure - 🛡️ Param Defaults - Fallback values when variables are null
- 🔔 OnBeforeIncludeRender - Event callback before each component renders
- 🎁 OnAfterIncludeRender - Event callback to wrap/modify rendered output
- 🏷️ Type-Specific Tags -
<Element>,<Block>,<Data>,<Nav>with auto path prefix - 📄 Pages Directory - Separate directory for page templates with
RenderPage() - 🔤 Case-Sensitive Tags - Template tags are PascalCase, HTML tags preserved
- 🔁 Template Fragments -
<Define>and<Render>for inline recursion - ✅ Smart If Conditions - Collections, objects, and nulls evaluated correctly
- 🧱 BlockParser - Extract block info from page templates for database storage
- ⚡ Callback SetVariable Priority - Variables set in callback override Param values (NEW in v1.0.20!)
- 🌐 Global Variables - Set static variables once for all engine instances (NEW in v1.0.35!)
- 🌍 Cross-Platform - Works on Windows, Linux, and macOS
📦 Installation
NuGet Package Manager:
Install-Package ASTTemplateParser
.NET CLI:
dotnet add package ASTTemplateParser
🚀 Quick Start (5 Minutes)
Step 1: Create the Engine
using ASTTemplateParser;
var engine = new TemplateEngine();
Step 2: Set Your Data
// Simple variables
engine.SetVariable("UserName", "John");
engine.SetVariable("IsLoggedIn", true);
// Objects with properties
engine.SetVariable("User", new {
Name = "John Doe",
Email = "john@example.com",
IsAdmin = false
});
// Lists for loops
engine.SetVariable("Products", new List<object> {
new { Name = "Laptop", Price = 999.99 },
new { Name = "Mouse", Price = 29.99 }
});
Step 3: Write Your Template
<Element template="page">
<h1>Welcome, {{UserName}}!</h1>
<If condition="IsLoggedIn">
<p>Hello {{User.Name}}, your email is {{User.Email}}</p>
<Else>
<p>Please log in to continue.</p>
</If>
<h2>Our Products</h2>
<ul>
<ForEach var="product" in="Products">
<li>{{product.Name}} - ${{product.Price}}</li>
</ForEach>
</ul>
</Element>
Step 4: Render!
string html = engine.Render(template);
Console.WriteLine(html);
Step 5: Global Variables (Optional)
// Set once at startup
TemplateEngine.SetGlobalVariable("SiteName", "My Website");
// Now available in ALL engine instances automatically
var engine2 = new TemplateEngine();
engine2.Render("Welcome to {{SiteName}}");
📖 Template Syntax Guide
1. Variables (Interpolation)
Use {{variableName}} to display values:
<p>Hello, {{UserName}}!</p>
<p>Email: {{User.Email}}</p>
<p>City: {{Order.Customer.Address.City}}</p>
1.5 Global Variables (NEW in v1.0.35!)
Global variables are static and shared across ALL TemplateEngine instances. Perfect for site-wide settings like copyright year, site name, or API URLs.
// Set once (e.g., in Program.cs)
TemplateEngine.SetGlobalVariable("SiteName", "My Website");
TemplateEngine.SetGlobalVariable("CurrentYear", DateTime.Now.Year);
// Available in any engine instance without calling SetVariable() again
var engine = new TemplateEngine();
string html = engine.Render("© {{CurrentYear}} {{SiteName}}");
Priority Logic:
- Additional variables in
Render()call (Highest) - Instance variables via
engine.SetVariable() - Global variables via
TemplateEngine.SetGlobalVariable()(Lowest)
2. Conditionals (If/Else)
Show or hide content based on conditions:
<If condition="IsLoggedIn">
<p>Welcome back!</p>
</If>
<If condition="IsAdmin">
<button>Delete</button>
<Else>
<button disabled>Delete (Admin only)</button>
</If>
<If condition="Role == 'admin'">
<span>👑 Admin</span>
<ElseIf condition="Role == 'moderator'">
<span>🛡️ Moderator</span>
<Else>
<span>👤 User</span>
</If>
Supported Operators:
| Operator | Example | Description |
|---|---|---|
== |
Status == 'active' |
Equal to |
!= |
Count != 0 |
Not equal to |
> |
Age > 18 |
Greater than |
< |
Stock < 10 |
Less than |
>= |
Score >= 50 |
Greater or equal |
<= |
Price <= 100 |
Less or equal |
&& or and |
A && B |
Both must be true |
|| or or |
A || B |
At least one true |
3. Loops (ForEach)
Repeat content for each item in a collection:
<ul>
<ForEach var="item" in="Items">
<li>{{item}}</li>
</ForEach>
</ul>
<table>
<ForEach var="product" in="Products">
<tr>
<td>{{product.Name}}</td>
<td>${{product.Price}}</td>
<If condition="product.IsOnSale">
<td class="sale">SALE!</td>
</If>
</tr>
</ForEach>
</table>
<ForEach var="category" in="Categories">
<h3>{{category.Name}}</h3>
<ForEach var="item" in="category.Items">
<p>{{item.Name}}</p>
</ForEach>
</ForEach>
4. Components (Reusable Templates)
Create reusable UI pieces:
Create a component file (components/element/button.html):
<a href="{{href}}" class="btn btn-{{type}}">
{{text}}
</a>
Use the component with static values:
<Include component="element/button">
<Param name="text" value="Click Me" />
<Param name="type" value="primary" />
<Param name="href" value="/action" />
</Include>
Use with dynamic values:
<Include component="element/button">
<Param name="text" value="{{ButtonText}}" />
<Param name="type" value="{{ButtonType}}" />
<Param name="href" value="/user/{{User.Id}}" />
</Include>
<Include component="element/button">
<Param name="text" value="ButtonText" />
</Include>
<Include component="element/button">
<Param name="text" value="Hello {{UserName}}!" />
</Include>
Param with Default Values (NEW in v1.0.5!):
<Include component="element/button">
<Param name="text" value="{{ButtonText}}" default="Click Me" />
<Param name="type" value="{{ButtonType}}" default="primary" />
</Include>
<Include component="element/header">
<Param name="title" value="{{PageTitle}}" default="Welcome, {{UserName}}!" />
</Include>
Folder-Based Components (NEW in v1.0.5!):
components/
├── element/
│ └── button.html ← component="element/button"
└── block/
└── projects/ ← component="block/projects" (folder)
└── default.html ← Automatically loaded
<Include component="block/projects">
<Param name="title" value="My Projects" />
</Include>
Setup components directory in C#:
engine.SetComponentsDirectory("./components");
engine.SetVariable("ButtonText", "Submit");
engine.SetVariable("UserName", "Alice");
5. Pre-Render Data Extraction (NEW in v1.0.4!)
Use name attribute on Include tags to identify components for cache lookup before rendering:
<Include component="element/header" name="header_abc123_cached">
<Param name="class" value="{{element.Class}}" />
<Param name="text" value="{{element.Content}}" />
</Include>
Extract Include names before rendering:
var template = @"<Include component=""element/header"" name=""header_abc123_cached"">
<Param name=""text"" value=""{{data.Title}}"" />
</Include>";
// Option 1: Simple extraction (AST is cached for later Render())
var includeInfos = TemplateEngine.ExtractIncludeNames(template);
foreach (var info in includeInfos)
{
// info.Name = "header_abc123_cached" (your cache key!)
// info.ComponentPath = "element/header"
// info.Parameters = {"text": "{{data.Title}}"}
var cachedData = YourCacheService.Get(info.Name);
engine.SetVariable("data", cachedData);
}
// Render uses cached AST - NO performance penalty!
var html = engine.Render(template);
Most efficient approach with PrepareTemplate:
var engine = new TemplateEngine();
// Step 1: Prepare once - parse, validate, cache, extract names
var prepared = engine.PrepareTemplate(template);
// Step 2: Use extracted names to fetch cached data
foreach (var info in prepared.IncludeInfos)
{
var cachedElement = YourCache.Get(info.Name);
engine.SetVariable("element", cachedElement);
}
// Step 3: Render directly from prepared AST (FASTEST!)
var html = engine.RenderPrepared(prepared);
6. OnBeforeIncludeRender Event (NEW in v1.0.6!)
Get a callback before each Include component renders - perfect for dynamic data loading:
var engine = new TemplateEngine();
engine.SetComponentsDirectory("./components");
// Set callback - fires BEFORE each Include renders
engine.OnBeforeIncludeRender((info, eng) =>
{
Console.WriteLine($"Rendering: {info.Name}");
// Fetch data from cache using Include name as key
var cachedData = YourCacheService.Get(info.Name);
eng.SetVariable("element", cachedData);
});
// Render template - callback fires for each Include
var html = engine.Render(template);
| IncludeInfo Property | Description |
|---|---|
Name |
Include name attribute (use as cache key) |
ComponentPath |
Component path like block/slider |
ComponentType |
Type: "element", "block", "data", "navigation", or "include" |
Parameters |
Dictionary of Param names and values |
SetVariable Priority in Callback (NEW in v1.0.20!)
Variables set via SetVariable() in callback take priority over Param values. This enables dynamic attribute injection:
engine.OnBeforeIncludeRender((info, eng) =>
{
if (info.ComponentType == "element")
{
// Get existing attributes from Param
var existingAttrs = info.Parameters.TryGetValue("attributes", out var val) ? val : "";
// Inject additional data attributes
var identity = GetElementIdentity(info.Name);
var dataAttrs = $"data-identity='{identity}' data-editable='true'";
// SetVariable takes priority over Param!
eng.SetVariable("attributes", $"{existingAttrs} {dataAttrs}");
}
});
Template (components/element/subTitle.html):
<h2 class="edit-able {{class}}" {{attributes}}>{{content}}</h2>
Usage:
<Element component="subTitle" name="my_subtitle">
<Param name="class" value="highlight" />
<Param name="content" value="Welcome!" />
<Param name="attributes" value="id='main-title'" />
</Element>
Output (with callback injection):
<h2 class="edit-able highlight" id='main-title' data-identity='abc123' data-editable='true'>Welcome!</h2>
7. OnAfterIncludeRender Event (NEW in v1.0.7!)
Get a callback after each component renders - perfect for wrapping output:
engine.OnAfterIncludeRender((info, renderedHtml) =>
{
// info.ComponentType tells you the type
// Wrap based on component type
if (info.ComponentType == "block")
{
return $"<section id=\"{info.Name}\">{renderedHtml}</section>";
}
return renderedHtml;
});
8. Type-Specific Component Tags (NEW in v1.0.7!)
Instead of <Include component="element/button">, use type-specific tags with auto path prefix:
| Tag | Path Prefix | Example |
|---|---|---|
<Element> |
element/ |
<Element component="button"> → loads element/button.html |
<Block> |
block/ |
<Block component="slider"> → loads block/slider.html |
<Data> |
data/ |
<Data component="schema"> → loads data/schema.html |
<Nav> |
navigation/ |
<Nav component="menu"> → loads navigation/menu.html |
<Include component="element/subTitle" name="my_subtitle">
<Param name="content" value="Years Experience" />
</Include>
<Element component="subTitle" name="my_subtitle">
<Param name="content" value="Years Experience" />
</Element>
⚠️ Important (v1.0.10+): Template tags MUST be PascalCase (
<Section>,<Data>,<Element>).
Lowercase HTML tags like<section>,<div>,<nav>are preserved as-is.
9. Container Tags
| Tag | Purpose | Example |
|---|---|---|
<Element> |
Main content wrapper | <Element template="page">...</Element> |
<Data> |
Data section | <Data section="meta">...</Data> |
<Nav> |
Navigation section | <Nav section="main">...</Nav> |
<Block> |
Named block | <Block name="footer">...</Block> |
10. Pages Directory & RenderPage (NEW in v1.0.8!)
Separate your page templates from components with a dedicated pages directory:
var engine = new TemplateEngine();
// Setup both directories
engine.SetComponentsDirectory("./components"); // For reusable components
engine.SetPagesDirectory("./pages"); // For page templates
// Set your data
engine.SetVariable("PageTitle", "Home");
engine.SetVariable("UserName", "John");
// Render a page template
string html = engine.RenderPage("home"); // → pages/home.html
// Or with RenderFile using flag
string html2 = engine.RenderFile("about", isPage: true); // → pages/about.html
string html3 = engine.RenderFile("element/button"); // → components/element/button.html
Directory Structure:
your-project/
├── components/ ← SetComponentsDirectory()
│ ├── element/
│ │ └── button.html
│ └── block/
│ └── footer.html
│
├── pages/ ← SetPagesDirectory()
│ ├── home.html
│ ├── about.html
│ └── contact.html
│
└── Program.cs
Page Template Example (pages/home.html):
<Element template="home">
<h1>{{PageTitle}}</h1>
<Element component="header" name="main_header">
<Param name="title" value="{{PageTitle}}" />
</Element>
<Block component="slider" name="hero">
<Param name="slides" value="{{Slides}}" />
</Block>
</Element>
11. Template Fragments: Define & Render (NEW in v1.0.11!)
Create reusable template fragments with inline recursion - perfect for multi-level navigation menus!
<Define name="menuItem">
<li>
<a href="{{menuNode.Url}}">{{menuNode.Title}}</a>
<If condition="menuNode.Children">
<ul>
<ForEach var="child" in="menuNode.Children">
<Render name="menuItem" menuNode="child" />
</ForEach>
</ul>
</If>
</li>
</Define>
<nav class="main-nav">
<ul>
<ForEach var="item" in="menuItems">
<Render name="menuItem" menuNode="item" />
</ForEach>
</ul>
</nav>
C# Setup:
var menuItems = new List<object>
{
new {
Title = "Home",
Url = "/",
Children = new List<object>
{
new { Title = "About", Url = "/about", Children = (object)null },
new { Title = "Services", Url = "/services", Children = (object)null }
}
}
};
engine.SetVariable("menuItems", menuItems);
Benefits:
- ✅ Single file recursion - no separate component files needed
- ✅ Supports unlimited depth (up to MaxRecursionDepth, default: 20)
- ✅ Parameters passed via attributes
- ✅ Variable interpolation works in parameters
12. Smart If Condition Truthiness (NEW in v1.0.11!)
If conditions now properly evaluate all types:
| Value | Result | Example |
|---|---|---|
null |
false |
<If condition="item.Children"> when Children is null |
Empty string "" |
false |
<If condition="name"> when name is "" |
Empty collection [] |
false |
<If condition="list"> when list has 0 items |
| Non-empty collection | true |
<If condition="Children"> when Children has items |
| Any non-null object | true |
<If condition="user"> when user exists |
| Boolean | As-is | <If condition="isActive"> |
<ul>
<ForEach var="item" in="menuItems">
<li>
{{item.Title}}
<If condition="item.Children">
<ul>
<ForEach var="child" in="item.Children">
<li>{{child.Title}}</li>
</ForEach>
</ul>
<Else>
<span>(No sub-items)</span>
</If>
</li>
</ForEach>
</ul>
🧱 BlockParser: Page Template Block Extraction (NEW in v1.0.12!)
Parse Block-based page templates and extract block information for database storage. Perfect for CMS systems where you want to:
- Store block configuration in database
- Enable/disable blocks dynamically
- Reorder blocks without editing template files
Block Template Format
<Block component="slider" name="slider">
<Param name="iscustomstyle" value="true"/>
<Param name="flag" value="slider"/>
</Block>
<Block component="about" name="about">
<Param name="sectionClass" value="about-area grey-bg2 pt-100 pb-100"/>
<Param name="iscustomstyle" value="true"/>
</Block>
<Block component="team" name="team">
<Param name="sectionClass" value="team-area pt-125 pb-100"/>
<Param name="style" value="background-image: url(assets/img/bg/01.jpg);"/>
</Block>
Parse and Save to Database
// Setup engine with pages directory
var engine = new TemplateEngine();
engine.SetPagesDirectory(@"D:\MyProject\Pages");
engine.SetComponentsDirectory(@"D:\MyProject\Components");
// Create BlockParser with engine
var blockParser = new BlockParser(engine);
// Option 1: Simple - just page name (uses PagesDirectory)
var blocks = blockParser.ParseBlocks("home-blocks"); // → pages/home-blocks.html
// Option 2: Full path (for custom locations)
// var blocks = blockParser.ParseBlocks(@"D:\Custom\home.html", "home");
// 2. Save to YOUR database (use your own EF/database code)
foreach (var block in blocks)
{
_dbContext.PageBlocks.Add(new PageBlockEntity
{
Id = block.Id,
Name = block.Name, // "slider"
ComponentPath = block.ComponentPath, // "slider"
Order = block.Order, // 0, 1, 2...
PageName = block.PageName, // "home"
ParametersJson = JsonSerializer.Serialize(block.Parameters)
});
}
_dbContext.SaveChanges();
Load from Database and Render
// 3. Load blocks from database
var blocksFromDb = _dbContext.PageBlocks
.Where(b => b.PageName == "home")
.OrderBy(b => b.Order)
.ToList();
// 4. Render each block's component from file
var engine = new TemplateEngine();
engine.SetComponentsDirectory(@"D:\Components");
var html = new StringBuilder();
foreach (var block in blocksFromDb)
{
// ComponentPath renders from file (e.g., slider.html)
html.Append(engine.RenderFile(block.ComponentPath));
}
return html.ToString();
BlockInfo Properties
| Property | Type | Description |
|---|---|---|
Id |
string |
Unique GUID |
Name |
string |
Block name (e.g., "slider") |
ComponentPath |
string |
Component file path for RenderFile() |
Order |
int |
Display order (0, 1, 2...) |
PageName |
string |
Page identifier |
Parameters |
Dictionary<string, string> |
Block parameters (save as JSON) |
Flow Diagram
┌─────────────────────────────────────────────────────────────────┐
│ FIRST RUN │
├─────────────────────────────────────────────────────────────────┤
│ Template File ──► ParseBlocks() ──► List<BlockInfo> │
│ │ │
│ ▼ │
│ Save to Database │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ SUBSEQUENT RUNS │
├─────────────────────────────────────────────────────────────────┤
│ Database ──► Load Blocks ──► RenderFile(ComponentPath) │
│ │ │
│ ▼ │
│ HTML Output │
└─────────────────────────────────────────────────────────────────┘
🔒 Security Features
The parser includes built-in protection against common attacks:
Default Protection (Automatic)
- ✅ XSS Prevention - All output is HTML-encoded
- ✅ Loop Limits - Maximum 1000 iterations (prevents DoS)
- ✅ Recursion Limits - Maximum 10 component depth
- ✅ Path Validation - Prevents directory traversal attacks
Custom Security Configuration
var security = new SecurityConfig
{
MaxLoopIterations = 500, // Limit loop iterations
MaxRecursionDepth = 5, // Limit component nesting
MaxPropertyDepth = 10, // Limit property chain depth
HtmlEncodeOutput = true, // Encode output (XSS protection)
AllowMethodCalls = false, // Block method invocation
BlockedPropertyNames = new HashSet<string>
{
"Password", "Secret", "Token", "ConnectionString"
}
};
var engine = new TemplateEngine(security);
📊 Performance
| Template Size | Operations/Second | Memory |
|---|---|---|
| Small (1KB) | ~7,000 ops/sec | Low |
| Medium (4KB) | ~1,600 ops/sec | Medium |
| Large (8KB) | ~800 ops/sec | Higher |
💡 Complete Example
using ASTTemplateParser;
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// 1. Create engine
var engine = new TemplateEngine();
// 2. Set components directory (optional)
engine.SetComponentsDirectory("./components");
// 3. Set your data
engine.SetVariable("PageTitle", "My Store");
engine.SetVariable("User", new { Name = "Alice", IsVIP = true });
engine.SetVariable("Products", new List<object>
{
new { Name = "Laptop", Price = 999, InStock = true },
new { Name = "Tablet", Price = 499, InStock = false },
new { Name = "Phone", Price = 699, InStock = true }
});
// 4. Define template
var template = @"
<Element template=""store"">
<h1>{{PageTitle}}</h1>
<If condition=""User.IsVIP"">
<div class=""vip-badge"">⭐ VIP Member: {{User.Name}}</div>
</If>
<div class=""products"">
<ForEach var=""p"" in=""Products"">
<div class=""product"">
<h3>{{p.Name}}</h3>
<p class=""price"">${{p.Price}}</p>
<If condition=""p.InStock"">
<button>Add to Cart</button>
<Else>
<span class=""out-of-stock"">Out of Stock</span>
</If>
</div>
</ForEach>
</div>
</Element>";
// 5. Render and output
string html = engine.Render(template);
Console.WriteLine(html);
}
}
🎯 Target Platforms
| Platform | Version | Status |
|---|---|---|
| .NET Standard | 2.0 | ✅ Supported |
| .NET Framework | 4.8 | ✅ Supported |
| .NET | 6.0 | ✅ Supported |
| .NET | 8.0 | ✅ Supported |
📄 License
MIT License - Free for personal and commercial use.
📚 Documentation
For complete documentation including Layout System, Advanced Components, and Slots, see DOCUMENTATION.md.
❓ FAQ
Q: How is this different from Razor?
A: This uses pure HTML-like syntax (<If>, <ForEach>) instead of @ directives. It's faster and has no runtime compilation.
Q: Can I use this with ASP.NET Core? A: Yes! It works with any .NET platform that supports .NET Standard 2.0 or higher.
Q: Is it safe for user-generated templates?
A: With proper SecurityConfig, yes. Enable strict mode for user templates.
Version 1.0.19 - January 2026
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 is compatible. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.8
- NCalc.NetCore (>= 1.0.1)
-
.NETStandard 2.0
- NCalc.NetCore (>= 1.0.1)
-
net6.0
- NCalc.NetCore (>= 1.0.1)
-
net8.0
- NCalc.NetCore (>= 1.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.3 | 70 | 1/21/2026 |
| 2.0.2 | 72 | 1/20/2026 |
| 2.0.0 | 81 | 1/20/2026 |
| 1.0.45 | 93 | 1/19/2026 |
| 1.0.43 | 83 | 1/19/2026 |
| 1.0.42 | 78 | 1/19/2026 |
| 1.0.41 | 80 | 1/19/2026 |
| 1.0.40 | 80 | 1/19/2026 |
| 1.0.39 | 92 | 1/17/2026 |
| 1.0.38 | 76 | 1/15/2026 |
| 1.0.37 | 86 | 1/15/2026 |
| 1.0.36 | 88 | 1/15/2026 |
| 1.0.35 | 82 | 1/15/2026 |
| 1.0.33 | 81 | 1/14/2026 |
| 1.0.32 | 76 | 1/14/2026 |
| 1.0.31 | 88 | 1/14/2026 |
| 1.0.30 | 88 | 1/14/2026 |
| 1.0.29 | 100 | 1/14/2026 |
| 1.0.28 | 86 | 1/14/2026 |
| 1.0.27 | 91 | 1/14/2026 |
| 1.0.26 | 80 | 1/13/2026 |
| 1.0.25 | 84 | 1/13/2026 |
| 1.0.24 | 78 | 1/13/2026 |
| 1.0.23 | 76 | 1/13/2026 |
| 1.0.22 | 79 | 1/13/2026 |
| 1.0.21 | 83 | 1/13/2026 |
| 1.0.20 | 83 | 1/13/2026 |
| 1.0.19 | 81 | 1/12/2026 |
| 1.0.18 | 84 | 1/12/2026 |
| 1.0.17 | 86 | 1/12/2026 |
| 1.0.16 | 78 | 1/12/2026 |
| 1.0.15 | 76 | 1/12/2026 |
| 1.0.14 | 84 | 1/12/2026 |
| 1.0.13 | 76 | 1/12/2026 |
| 1.0.12 | 84 | 1/12/2026 |
| 1.0.11 | 83 | 1/11/2026 |
| 1.0.10 | 78 | 1/11/2026 |
| 1.0.9 | 85 | 1/10/2026 |
| 1.0.8 | 84 | 1/10/2026 |
| 1.0.7 | 85 | 1/10/2026 |
| 1.0.6 | 85 | 1/8/2026 |
| 1.0.5 | 85 | 1/8/2026 |
| 1.0.4 | 82 | 1/8/2026 |
| 1.0.3 | 83 | 1/7/2026 |
| 1.0.2 | 84 | 1/7/2026 |
| 1.0.1 | 86 | 1/7/2026 |
| 1.0.0 | 83 | 1/7/2026 |
v2.0.0 - (January 20, 2026)
- ADDED: High-performance Indexer Support (item[key]).
- ADDED: Compiled delegate caching for indexers for maximum speed.
- ADDED: Support for dynamic key resolution in templates.
- IMPROVED: Expression evaluation robustness.
- SECURITY: Enhanced indexer validation with BlockedPropertyNames.