diff --git a/Samples/Dynamics365FODemo/.gitignore b/Samples/Dynamics365FODemo/.gitignore
new file mode 100644
index 0000000..c194bb3
--- /dev/null
+++ b/Samples/Dynamics365FODemo/.gitignore
@@ -0,0 +1,15 @@
+**/.vs
+**/obj
+**/bin/*
+**/Resources
+**/XppMetadata
+**/Reports
+**/references
+
+BuildModelResult.log
+BuildModelResult.xml
+BuildModelResult.err.xml
+BuildProjectResult.log
+BuildProjectResult.xml
+BuildProjectResult.err.xml
+CompileLabels.xml
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/DeployablePackage/AXDeployablePackage_20231124_17_06_15.zip b/Samples/Dynamics365FODemo/DeployablePackage/AXDeployablePackage_20231124_17_06_15.zip
new file mode 100644
index 0000000..b3dddb7
Binary files /dev/null and b/Samples/Dynamics365FODemo/DeployablePackage/AXDeployablePackage_20231124_17_06_15.zip differ
diff --git a/Samples/Dynamics365FODemo/Dynamics365FODemo.sln b/Samples/Dynamics365FODemo/Dynamics365FODemo.sln
new file mode 100644
index 0000000..c359b69
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Dynamics365FODemo.sln
@@ -0,0 +1,36 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.34031.81
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PowerFxDemoLibrary", "PowerFxDemoLibrary\PowerFxDemoLibrary.csproj", "{7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7DEDF1AD-DD13-4127-8EEC-12B9E30A7B9D}"
+ ProjectSection(SolutionItems) = preProject
+ .gitignore = .gitignore
+ EndProjectSection
+EndProject
+Project("{FC65038C-1B2F-41E1-A629-BED71D161FFF}") = "PowerFxDemo D365FO (VAR) [PowerFx D365FO Demo]", "PowerFxDemo D365FO\PowerFxDemo D365FO.rnrproj", "{3877D19E-04BE-4209-BFB9-979C95B574BC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3877D19E-04BE-4209-BFB9-979C95B574BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3877D19E-04BE-4209-BFB9-979C95B574BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3877D19E-04BE-4209-BFB9-979C95B574BC}.Release|Any CPU.ActiveCfg = Debug|Any CPU
+ {3877D19E-04BE-4209-BFB9-979C95B574BC}.Release|Any CPU.Build.0 = Debug|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {914DB8F6-2C93-420B-9F34-90AF1B21D442}
+ EndGlobalSection
+EndGlobal
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/Descriptor/PowerFxDemo.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/Descriptor/PowerFxDemo.xml
new file mode 100644
index 0000000..6a5e9b2
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/Descriptor/PowerFxDemo.xml
@@ -0,0 +1,34 @@
+
+
+
+ Allow
+ PowerFx D365FO Demo
+ PowerFx D365FO Demo
+ 896000516
+
+ 10
+ false
+ PowerFxDemo
+
+
+ ApplicationFoundation
+ ApplicationPlatform
+ ApplicationSuite
+ ContactPerson
+ Directory
+ FiscalBooks
+ Ledger
+ Retail
+ SourceDocumentation
+ SourceDocumentationTypes
+ Tax
+
+ PowerFxDemo
+ Dominik Downarowicz
+ 00000000-0000-0000-0000-000000000000
+ 0
+ 1
+ 0
+ 0
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo.xref b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo.xref
new file mode 100644
index 0000000..6bcec00
Binary files /dev/null and b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo.xref differ
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDeliveryAddressContract.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDeliveryAddressContract.xml
new file mode 100644
index 0000000..8849e22
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDeliveryAddressContract.xml
@@ -0,0 +1,96 @@
+
+
+ PowerFxDeliveryAddressContract
+
+
+
+
+ parmName
+
+
+
+ parmStreet
+
+
+
+ parmCity
+
+
+
+ parmZipCode
+
+
+
+ parmCountryCode
+
+
+
+ getInstance
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDemoRunnable.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDemoRunnable.xml
new file mode 100644
index 0000000..5b37e3e
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxDemoRunnable.xml
@@ -0,0 +1,107 @@
+
+
+ PowerFxDemoRunnable
+
+
+
+
+ main
+
+
+
+ run
+
+
+
+ modifiedSalesOrderSelection
+
+
+
+ modifiedFormula
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesHeaderContract.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesHeaderContract.xml
new file mode 100644
index 0000000..4d31575
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesHeaderContract.xml
@@ -0,0 +1,288 @@
+
+
+ PowerFxSalesHeaderContract
+
+
+
+
+ parmSalesNumber
+
+
+
+ parmCustName
+
+
+
+ parmCustAccount
+
+
+
+ parmCustGroup
+
+
+
+ parmVATNum
+
+
+
+ parmMarkupGroup
+
+
+
+ parmDlvMode
+
+
+
+ parmPaymMode
+
+
+
+ parmSalesGroup
+
+
+
+ parmSalesType
+
+
+
+ parmDeliveryDate
+
+
+
+ parmAmountInvoiced
+
+
+
+ parmAmountOrderedNotInvoiced
+
+
+
+ parmAmountRemainSalesFinancial
+
+
+
+ parmAmountRemainSalesPhyscial
+
+
+
+ parmContributionMargin
+
+
+
+ parmDeliveryAddress
+
+
+
+ parmSalesOrderLines
+
+
+
+ getInstance
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesLineContract.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesLineContract.xml
new file mode 100644
index 0000000..a4d2342
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxClass/PowerFxSalesLineContract.xml
@@ -0,0 +1,183 @@
+
+
+ PowerFxSalesLineContract
+
+
+
+
+ parmItemName
+
+
+
+ parmItemId
+
+
+
+ parmLineNum
+
+
+
+ parmLineAmount
+
+
+
+ parmSalesPrice
+
+
+
+ parmDiscPercent
+
+
+
+ parmQtyOrdered
+
+
+
+ parmSalesQty
+
+
+
+ parmUnitId
+
+
+
+ parmCurrencyCode
+
+
+
+ parmDeliveryDate
+
+
+
+ getInstance
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.resources.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.resources.xml
new file mode 100644
index 0000000..9062e53
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.resources.xml
@@ -0,0 +1,8 @@
+
+
+ Microsoft.PowerFx.Core.resources
+ Microsoft.PowerFx.Core.resources
+ Microsoft.PowerFx.Core.resources, Version=1.1.0.0, Culture=en-US, PublicKeyToken=31bf3856ad364e35
+ 31bf3856ad364e35
+ 1.1.0.0
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.xml
new file mode 100644
index 0000000..bb7e872
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Core.xml
@@ -0,0 +1,8 @@
+
+
+ Microsoft.PowerFx.Core
+ Microsoft.PowerFx.Core
+ Microsoft.PowerFx.Core, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
+ 31bf3856ad364e35
+ 1.1.0.0
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Interpreter.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Interpreter.xml
new file mode 100644
index 0000000..c35a40f
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Interpreter.xml
@@ -0,0 +1,8 @@
+
+
+ Microsoft.PowerFx.Interpreter
+ Microsoft.PowerFx.Interpreter
+ Microsoft.PowerFx.Interpreter, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
+ 31bf3856ad364e35
+ 1.1.0.0
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Json.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Json.xml
new file mode 100644
index 0000000..86c3a83
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Json.xml
@@ -0,0 +1,8 @@
+
+
+ Microsoft.PowerFx.Json
+ Microsoft.PowerFx.Json
+ Microsoft.PowerFx.Json, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
+ 31bf3856ad364e35
+ 1.1.0.0
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Repl.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Repl.xml
new file mode 100644
index 0000000..13649fc
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Repl.xml
@@ -0,0 +1,8 @@
+
+
+ Microsoft.PowerFx.Repl
+ Microsoft.PowerFx.Repl
+ Microsoft.PowerFx.Repl, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
+ 31bf3856ad364e35
+ 1.1.0.0
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Transport.Attributes.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Transport.Attributes.xml
new file mode 100644
index 0000000..04026bf
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/Microsoft.PowerFx.Transport.Attributes.xml
@@ -0,0 +1,8 @@
+
+
+ Microsoft.PowerFx.Transport.Attributes
+ Microsoft.PowerFx.Transport.Attributes
+ Microsoft.PowerFx.Transport.Attributes, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
+ 31bf3856ad364e35
+ 1.1.0.0
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/PowerFxDemoLibrary.xml b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/PowerFxDemoLibrary.xml
new file mode 100644
index 0000000..da83ddd
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/PowerFxDemo/PowerFxDemo/AxReference/PowerFxDemoLibrary.xml
@@ -0,0 +1,7 @@
+
+
+ PowerFxDemoLibrary
+ PowerFxDemoLibrary
+ PowerFxDemoLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+ 1.0.0.0
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/Metadata/Readme.txt.txt b/Samples/Dynamics365FODemo/Metadata/Readme.txt.txt
new file mode 100644
index 0000000..cf22995
--- /dev/null
+++ b/Samples/Dynamics365FODemo/Metadata/Readme.txt.txt
@@ -0,0 +1,29 @@
+Add all AX modules under the Metadata folder.
+
+Example tree structure:
+$/Project/Trunk/Main/Metadata
++---MyMainModule
+| +---Descriptor
+| | MyFirstModel.xml
+| | MyOtherModel.xml
+| |
+| +---MyFirstModel
+| | +---AxClass
+| | | MyClass.xml
+| | |
+| | \---AxTable
+| | MyMainTable.xml
+| |
+| \---MyOtherModel
+| \---AxForm
+| MyOtherForm.xml
+|
+\---MyOtherModule
+ +---Descriptor
+ | MyTestModel.xml
+ |
+ \---MyTestModel
+ \---AxClass
+ MyTestClass.xml
+
+Please see https://ax.help.dynamics.com for more details.
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/PowerFxDemo D365FO/PowerFxDemo D365FO.rnrproj b/Samples/Dynamics365FODemo/PowerFxDemo D365FO/PowerFxDemo D365FO.rnrproj
new file mode 100644
index 0000000..5287629
--- /dev/null
+++ b/Samples/Dynamics365FODemo/PowerFxDemo D365FO/PowerFxDemo D365FO.rnrproj
@@ -0,0 +1,89 @@
+
+
+
+ Debug
+ AnyCPU
+ $(MSBuildProgramFiles32)\MSBuild\Microsoft\Dynamics\AX
+ PowerFxDemo
+ v4.7.2
+ bin
+ 2.0
+ True
+ False
+ False
+ False
+ {3877d19e-04be-4209-bfb9-979c95b574bc}
+ PowerFxDemo D365FO
+ PowerFxDemo D365FO
+ Class
+
+
+ Debug
+ False
+ False
+
+
+ initial
+ AnyCPU
+ False
+ False
+
+
+ true
+ false
+
+
+
+ Content
+ PowerFxDeliveryAddressContract
+ PowerFxDeliveryAddressContract
+
+
+ Content
+ PowerFxDemoRunnable
+ PowerFxDemoRunnable
+
+
+ Content
+ PowerFxSalesHeaderContract
+ PowerFxSalesHeaderContract
+
+
+ Content
+ PowerFxSalesLineContract
+ PowerFxSalesLineContract
+
+
+
+
+ Microsoft.PowerFx.Core
+ C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Core.dll
+
+
+ Microsoft.PowerFx.Core.resources
+ C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Core.resources.dll
+
+
+ Microsoft.PowerFx.Interpreter
+ C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Interpreter.dll
+
+
+ Microsoft.PowerFx.Json
+ C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Json.dll
+
+
+ Microsoft.PowerFx.Repl
+ C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Repl.dll
+
+
+ Microsoft.PowerFx.Transport.Attributes
+ C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\Microsoft.PowerFx.Transport.Attributes.dll
+
+
+ PowerFxDemoLibrary
+ C:\Git\power-fx-host-samples\Samples\Dynamics365FODemo\references\PowerFxDemoLibrary.dll
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Evaluation.cs b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Evaluation.cs
new file mode 100644
index 0000000..8dd0330
--- /dev/null
+++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Evaluation.cs
@@ -0,0 +1,119 @@
+using Microsoft.PowerFx;
+using Microsoft.PowerFx.Syntax;
+using Microsoft.PowerFx.Types;
+using System;
+using System.Collections.Generic;
+
+namespace PowerFxDemoLibrary
+{
+ public class Evaluation
+ {
+ public class Response
+ {
+ public string output { get; set; }
+ public string error { get; set; }
+ public bool errorThrown { get; set; }
+ }
+
+ private class ThrowErrorFunction : ReflectionFunction
+ {
+ private string errorMessage = "";
+
+ public ThrowErrorFunction(): base("ThrowError", FormulaType.Blank, new[] { FormulaType.String }) { }
+
+ public BlankValue Execute(StringValue x)
+ {
+ addError(x.Value);
+ return FormulaValue.NewBlank();
+ }
+
+ public void addError(string error)
+ {
+ if (error != "")
+ {
+ errorMessage += errorMessage + "\n" + error;
+ }
+ else
+ {
+ errorMessage += errorMessage + "\n";
+ }
+ }
+
+ public string getErrorMessage()
+ {
+ return errorMessage.TrimStart('\n').TrimEnd('\n');
+ }
+
+ public bool isFailed()
+ {
+ return errorMessage != "" ? true : false;
+ }
+
+ }
+
+ private class ThrowErrorFunctionEmtpy : ReflectionFunction
+ {
+ public ThrowErrorFunction function;
+ public ThrowErrorFunctionEmtpy(ThrowErrorFunction _function) : base("ThrowError", FormulaType.Blank)
+ {
+ function = _function;
+ }
+
+ public BlankValue Execute()
+ {
+ function.addError("");
+ return FormulaValue.NewBlank();
+ }
+ }
+
+ public Response EvaluateInput(string context, string expression)
+ {
+ Response response = new Response();
+ IReadOnlyList tokens = null;
+ CheckResult check = null;
+
+ ThrowErrorFunction errorFunction = new ThrowErrorFunction();
+ ThrowErrorFunctionEmtpy errorFunctionEmpty = new ThrowErrorFunctionEmtpy(errorFunction);
+
+ response.output = "";
+ response.error = "";
+ response.errorThrown = false;
+ try
+ {
+ var config = new PowerFxConfig();
+ config.AddFunction(errorFunction);
+ config.AddFunction(errorFunctionEmpty);
+ var engine = new RecalcEngine(config);
+
+ var parameters = (RecordValue)FormulaValueJSON.FromJson(context);
+
+ if (parameters == null)
+ {
+ parameters = RecordValue.Empty();
+ }
+
+ tokens = engine.Tokenize(expression);
+ check = engine.Check(expression, parameters.Type, options: null);
+ check.ThrowOnErrors();
+ var eval = check.GetEvaluator();
+ var result = eval.Eval(parameters);
+
+ response.output = HelperClass.TestToString(result);
+ }
+ catch(Exception ex)
+ {
+ errorFunction.addError(ex.Message);
+ }
+ finally
+ {
+ response.errorThrown = errorFunction.isFailed();
+ response.error = errorFunction.getErrorMessage();
+ }
+
+ return response;
+
+ }
+
+ }
+
+}
diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/HelperClass.cs b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/HelperClass.cs
new file mode 100644
index 0000000..5b8ff1d
--- /dev/null
+++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/HelperClass.cs
@@ -0,0 +1,149 @@
+using Microsoft.PowerFx.Types;
+using System;
+using System.Linq;
+using System.Text;
+
+namespace PowerFxDemoLibrary
+{
+ // Helper to print results.
+ // This should get shared with Repl or from framework.
+ public class HelperClass
+ {
+ internal static string TestToString(FormulaValue result)
+ {
+ StringBuilder sb = new StringBuilder();
+ try
+ {
+ TestToString(result, sb);
+ }
+ catch (Exception e)
+ {
+ // This will cause a diff and test failure below.
+ sb.Append($"");
+ }
+
+ string actualStr = sb.ToString();
+ return actualStr;
+ }
+
+ // $$$ Move onto FormulaValue.
+ // Result here should be a string value that could be parsed.
+ // Normalize so we can use this in test cases.
+ internal static void TestToString(FormulaValue result, StringBuilder sb)
+ {
+ if (result is DecimalValue dec)
+ {
+ sb.Append(dec.Value);
+ }
+ else if (result is NumberValue n)
+ {
+ sb.Append(n.Value);
+ }
+ else if (result is BooleanValue b)
+ {
+ sb.Append(b.Value ? "true" : "false");
+ }
+ else if (result is StringValue s)
+ {
+ // $$$ proper escaping?
+ sb.Append('"' + s.Value + '"');
+ }
+ else if (result is TableValue t)
+ {
+ var tableType = (TableType)t.Type;
+ var canUseSquareBracketSyntax = t.IsColumn && t.Rows.All(r => r.IsValue) && tableType.FieldNames.First() == "Value";
+ if (canUseSquareBracketSyntax)
+ {
+ sb.Append('[');
+ }
+ else
+ {
+ sb.Append("Table(");
+ }
+
+ var dil = string.Empty;
+ foreach (var row in t.Rows)
+ {
+ sb.Append(dil);
+ dil = ",";
+
+ if (canUseSquareBracketSyntax)
+ {
+ var val = row.Value.Fields.First().Value;
+ TestToString(val, sb);
+ }
+ else
+ {
+ if (row.IsValue)
+ {
+ TestToString(row.Value, sb);
+ }
+ else
+ {
+ TestToString(row.ToFormulaValue(), sb);
+ }
+ }
+
+ dil = ",";
+ }
+
+ if (canUseSquareBracketSyntax)
+ {
+ sb.Append(']');
+ }
+ else
+ {
+ sb.Append(')');
+ }
+ }
+ else if (result is RecordValue r)
+ {
+ var fields = r.Fields.ToArray();
+ Array.Sort(fields, (a, c) => string.CompareOrdinal(a.Name, c.Name));
+
+ sb.Append('{');
+ var dil = string.Empty;
+
+ foreach (var field in fields)
+ {
+ sb.Append(dil);
+ sb.Append(field.Name);
+ sb.Append(':');
+ TestToString(field.Value, sb);
+
+ dil = ",";
+ }
+
+ sb.Append('}');
+ }
+ else if (result is TimeValue tv)
+ {
+ sb.Append(tv.Value.ToString());
+ }
+ else if (result is BlankValue)
+ {
+ sb.Append("Blank()");
+ }
+ else if (result is DateValue d)
+ {
+ // Date(YYYY,MM,DD)
+ var date = d.GetConvertedValue(null);
+ sb.Append($"Date({date.Year},{date.Month},{date.Day})");
+ }
+ else if (result is DateTimeValue dt)
+ {
+ // DateTime(yyyy,MM,dd,HH,mm,ss,fff)
+ var dateTime = dt.GetConvertedValue(null);
+ sb.Append($"DateTime({dateTime.Year},{dateTime.Month},{dateTime.Day},{dateTime.Hour},{dateTime.Minute},{dateTime.Second},{dateTime.Millisecond})");
+ }
+ else if (result is ErrorValue)
+ {
+ sb.Append(result);
+ }
+ else
+ {
+ throw new InvalidOperationException($"unsupported value type: {result.GetType().Name}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/PowerFxDemoLibrary.csproj b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/PowerFxDemoLibrary.csproj
new file mode 100644
index 0000000..e4f9fd9
--- /dev/null
+++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/PowerFxDemoLibrary.csproj
@@ -0,0 +1,115 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {7BACE988-ECCE-4A1A-AF7F-2A400BBF9601}
+ Library
+ Properties
+ PowerFxDemoLibrary
+ PowerFxDemoLibrary
+ v4.7.2
+ 512
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll
+
+
+ ..\packages\Microsoft.PowerFx.Core.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Core.dll
+
+
+ ..\packages\Microsoft.PowerFx.Interpreter.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Interpreter.dll
+
+
+ ..\packages\Microsoft.PowerFx.Json.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Json.dll
+
+
+ ..\packages\Microsoft.PowerFx.Repl.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Repl.dll
+
+
+ ..\packages\Microsoft.PowerFx.Transport.Attributes.1.1.0\lib\netstandard2.0\Microsoft.PowerFx.Transport.Attributes.dll
+
+
+
+ ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
+
+
+ ..\packages\System.Collections.Immutable.6.0.0\lib\net461\System.Collections.Immutable.dll
+
+
+
+ ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll
+
+
+
+ ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Text.Encodings.Web.6.0.0\lib\net461\System.Text.Encodings.Web.dll
+
+
+ ..\packages\System.Text.Json.6.0.8\lib\net461\System.Text.Json.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
+
+
+ ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Properties/AssemblyInfo.cs b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5fbe1a1
--- /dev/null
+++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("PowerFx D365 Demo")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Dominik Downarowicz")]
+[assembly: AssemblyProduct("PowerFxDemoLibrary")]
+[assembly: AssemblyCopyright("Copyright © 2023")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("7bace988-ecce-4a1a-af7f-2a400bbf9601")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/app.config b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/app.config
new file mode 100644
index 0000000..0453880
--- /dev/null
+++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/app.config
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/PowerFxDemoLibrary/packages.config b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/packages.config
new file mode 100644
index 0000000..c6fea9b
--- /dev/null
+++ b/Samples/Dynamics365FODemo/PowerFxDemoLibrary/packages.config
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/README.md b/Samples/Dynamics365FODemo/README.md
new file mode 100644
index 0000000..1ec2016
--- /dev/null
+++ b/Samples/Dynamics365FODemo/README.md
@@ -0,0 +1,15 @@
+# Introduction
+Demonstrates using PowerFx in a Dynamics 365 Finance and Operations (D365F&O) environment (Platform Version PU61). PowerFx is used here to evaluate SalesOrder data.
+
+![PowerFx Demo in D365 F&O](media/d365demo.gif)
+
+Additionally the custom function **ThrowError("...")** has been added to PowerFx. There is no concret use for that in the demo, I use the function to pass errors back to D365F&O. Outside the context of that demo, I implemented the **ThrowError** function for a validation framework where the customer can define by himeself with PowerFx the validation rules and throw errors without the need of a D365F&O developer.
+
+# Deploy and demo usage
+In the folder **DeployablePackage** is a ready to deploy package for D365F&O works. Can be deployed like any other package for D365F&O. To run the demo script you then only need to call the **SysClassRunner** in the URL.
+
+**Example: https:///?cmp=USMF&mi=SysClassRunner&cls=PowerFxDemoRunnable**
+
+# Build
+If you want to build the project by yourself on a cloud-hosted environment or local VHD image, do not forget to use the registerSymbolicLink.ps1 to link the Metadata folder to the AosService/PackagesLocalDirectory of the machine. You can also build the project on a new [Unified Experience environment](https://learn.microsoft.com/en-us/power-platform/developer/unified-experience/finance-operations-dev-overview). (2023-11-24: Unified experience environments are in preview and some features might not work).
+
diff --git a/Samples/Dynamics365FODemo/copyReferences.ps1 b/Samples/Dynamics365FODemo/copyReferences.ps1
new file mode 100644
index 0000000..9c311e5
--- /dev/null
+++ b/Samples/Dynamics365FODemo/copyReferences.ps1
@@ -0,0 +1,5 @@
+$buildPath = ".\PowerFxDemoLibrary\bin\Debug"
+$dest = ".\references"
+
+copy-item -path $buildPath\* -destination $dest -include *.dll
+copy-item -path $buildPath\en-US\* -destination $dest -include *.dll
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/media/d365demo.gif b/Samples/Dynamics365FODemo/media/d365demo.gif
new file mode 100644
index 0000000..7d3f6b9
Binary files /dev/null and b/Samples/Dynamics365FODemo/media/d365demo.gif differ
diff --git a/Samples/Dynamics365FODemo/registerSymbolicLink.ps1 b/Samples/Dynamics365FODemo/registerSymbolicLink.ps1
new file mode 100644
index 0000000..ee7c2ae
--- /dev/null
+++ b/Samples/Dynamics365FODemo/registerSymbolicLink.ps1
@@ -0,0 +1,15 @@
+$AOSMetadataPath = "K:\AOSService\PackagesLocalDirectory"
+
+If(!(Test-Path -Path $AOSMetadataPath))
+{
+ $AOSMetadataPath = "C:\AOSService\PackagesLocalDirectory"
+}
+
+$RepoPath = "."
+$RepoMetadataPath = $RepoPath + "\Metadata"
+$RepoModelFolders = Get-ChildItem $RepoMetadataPath -Directory
+foreach ($ModelFolder in $RepoModelFolders)
+{
+ $Target = "$RepoMetadataPath\$ModelFolder"
+ New-Item -ItemType SymbolicLink -Path "$AOSMetadataPath" -Name "$ModelFolder" -Value "$Target"
+}
\ No newline at end of file
diff --git a/Samples/Dynamics365FODemo/unregisterSymbolicLink.ps1 b/Samples/Dynamics365FODemo/unregisterSymbolicLink.ps1
new file mode 100644
index 0000000..7ee62f1
--- /dev/null
+++ b/Samples/Dynamics365FODemo/unregisterSymbolicLink.ps1
@@ -0,0 +1,14 @@
+$AOSMetadataPath = "K:\AOSService\PackagesLocalDirectory"
+
+If(!(Test-Path -Path $AOSMetadataPath))
+{
+ $AOSMetadataPath = "C:\AOSService\PackagesLocalDirectory"
+}
+
+$RepoPath = "."
+$RepoMetadataPath = $RepoPath + "\Metadata"
+$RepoModelFolders = Get-ChildItem $RepoMetadataPath -Directory
+foreach ($ModelFolder in $RepoModelFolders)
+{
+ cmd /c rmdir "$AOSMetadataPath\$ModelFolder"
+}
\ No newline at end of file