diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/concurrency/semaphores/what-is-a-semaphore.md b/content/en/docs/2026.3/Reference/Concepts/working-with/concurrency/semaphores/what-is-a-semaphore.md index 872e904ab..b6e80adff 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/concurrency/semaphores/what-is-a-semaphore.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/concurrency/semaphores/what-is-a-semaphore.md @@ -1,33 +1,93 @@ --- title: "What is a Semaphore?" linkTitle: "What is a Semaphore?" -description: "Information regarding what a semaphore is." +description: "Information regarding what a semaphore is, how it limits concurrent flow executions, and how to configure queuing behaviour." weight: 1 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -A semaphore is a mechanism that limits the number of concurrent executions that can be executing a block or workspace, and can be enabled using the [semaphore property][SemaphoreProperty]. +A **semaphore** is a mechanism that limits how many [executions][] can run a [block][] (or [workspace][]) at the same time. It is the primary concurrency control in {{% ctx %}} flows. + +Semaphores are conceptually similar to [SemaphoreSlim][] in C# (`System.Threading.SemaphoreSlim`), which limits how many threads can access a resource concurrently. In {{% ctx %}}, semaphores are platform-managed: you configure them on blocks using the [semaphore property][SemaphoreProperty], and {{% ctx %}} acquires and releases them automatically around block execution. Semaphore state is shared within the [scope][Scope] you define and is stored in reliable collections on the platform. + +| Term | Meaning | +| --- | --- | +| Acquire | An execution enters the semaphore and consumes one concurrency slot | +| Release | An execution leaves the semaphore and frees a concurrency slot | +| [ConcurrencyLimit][] | Maximum number of executions that can be inside the semaphore at once | +| [Queue][] | Optional settings that control whether executions wait in a queue or fail immediately | + +Enable a semaphore by setting the [semaphore property][SemaphoreProperty] on a block. The property is an [advanced property][AdvancedProperties] available on most blocks and is represented by the [SemaphoreSettings][] data type. A value of `null` means no semaphore is applied. -The [semaphore property][SemaphoreProperty] is an [advanced property][AdvancedProperties] added to most blocks to allow any part of a flow to be concurrency limited. The [semaphore property][SemaphoreProperty] has a [Scope][] that defines the area in which the semaphore will operate, a [Name][] to identify the semaphore, a [ConcurrencyLimit][] that defines the maximum number of concurrent executions that can enter the semaphore at once and a [Queue][] property that defines whether the execution should [queue][queuing semaphore] or [not queue][non-queuing semaphore] for the semaphore. +The [semaphore property][SemaphoreProperty] has a [Scope][] that defines the area in which the semaphore operates, a [Name][] to identify the semaphore, a [ConcurrencyLimit][] that defines the maximum number of concurrent executions that can enter the semaphore at once, and a [Queue][] property that defines whether the execution should [queue][Queuing Semaphore] or [not queue][Non-Queuing Semaphore] for the semaphore. + +For how to set the property in the block editor, see [Semaphore Property][] in [Common Properties][]. ## Anatomy of a Semaphore -TODO +A semaphore is identified by its [Scope][] and [Name][], and configured using [SemaphoreSettings][]. + +### Configuration + +{{< figure src="/images/workspace/workspace-block-semaphore.svg" >}} + +| Property | Data type | Purpose | +| --- | --- | --- | +| [Scope][] | [ScopeDefinition][ScopeDatatype] | Defines which tenant, system, package, and flow share the same semaphore instance | +| [Name][] | [String][] | Identifies the semaphore within the scope (for example `"ApiRateLimit"`) | +| [ConcurrencyLimit][] | [Int32][] | Maximum number of executions allowed inside the semaphore at one time | +| [Queue][] | [QueueSettings][] or `null` | When `null`, the semaphore is [non-queuing][Non-Queuing Semaphore]; otherwise it is [queuing][Queuing Semaphore] with [Priority][] and optional [QueueTimeout][] | + +When several blocks use the same [Scope][] and [Name][], they share one semaphore. Blocks that use different names or scopes use separate semaphores, even within the same flow. + +In expressions, create settings with the [SemaphoreSettings][] constructor—for example: + +```csharp +new SemaphoreSettings( + new ScopeDefinition(ScopeOption.Current, ScopeOption.Current, ScopeOption.Current, ScopeOption.All), + "SemaphoreA", + 1, + null) +``` + +See [SemaphoreSettings][] for examples of creating settings in the Expression Editor and Literal Editor. + +### Semaphore identity + +Each semaphore instance is addressed by a path built from its scope and name. When acquisition fails, [SemaphoreCouldNotBeAcquiredException][] messages use this format: + +```json +"///*//*//" +``` + +where ``, ``, ``, and `` come from the [Scope][] definition, and `` is the [Name][] value. + +### Lifecycle + +For each block that has a non-`null` [semaphore property][SemaphoreProperty]: + +1. **Acquire** — Before the block starts, the execution attempts to acquire the semaphore. If the [ConcurrencyLimit][] has not been reached, the execution enters immediately. Otherwise behaviour depends on the [Queue][] setting (see below). +2. **Hold** — While the block runs, the execution holds one concurrency slot. Nested blocks with their own semaphores may cause an execution to hold multiple semaphores at once. +3. **Release** — When the block finishes, is stopped, or throws an unhandled exception, the execution releases the semaphore. If it was the last execution inside the semaphore, the semaphore is removed automatically. + +An execution that queues for a semaphore waits before the block starts; it does not consume a concurrency slot until it has been dequeued and has entered the semaphore. ### Non-Queuing Semaphore A [Non-Queuing Semaphore][] refers to a semaphore where executions do not queue when attempting to acquire a semaphore that has reached its concurrency limit. Instead, it throws a [SemaphoreCouldNotBeAcquiredException][ConcurrencyLimitReached] with the [Queued][] property set to `false` to indicate that it did not queue. +Set [Queue][] to `null` to use a non-queuing semaphore. + ### Queuing Semaphore A [Queuing Semaphore][] refers to a semaphore where executions queue when attempting to acquire a semaphore that has reached its concurrency limit. It will join the queue with the [Priority][] specified in the [Queue][] property and wait to enter the semaphore. When it has reached the front of the queue and there is space inside, it will be removed from the queue and enter the semaphore. The queue orders items by priority, preserving first-in-first-out behaviour between items with the same priority. The item with the lowest priority will be dequeued first. -If an execution spends more time in the queue than the [QueueTimeout][] specified in the [Queue][] property, it will exit the queue and throw a [SemaphoreCouldNotBeAcquiredException][QueueTimeoutReached] with the [Queued][] property set to `true` to indicate that it attempted to queue. +Provide a [QueueSettings][] value (for example `new QueueSettings(0, null)`) to enable queuing. + +If an execution spends more time in the queue than the [QueueTimeout][] specified in the [Queue][] property, it will exit the queue and throw a [SemaphoreCouldNotBeAcquiredException][QueueTimeoutReached] with the [Queued][] property set to `true` to indicate that it attempted to queue. A `null` [QueueTimeout][] allows an execution to queue indefinitely. ## Remarks @@ -59,9 +119,9 @@ There exists two executions: In this example, Execution A can't proceed as it's waiting for the semaphore that Execution B is using; which in turn is waiting for the semaphore that Execution A is using. -The best way to avoid these deadlocks is to design your flows to avoid them happening in the first place. In the example above, if both executions will need both semaphores to operate, a change that could be made would be to combine both semaphores into one. +The best way to avoid these deadlocks is to design your flows to avoid them happening in the first place. In the example above, if both executions will need both semaphores to operate, a change that could be made would be to combine both semaphores into one. Alternatively, ensure every flow acquires the semaphores in the same order. -Another way to mitigate the problem would be to use the [QueueTimeout][] property when defining the [QueueSettings][] for each semaphore. Setting the [QueueTimeout][] to a duration reasonably higher than the expected execution time of the operations inside of the semaphore would allow the execution to throw if it has been waiting too long, likely due to a deadlock having occurred. The [SemaphoreCouldNotBeAcquiredException][QueueTimeoutReached] thrown could then be caught to allow the execution to release its semaphores and try again. In the example above, if Semaphore A had a [QueueTimeout][], Execution B timeouts out as it waits to acquire Semaphore A and releases Semaphore B. Execution A then acquires Semaphore B. Execution B retries to acquire Semaphore B and joins the queue. Execution A then finishes its operations, releasing both semaphores and allows Execution B to complete its operations as well. +Another way to mitigate the problem would be to use the [QueueTimeout][] property when defining the [QueueSettings][] for each semaphore. Setting the [QueueTimeout][] to a duration reasonably higher than the expected execution time of the operations inside of the semaphore would allow the execution to throw if it has been waiting too long, likely due to a deadlock having occurred. The [SemaphoreCouldNotBeAcquiredException][QueueTimeoutReached] thrown could then be caught to allow the execution to release its semaphores and try again. In the example above, if Semaphore A had a [QueueTimeout][], Execution B times out as it waits to acquire Semaphore A and releases Semaphore B. Execution A then acquires Semaphore B. Execution B retries to acquire Semaphore B and joins the queue. Execution A then finishes its operations, releasing both semaphores and allows Execution B to complete its operations as well. ### Known Limitations @@ -71,7 +131,10 @@ None ### Related Concepts -None +- [Common Properties][] +- [Executions][executions] +- [Handling Exceptions][] +- [Scopes][] ### Related Data Types @@ -79,6 +142,10 @@ None - [ScopeDefinition][ScopeDatatype] - [SemaphoreSettings][] +### Related Exceptions + +- [SemaphoreCouldNotBeAcquiredException][] + ### Related Blocks - [All Blocks][] @@ -93,17 +160,20 @@ Except: ### External Documentation -None +- [SemaphoreSlim Class (System.Threading)][SemaphoreSlim] +- [Semaphore Class (System.Threading)][Semaphore] [Queuing Semaphore]: {{< ref "#queuing-semaphore" >}} [Non-Queuing Semaphore]: {{< ref "#non-queuing-semaphore" >}} - [ConcurrencyLimitReached]: {{< url path="Cortex.Reference.Exceptions.Concurrency.Semaphores.SemaphoreCouldNotBeAcquiredException.ConcurrencyLimitReached" >}} [QueueTimeoutReached]: {{< url path="Cortex.Reference.Exceptions.Concurrency.Semaphores.SemaphoreCouldNotBeAcquiredException.QueueTimeoutReached" >}} [Queued]: {{< url path="Cortex.Reference.Exceptions.Concurrency.Semaphores.SemaphoreCouldNotBeAcquiredException.Queued" >}} +[SemaphoreCouldNotBeAcquiredException]: {{< url path="Cortex.Reference.Exceptions.Concurrency.Semaphores.SemaphoreCouldNotBeAcquiredException.MainDoc" >}} [SemaphoreProperty]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.CommonProperties.SemaphoreProperty" >}} +[Semaphore Property]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.CommonProperties.SemaphoreProperty" >}} [AdvancedProperties]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.AdvancedProperties.MainDoc" >}} +[Common Properties]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.CommonProperties.MainDoc" >}} [SemaphoreSettings]: {{< url path="Cortex.Reference.DataTypes.Concurrency.Semaphores.SemaphoreSettings.MainDoc" >}} [QueueSettings]: {{< url path="Cortex.Reference.DataTypes.Concurrency.Semaphores.QueueSettings.MainDoc" >}} @@ -116,6 +186,15 @@ None [Queue]: {{< url path="Cortex.Reference.DataTypes.Concurrency.Semaphores.SemaphoreSettings.Queue" >}} [ScopeDatatype]: {{< url path = "Cortex.Reference.DataTypes.Scopes.ScopeDefinition.MainDoc">}} +[Scopes]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Scopes.MainDoc" >}} + +[executions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Executions.WhatIsAnExecution.MainDoc" >}} +[block]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.WhatIsABlock.MainDoc" >}} +[workspace]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Workspaces.WhatIsAWorkspace.MainDoc" >}} +[Handling Exceptions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Exceptions.HandlingExceptions.MainDoc" >}} + +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} [All Blocks]: {{< url path="Cortex.Reference.Blocks.MainDoc" >}} [End Flow]: {{< url path="Cortex.Reference.Blocks.Flows.EndFlow.EndFlow.MainDoc" >}} @@ -123,3 +202,6 @@ None [Handle Flow Exception]: {{< url path="Cortex.Reference.Blocks.Exceptions.HandleFlow.HandleFlowException.MainDoc" >}} [Start Flow]: {{< url path="Cortex.Reference.Blocks.Flows.StartFlow.StartFlow.MainDoc" >}} [Wait For Duration]: {{< url path="Cortex.Reference.Blocks.Schedules.WaitFor.WaitForDuration.MainDoc" >}} + +[SemaphoreSlim]: https://learn.microsoft.com/en-us/dotnet/api/system.threading.semaphoreslim +[Semaphore]: https://learn.microsoft.com/en-us/dotnet/api/system.threading.semaphore diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/odbc.md b/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/odbc.md index e22bafadb..978b6f76a 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/odbc.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/odbc.md @@ -1,40 +1,176 @@ --- title: "ODBC" linkTitle: "ODBC" -description: "Information regarding ODBC as a data source." +description: "Information regarding ODBC as a data source, including connection strings, common backends, and known limitations." --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +In {{% ctx %}}, **ODBC** is a data source type for connecting to databases and file-based systems through an ODBC driver on the execution environment. Flows use [OdbcConnectionDetails][] with [Execute Data Command][] to open a connection and run [DataCommand][] statements against the backend. + +{{% ctx %}} implements ODBC through ADO.NET's [OdbcConnection][] class (`System.Data.Odbc`). The connection is configured with a single [connection string][] on [OdbcConnectionDetails][]; the driver and DSN in that string determine which backend is reached. [CommandException][] messages for ODBC connections use the category `ODBC`. + +Use ODBC when the target system does not have a dedicated [ConnectionDetails][] type in {{% ctx %}}. [SQL Server][] and [Oracle][] have native connection types ([SqlServerConnectionDetails][] and [OracleConnectionDetails][]); prefer those when connecting to SQL Server or Oracle directly. Use [OdbcConnectionDetails][] for other backends—for example Microsoft Access, Excel, PostgreSQL, or MySQL—when a suitable ODBC driver is installed where the flow runs. + +For how data sources work in general (commands, connection reuse, and closing connections), see [What is a Data Source?][]. + +## Connection configuration + +### OdbcConnectionDetails + +[OdbcConnectionDetails][] (`Cortex.DataTypes.Data.OdbcConnectionDetails`) holds the ODBC [connection string][]. Create it in the [Expression Editor][] or assign a [variable][] that already holds an [OdbcConnectionDetails][] instance: + +| Method | Example | Result | +| --- | --- | --- | +| Constructor | `new OdbcConnectionDetails("DSN=LocalHost;Driver={ODBC Driver 17 for SQL Server};")` | `{"ConnectionString": "DSN=LocalHost;Driver={ODBC Driver 17 for SQL Server};"}` | + +The [Connection String][] property is [EncryptableText][] and is the only property on [OdbcConnectionDetails][]. + +Pass the resulting object to [Execute Data Command][] on [Connection Details][]. Connection opening, reuse, and closing follow the same rules as other data source types; see [Opening Connections][] and [Closing Connections][] on [What is a Data Source?][]. + +### Connection strings + +ODBC connection strings are driver-specific. They typically identify a **DSN** (data source name), a **Driver** (registered ODBC driver name), or both, plus backend-specific options such as database name, file path, or credentials. + +| Pattern | Example fragment | Notes | +| --- | --- | --- | +| DSN | `DSN=MyDataSource;` | Uses a system or user DSN configured on the execution machine | +| Driver | `Driver={PostgreSQL Unicode};Server=localhost;Database=mydb;` | Driver name must match the installed ODBC driver exactly (often wrapped in `{ }`) | +| File path | `Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=C:\Data\Example.accdb;` | Common for Access and Excel backends | + +See [ConnectionStrings.com][] for connection string formats per driver and backend. If a connection fails during open, check [CommandException][] error code [1000][Connection Failed] and the [InnerException][]; an invalid string may also surface as [InvalidConnectionStringException][]. -- Summary -- Table or other format to display verified data sources (e.g. postgres, mysql, access, excel, oracle) and examples (link to connection strings) +## Common ODBC backends + +Which backends are available depends entirely on the ODBC drivers installed on the machine or container where the flow executes. The table below lists backends commonly reached through ODBC in {{% ctx %}} flows. Verify the driver name and string format on your environment before deploying to production. + +| Backend | Prefer native type? | Example connection string (illustrative) | Further reading | +| --- | --- | --- | --- | +| Microsoft Access | No | `Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=C:\Data\Example.accdb;` | [ConnectionStrings.com][] — Access | +| Microsoft Excel | No | `Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=C:\Data\Example.xlsx;` | [ConnectionStrings.com][] — Excel | +| PostgreSQL | No | `Driver={PostgreSQL Unicode};Server=localhost;Port=5432;Database=mydb;Uid=user;Pwd=password;` | [ConnectionStrings.com][] — PostgreSQL ODBC | +| MySQL | No | `Driver={MySQL ODBC 8.0 Unicode Driver};Server=localhost;Database=mydb;User=user;Password=password;` | [ConnectionStrings.com][] — MySQL ODBC | +| SQL Server | Yes — [SqlServerConnectionDetails][] | `Driver={ODBC Driver 17 for SQL Server};Server=localhost;Database=mydb;Trusted_Connection=Yes;` | [SQL Server][] | +| Oracle | Yes — [OracleConnectionDetails][] | `Driver={Oracle in OraClient19Home1};Dbq=//host:1521/service;Uid=user;Pwd=password;` | [Oracle][] | + +Driver names and optional keywords vary by vendor and version. Use the ODBC Data Source Administrator (or equivalent) on the execution environment to confirm the exact driver string. + +## Commands and parameters + +Connect with [OdbcConnectionDetails][] and run commands the same way as for [SQL Server][] or [Oracle][]: supply a [QueryCommand][], [NonQueryCommand][], [Command][], or [Commands][] on [Execute Data Command][]. + +* Use [parameterised commands][Parameterised Commands] with `@ParameterName` placeholders in command text to avoid [SQL Injection][]. +* For [OdbcConnectionDetails][], [CommandException][] error code [2003][Incompatible Parameter Type] applies when parameter types are not compatible; compatible parameter values use [Structure][]. +* [Query Statements][] and [Non Query Statements][] follow the general rules on [Execute Data Command][] (including behaviour for [Array][] and [IEnumerable][] parameters). + +Invalid or unsupported SQL for ODBC is typically reported at **runtime** ([DataCommandErrorCode.Runtime][]) rather than during statement parsing ([DataCommandErrorCode.Statement][]), because the ODBC provider attempts execution instead of failing at parse time. See [DataCommandErrorCode][] for details. ## Remarks -### Known Limitations +### ODBC drivers on the execution environment -TODO +An ODBC connection succeeds only if the correct 32-bit or 64-bit driver is installed for the architecture {{% ctx %}} uses on that machine. DSNs configured on a developer workstation are not automatically available on remote execution targets unless the same DSN or driver-based connection string is configured there. + +### Property editor support + +* The [Expression Editor][] is available for [Input][] properties where the data type is [OdbcConnectionDetails][]. +* The [Literal Editor][] is not available for [Input][] properties where the data type is [OdbcConnectionDetails][]. +* The [Variable Editor][] is available for [Input][], [InputOutput][], and [Output][] properties where the data type is [OdbcConnectionDetails][]. + +### Known limitations + +* **Microsoft Access string parameters** — [String][] values cannot be used as command parameters when connected to a Microsoft Access data source through [OdbcConnectionDetails][]. Use other parameter types or avoid parameterised string filters for Access until this limitation is addressed. +* **Stored procedure output parameters** — When using a [parameterised command][] to execute a stored procedure, output parameters cannot be written back ([Execute Data Command][]). +* **Limited native providers** — Only the data source types listed under [Supported Data Sources][] are supported natively; ODBC extends reach via installed drivers but does not guarantee every ODBC driver or backend is tested on all platforms. ## See Also ### Related Concepts -TODO +* [What is a Data Source?][] +* [Supported Data Sources][] +* [SQL Server][] +* [Oracle][] ### Related Data Types -TODO +* [ConnectionDetails][] +* [OdbcConnectionDetails][] +* [DataCommand][] +* [QueryCommand][] +* [NonQueryCommand][] +* [Command][] +* [Commands][] +* [DataCommandErrorCode][] ### Related Blocks -TODO +* [Execute Data Command][] + +### Related Exceptions + +* [CommandException][] +* [InvalidConnectionStringException][] ### External Documentation -TODO +* [OdbcConnection Class (System.Data.Odbc)][OdbcConnection] +* [OdbcCommand Class (System.Data.Odbc)][OdbcCommand] +* [DbConnection Class (System.Data.Common)][DbConnection] +* [Connection strings][ConnectionStrings.com] + +[What is a Data Source?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}} +[Supported Data Sources]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.MainDoc" >}} +[SQL Server]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.SqlServer.MainDoc" >}} +[Oracle]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.Oracle.MainDoc" >}} + +[Execute Data Command]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.MainDoc" >}} +[Connection Details]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ConnectionDetailsProperty" >}} +[Parameterised Commands]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ParameterisedCommands" >}} +[parameterised command]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ParameterisedCommands" >}} +[Query Statements]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.QueryStatements" >}} +[Non Query Statements]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.NonQueryStatements" >}} +[Opening Connections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}}#opening-connections +[Closing Connections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}}#closing-connections + +[ConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.ConnectionDetails.MainDoc" >}} +[OdbcConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.OdbcConnectionDetails.MainDoc" >}} +[SqlServerConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.SqlServerConnectionDetails.MainDoc" >}} +[OracleConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.OracleConnectionDetails.MainDoc" >}} +[DataCommand]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommand.MainDoc" >}} +[QueryCommand]: {{< url path="Cortex.Reference.DataTypes.Data.QueryCommand.MainDoc" >}} +[NonQueryCommand]: {{< url path="Cortex.Reference.DataTypes.Data.NonQueryCommand.MainDoc" >}} +[Command]: {{< url path="Cortex.Reference.DataTypes.Data.Command.MainDoc" >}} +[Commands]: {{< url path="Cortex.Reference.DataTypes.Data.Commands.MainDoc" >}} +[DataCommandErrorCode]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}} +[DataCommandErrorCode.Runtime]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}}#3000 +[DataCommandErrorCode.Statement]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}}#2000 +[EncryptableText]: {{< url path="Cortex.Reference.DataTypes.Text.EncryptableText.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[IEnumerable]: {{< url path="Cortex.Reference.DataTypes.Collections.IEnumerable.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} + +[CommandException]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}} +[InvalidConnectionStringException]: {{< url path="Cortex.Reference.Exceptions.Data.InvalidConnectionStringException.MainDoc" >}} +[Connection Failed]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#1000 +[Incompatible Parameter Type]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#2003 +[InnerException]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#innerexception + +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[Literal Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.LiteralEditor.MainDoc" >}} +[Variable Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.VariableEditor.MainDoc" >}} +[Input]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.Input" >}} +[InputOutput]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.InputOutput" >}} +[Output]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.Output" >}} +[variable]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[connection string]: {{< url path="Cortex.Reference.DataTypes.Data.OdbcConnectionDetails.ConnectionString" >}} + +[ConnectionStrings.com]: {{< url path="ConnectionStrings.MainDoc" >}} +[SQL Injection]: {{< url path="W3.SqlInjection" >}} + +[OdbcConnection]: https://learn.microsoft.com/en-us/dotnet/api/system.data.odbc.odbcconnection +[OdbcCommand]: https://learn.microsoft.com/en-us/dotnet/api/system.data.odbc.odbccommand +[DbConnection]: https://learn.microsoft.com/en-us/dotnet/api/system.data.common.dbconnection diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/oracle.md b/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/oracle.md index 3d0f2d832..4320acc78 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/oracle.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/oracle.md @@ -1,40 +1,192 @@ --- title: "Oracle" linkTitle: "Oracle" -description: "Information regarding Oracle as a data source." +description: "Information regarding Oracle as a data source, including connection strings, PL/SQL commands, parameters, and known limitations." --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +In {{% ctx %}}, **Oracle** is a native data source type for connecting directly to Oracle Database. Flows use [OracleConnectionDetails][] with [Execute Data Command][] to open a connection and run [DataCommand][] statements against the database. + +{{% ctx %}} connects to Oracle through ADO.NET using the [OracleConnection][] class (`Oracle.ManagedDataAccess.Client`). The connection is configured with a single [connection string][] on [OracleConnectionDetails][]. Parameter binding for advanced scenarios uses Dapper.Oracle types such as [OracleMappingType][]. [CommandException][] messages for Oracle connections use the category `Oracle`. + +Prefer [OracleConnectionDetails][] over [OdbcConnectionDetails][] when connecting to Oracle directly. Use [ODBC][] only when a native Oracle connection is not suitable for your environment. + +For how data sources work in general (commands, connection reuse, and closing connections), see [What is a Data Source?][]. + +## Connection configuration + +### OracleConnectionDetails + +[OracleConnectionDetails][] (`Cortex.DataTypes.Data.OracleConnectionDetails`) holds the Oracle [connection string][]. Create it in the [Expression Editor][] or assign a [variable][] that already holds an [OracleConnectionDetails][] instance: + +| Method | Example | Result | +| --- | --- | --- | +| Constructor | `new OracleConnectionDetails("Data Source=host:1521/orclpdb;User Id=user;Password=password;")` | `{"ConnectionString": "Data Source=host:1521/orclpdb;User Id=user;Password=password;"}` | + +The [Connection String][] property is [EncryptableText][] and is the only property on [OracleConnectionDetails][]. + +Pass the resulting object to [Execute Data Command][] on [Connection Details][]. Connection opening, reuse, and closing follow the same rules as other data source types; see [Opening Connections][] and [Closing Connections][] on [What is a Data Source?][]. + +### Connection strings + +Oracle connection strings are provider-specific. They typically identify how to reach the database (for example Easy Connect host and service name, or a TNS descriptor) and how to authenticate (user ID and password). + +| Pattern | Example fragment | Notes | +| --- | --- | --- | +| Easy Connect | `Data Source=host:1521/service_name;User Id=user;Password=password;` | Common for direct TCP connections to a service | +| TNS descriptor | `Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=host)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=service)));User Id=user;Password=password;` | Full TNS connect descriptor in the connection string | +| User credentials | `User Id=user;Password=password;` | Combine with a `Data Source=` value that identifies the target database | + +See [ConnectionStrings.com][] for Oracle connection string formats. If a connection fails during open, check [CommandException][] error code [1000][Connection Failed] and the [InnerException][]; an invalid string may also surface as [InvalidConnectionStringException][]. + +The [Integrated Security][] connection string format is **not** supported for [OracleConnectionDetails][]. -- Summary -- Table or other format to display verified data sources (e.g. postgres, mysql, access, excel, oracle) and examples (link to connection strings) +## Commands and parameters + +Connect with [OracleConnectionDetails][] and run commands through [Execute Data Command][] using [QueryCommand][], [NonQueryCommand][], [Command][], or [Commands][] on the [Command][] property. + +* Use [parameterised commands][Parameterised Commands] with `@ParameterName` placeholders in command text to avoid [SQL Injection][]. +* For simple parameters, pass a [Structure][] or anonymous object in the [Parameters][] property (for example `new { Id = 3 }`). +* For advanced Oracle parameters—specifying [OracleMappingType][], [ParameterDirection][], or size (for example CLOB values for stored procedures)—use [OracleParameter][], [OracleParameters][], or `IEnumerable`. +* [Query Statements][] and [Non Query Statements][] follow the general rules on [Execute Data Command][] (including behaviour for [Array][] and [IEnumerable][] parameters). + +### PL/SQL and multiple statements + +Oracle uses PL/SQL. Behaviour differs from SQL Server in several ways: + +| Scenario | Required approach | +| --- | --- | +| Multiple statements in one script (for example cursors or variables with dependencies) | Use [QueryCommand][] or [NonQueryCommand][] with [block statement][] syntax—not [Commands][] | +| [OracleBlockStatement][block statement] in [Command][] or [Commands][] | Not supported; use [QueryCommand][] or [NonQueryCommand][] instead ([error code 2001][Incompatible Statement Type]) | +| Multiple statements in [QueryCommand][] or [NonQueryCommand][] | Wrap statements in a PL/SQL [block statement][] | + +[Command][] and [Commands][] parse statement text before execution. [QueryCommand][] and [NonQueryCommand][] send the command text as a whole, which is required for PL/SQL blocks and other scripts where statements depend on each other. See [Complex Commands][] on [Execute Data Command][]. + +### Statement parsing and errors + +{{% ctx %}} parses Oracle command text before execution. Invalid SQL or PL/SQL may surface as [DataCommandErrorCode.Statement][] during parsing ([error code 2000][Statement]) rather than only at runtime. Syntax errors may include a nested `ParserException` in the [CommandException][] [InnerException][]. + +For incompatible parameter types, [CommandException][] error code [2003][Incompatible Parameter Type] applies when parameter values are not compatible with [OracleConnectionDetails][]. Compatible parameter types are [Structure][], [OracleParameters][], [OracleParameter][], and `IEnumerable`. Mismatched [OracleMappingType][] and parameter values may surface as [error code 3001][Invalid Parameter Binding]. ## Remarks -### Known Limitations +### Native connection vs ODBC + +[OracleConnectionDetails][] is the recommended connection type for Oracle Database. [OdbcConnectionDetails][] can reach Oracle when an ODBC driver is installed, but native connections avoid driver-name and DSN differences and support Oracle-specific parameter types. See [ODBC][] for when ODBC is appropriate. + +### Property editor support + +* The [Expression Editor][] is available for [Input][] properties where the data type is [OracleConnectionDetails][]. +* The [Literal Editor][] is not available for [Input][] properties where the data type is [OracleConnectionDetails][]. +* The [Variable Editor][] is available for [Input][], [InputOutput][], and [Output][] properties where the data type is [OracleConnectionDetails][]. -TODO +### Known limitations + +* **Integrated Security connection strings** — The [Integrated Security][] connection string format is not supported for [OracleConnectionDetails][]. +* **PL/SQL block statements** — [Command][] and [Commands][] cannot execute [OracleBlockStatement][block statement] text; use [QueryCommand][] or [NonQueryCommand][] with [block statement][] syntax instead. +* **Multiple statements** — When running multiple statements with [QueryCommand][] or [NonQueryCommand][], use PL/SQL [block statement][] syntax. +* **Stored procedure output parameters** — When using a [parameterised command][] to execute a stored procedure, output parameters cannot be written back ([Execute Data Command][]). +* **ODBC alternative** — If you connect through [OdbcConnectionDetails][] instead of [OracleConnectionDetails][], Oracle-specific parameter types ([OracleParameter][], [OracleParameters][]) are not available; use [Structure][] parameters only. ## See Also ### Related Concepts -TODO +* [What is a Data Source?][] +* [Supported Data Sources][] +* [ODBC][] +* [SQL Server][] ### Related Data Types -TODO +* [ConnectionDetails][] +* [OracleConnectionDetails][] +* [OracleParameter][] +* [OracleParameters][] +* [OracleMappingType][] +* [DataCommand][] +* [QueryCommand][] +* [NonQueryCommand][] +* [Command][] +* [Commands][] +* [DataCommandErrorCode][] ### Related Blocks -TODO +* [Execute Data Command][] + +### Related Exceptions + +* [CommandException][] +* [InvalidConnectionStringException][] ### External Documentation -TODO +* [OracleConnection Class (Oracle.ManagedDataAccess.Client)][OracleConnection] +* [DbConnection Class (System.Data.Common)][DbConnection] +* [Oracle connection strings][ConnectionStrings.com] +* [PL/SQL block statement][block statement] + +[What is a Data Source?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}} +[Supported Data Sources]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.MainDoc" >}} +[SQL Server]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.SqlServer.MainDoc" >}} +[ODBC]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.Odbc.MainDoc" >}} + +[Execute Data Command]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.MainDoc" >}} +[Connection Details]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ConnectionDetailsProperty" >}} +[Parameterised Commands]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ParameterisedCommands" >}} +[parameterised command]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ParameterisedCommands" >}} +[Query Statements]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.QueryStatements" >}} +[Non Query Statements]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.NonQueryStatements" >}} +[Complex Commands]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ComplexCommands" >}} +[Opening Connections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}}#opening-connections +[Closing Connections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}}#closing-connections + +[ConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.ConnectionDetails.MainDoc" >}} +[OracleConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.OracleConnectionDetails.MainDoc" >}} +[OdbcConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.OdbcConnectionDetails.MainDoc" >}} +[OracleParameter]: {{< url path="Cortex.Reference.DataTypes.Data.OracleParameter.MainDoc" >}} +[OracleParameters]: {{< url path="Cortex.Reference.DataTypes.Data.OracleParameters.MainDoc" >}} +[OracleMappingType]: {{< url path="Cortex.Reference.DataTypes.Data.OracleMappingType.MainDoc" >}} +[DataCommand]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommand.MainDoc" >}} +[QueryCommand]: {{< url path="Cortex.Reference.DataTypes.Data.QueryCommand.MainDoc" >}} +[NonQueryCommand]: {{< url path="Cortex.Reference.DataTypes.Data.NonQueryCommand.MainDoc" >}} +[Command]: {{< url path="Cortex.Reference.DataTypes.Data.Command.MainDoc" >}} +[Commands]: {{< url path="Cortex.Reference.DataTypes.Data.Commands.MainDoc" >}} +[DataCommandErrorCode]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}} +[DataCommandErrorCode.Statement]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}}#2000 +[EncryptableText]: {{< url path="Cortex.Reference.DataTypes.Text.EncryptableText.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[IEnumerable]: {{< url path="Cortex.Reference.DataTypes.Collections.IEnumerable_TItem.MainDoc" >}} +[Parameters]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommand.Parameters" >}} + +[CommandException]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}} +[InvalidConnectionStringException]: {{< url path="Cortex.Reference.Exceptions.Data.InvalidConnectionStringException.MainDoc" >}} +[Connection Failed]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#1000 +[Statement]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#2000 +[Incompatible Statement Type]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#2001 +[Incompatible Parameter Type]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#2003 +[Invalid Parameter Binding]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#3001 +[InnerException]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#innerexception + +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[Literal Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.LiteralEditor.MainDoc" >}} +[Variable Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.VariableEditor.MainDoc" >}} +[Input]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.Input" >}} +[InputOutput]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.InputOutput" >}} +[Output]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.Output" >}} +[variable]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[connection string]: {{< url path="Cortex.Reference.DataTypes.Data.OracleConnectionDetails.ConnectionString" >}} + +[Integrated Security]: {{< url path="ConnectionStrings.OracleConnectionIntegratedSecurity" >}} +[ConnectionStrings.com]: {{< url path="ConnectionStrings.OracleConnection" >}} +[block statement]: {{< url path="Oracle.PL-SQL.BlockStatement" >}} +[SQL Injection]: {{< url path="W3.SqlInjection" >}} +[ParameterDirection]: {{< url path="OracleParameter.ParameterDirection" >}} + +[OracleConnection]: https://learn.microsoft.com/en-us/dotnet/api/oracle.manageddataaccess.client.oracleconnection +[DbConnection]: https://learn.microsoft.com/en-us/dotnet/api/system.data.common.dbconnection diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/sql-server.md b/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/sql-server.md index 2f06517a2..a48825f40 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/sql-server.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/supported-data-sources/sql-server.md @@ -1,45 +1,207 @@ --- title: "SQL Server" linkTitle: "SQL Server" -description: "Information regarding SQL Server as a data source." +description: "Information regarding SQL Server as a data source, including connection strings, T-SQL commands, parameters, and known limitations." --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: +In {{% ctx %}}, **SQL Server** is a native data source type for connecting directly to Microsoft SQL Server and compatible databases (including Azure SQL). Flows use [SqlServerConnectionDetails][] with [Execute Data Command][] to open a connection and run [DataCommand][] statements against the database. + +{{% ctx %}} connects to SQL Server through ADO.NET using the [SqlConnection][] class (`Microsoft.Data.SqlClient`). The connection is configured with a single [connection string][] on [SqlServerConnectionDetails][]. Parameter binding uses [Structure][] values with `@ParameterName` placeholders in command text. [CommandException][] messages for SQL Server connections use the category `SQL`. + +Prefer [SqlServerConnectionDetails][] over [OdbcConnectionDetails][] when connecting to SQL Server directly. Use [ODBC][] only when a native SQL Server connection is not suitable for your environment. + +For how data sources work in general (commands, connection reuse, and closing connections), see [What is a Data Source?][]. + +## Connection configuration + +### SqlServerConnectionDetails + +[SqlServerConnectionDetails][] (`Cortex.DataTypes.Data.SqlServerConnectionDetails`) holds the SQL Server [connection string][]. Create it in the [Expression Editor][] or assign a [variable][] that already holds a [SqlServerConnectionDetails][] instance: + +| Method | Example | Result | +| --- | --- | --- | +| Constructor | `new SqlServerConnectionDetails("Server=localhost;Database=mydb;Trusted_Connection=True;")` | `{"ConnectionString": "Server=localhost;Database=mydb;Trusted_Connection=True;"}` | + +The [Connection String][] property is [EncryptableText][] and is the only property on [SqlServerConnectionDetails][]. + +Pass the resulting object to [Execute Data Command][] on [Connection Details][]. Connection opening, reuse, and closing follow the same rules as other data source types; see [Opening Connections][] and [Closing Connections][] on [What is a Data Source?][]. + +### Connection strings + +SQL Server connection strings are provider-specific. They typically identify the server and database and how to authenticate (Windows integrated security or SQL Server user credentials). + +| Pattern | Example fragment | Notes | +| --- | --- | --- | +| Windows authentication | `Server=localhost;Database=mydb;Trusted_Connection=True;` | Connects as the Windows identity of the flow execution context; see [Authentication][] | +| SQL authentication | `Server=localhost;Database=mydb;User Id=user;Password=password;` | Connects with a SQL Server login | +| Named instance | `Server=host\INSTANCE;Database=mydb;Trusted_Connection=True;` | Use `Server=` with the instance name | +| Azure SQL | `Server=tcp:myserver.database.windows.net,1433;Database=mydb;User Id=user;Password=password;Encrypt=True;` | Follow [Microsoft][] guidance for encryption and firewall rules | + +See [ConnectionStrings.com][] for SQL Server connection string formats. If a connection fails during open, check [CommandException][] error code [1000][Connection Failed] and the [InnerException][]; an invalid string may also surface as [InvalidConnectionStringException][]. + +## Commands and parameters + +Connect with [SqlServerConnectionDetails][] and run commands through [Execute Data Command][] using [QueryCommand][], [NonQueryCommand][], [Command][], or [Commands][] on the [Command][] property. -- Summary -- Table or other format to display verified data sources (SQL Server versions) and examples (link to connection strings) +* Use [parameterised commands][Parameterised Commands] with `@ParameterName` placeholders in command text to avoid [SQL Injection][]. +* For [SqlServerConnectionDetails][], pass parameter values as a [Structure][] or anonymous object in the [Parameters][] property (for example `new { Id = 3 }`). [CommandException][] error code [2003][Incompatible Parameter Type] applies when parameter types are not compatible; compatible parameter values use [Structure][]. +* [Query Statements][] and [Non Query Statements][] follow the general rules on [Execute Data Command][] (including behaviour for [Array][] and [IEnumerable][] parameters). +* Missing or mismatched parameters may surface as a [SqlException][] at runtime; see [SqlException error codes][SqlException Error Codes]. + +Invalid or unsupported T-SQL for [SqlServerConnectionDetails][] is typically reported at **runtime** ([DataCommandErrorCode.Runtime][]) rather than during statement parsing ([DataCommandErrorCode.Statement][]), because the SQL Server provider attempts execution instead of failing at parse time. See [DataCommandErrorCode][] for details. + +### T-SQL and multiple statements + +SQL Server uses T-SQL. Choose the [DataCommand][] type based on how statements relate to each other: + +| Scenario | Recommended approach | +| --- | --- | +| Single statement | [Command][], [QueryCommand][], or [NonQueryCommand][] | +| Multiple independent statements (for example several `SELECT` or `INSERT` commands) | [Commands][] | +| Statements with dependencies (for example variables, temp tables, or cursors in one batch) | [QueryCommand][] or [NonQueryCommand][]—not [Commands][] | +| Multiple statements in [Command][] | Not supported; use [Commands][] instead ([error code 2002][Multiple Statements]) | + +[Command][] and [Commands][] parse statement text before execution. [QueryCommand][] and [NonQueryCommand][] send the command text as a whole, which is required when statements depend on each other. See [Complex Commands][] on [Execute Data Command][]. + +Batch separators such as `GO` (used in SQL Server Management Studio and `sqlcmd`) are **not** part of T-SQL and are not supported in ADO.NET command text. Include only the T-SQL that should run in a single batch. ## Remarks -TODO: +### Authentication {#authentication} + +How SQL Server authenticates the connection depends on the [connection string][] and, for Windows authentication, who runs the flow: + +| Goal | Configuration | +| --- | --- | +| Connect as the service account | Use a [trusted connection][Trusted Connection] string (`Trusted_Connection=True`) without [Run As][]. If the [Global RunAs User][] is configured it will run as this rather than the service account. | +| Connect as a specific Windows user | Use a [trusted connection][Trusted Connection] string and set [Run As][] on [Execute Data Command][] to [UserCredentials][] for that user | +| Connect with a SQL login | Use `User Id=` and `Password=` in the [connection string][] | -- Connected as the user running the service or as a custom user (e.g. trusted user vs username and password) - - check that impersonation works with trusted when implemented +When using [Run As][] with a [trusted connection][Trusted Connection] string: -### Known Limitations +* For a **local** database, set [LogonType][] to `LogonType.Network` on the [UserCredentials][]. +* For a **remote** database, set [LogonType][] to `LogonType.NewCredentials` on the [UserCredentials][]. -TODO +See [Execute Data Command][] for full [Run As][] requirements. + +### Native connection vs ODBC + +[SqlServerConnectionDetails][] is the recommended connection type for SQL Server. [OdbcConnectionDetails][] can reach SQL Server when an ODBC driver (for example ODBC Driver 17 for SQL Server) is installed, but native connections avoid driver-name and DSN differences. See [ODBC][] for when ODBC is appropriate. + +### Property editor support + +* The [Expression Editor][] is available for [Input][] properties where the data type is [SqlServerConnectionDetails][]. +* The [Literal Editor][] is not available for [Input][] properties where the data type is [SqlServerConnectionDetails][]. +* The [Variable Editor][] is available for [Input][], [InputOutput][], and [Output][] properties where the data type is [SqlServerConnectionDetails][]. + +### Known limitations + +* **Stored procedure output parameters** — When using a [parameterised command][] to execute a stored procedure, output parameters cannot be written back ([Execute Data Command][]). +* **Runtime SQL errors** — Invalid T-SQL is typically reported at runtime ([DataCommandErrorCode.Runtime][]) rather than during parsing ([DataCommandErrorCode.Statement][]). +* **Dependent statements in [Commands][]** — [Commands][] runs each parsed statement separately; use [QueryCommand][] or [NonQueryCommand][] when statements depend on each other. +* **`GO` batch separators** — Not supported in command text sent through ADO.NET. +* **ODBC alternative** — If you connect through [OdbcConnectionDetails][] instead of [SqlServerConnectionDetails][], behaviour and driver requirements depend on the installed ODBC driver; see [ODBC][]. ## See Also ### Related Concepts -TODO +* [What is a Data Source?][] +* [Supported Data Sources][] +* [ODBC][] +* [Oracle][] ### Related Data Types -TODO +* [ConnectionDetails][] +* [SqlServerConnectionDetails][] +* [DataCommand][] +* [QueryCommand][] +* [NonQueryCommand][] +* [Command][] +* [Commands][] +* [DataCommandErrorCode][] ### Related Blocks -TODO +* [Execute Data Command][] + +### Related Exceptions + +* [CommandException][] +* [InvalidConnectionStringException][] ### External Documentation -TODO +* [SqlConnection Class (Microsoft.Data.SqlClient)][SqlConnection] +* [DbConnection Class (System.Data.Common)][DbConnection] +* [SqlException Class (Microsoft.Data.SqlClient)][SqlException] +* [SQL Server connection strings][ConnectionStrings.com] +* [SqlException error codes][SqlException Error Codes] + +[Authentication]: {{< ref "#authentication" >}} +[What is a Data Source?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}} +[Supported Data Sources]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.MainDoc" >}} +[Oracle]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.Oracle.MainDoc" >}} +[ODBC]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.Odbc.MainDoc" >}} + +[Execute Data Command]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.MainDoc" >}} +[Connection Details]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ConnectionDetailsProperty" >}} +[Parameterised Commands]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ParameterisedCommands" >}} +[parameterised command]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ParameterisedCommands" >}} +[Query Statements]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.QueryStatements" >}} +[Non Query Statements]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.NonQueryStatements" >}} +[Complex Commands]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ComplexCommands" >}} +[Opening Connections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}}#opening-connections +[Closing Connections]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource.MainDoc" >}}#closing-connections +[Run As]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.CommonProperties.RunAsProperty" >}} +[Global RunAs User]: {{< url path="Cortex.Faqs.ConfigureGlobalRunAs.MainDoc" >}} + +[ConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.ConnectionDetails.MainDoc" >}} +[SqlServerConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.SqlServerConnectionDetails.MainDoc" >}} +[OdbcConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.OdbcConnectionDetails.MainDoc" >}} +[DataCommand]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommand.MainDoc" >}} +[QueryCommand]: {{< url path="Cortex.Reference.DataTypes.Data.QueryCommand.MainDoc" >}} +[NonQueryCommand]: {{< url path="Cortex.Reference.DataTypes.Data.NonQueryCommand.MainDoc" >}} +[Command]: {{< url path="Cortex.Reference.DataTypes.Data.Command.MainDoc" >}} +[Commands]: {{< url path="Cortex.Reference.DataTypes.Data.Commands.MainDoc" >}} +[DataCommandErrorCode]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}} +[DataCommandErrorCode.Runtime]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}}#3000 +[DataCommandErrorCode.Statement]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}}#2000 +[EncryptableText]: {{< url path="Cortex.Reference.DataTypes.Text.EncryptableText.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[Array]: {{< url path="Cortex.Reference.DataTypes.Collections.Array.MainDoc" >}} +[IEnumerable]: {{< url path="Cortex.Reference.DataTypes.Collections.IEnumerable_TItem.MainDoc" >}} +[Parameters]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommand.Parameters" >}} +[UserCredentials]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.MainDoc" >}} +[LogonType]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.LogonType" >}} + +[CommandException]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}} +[InvalidConnectionStringException]: {{< url path="Cortex.Reference.Exceptions.Data.InvalidConnectionStringException.MainDoc" >}} +[Connection Failed]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#1000 +[Incompatible Parameter Type]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#2003 +[Multiple Statements]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#2002 +[InnerException]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}}#innerexception + +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[Literal Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.LiteralEditor.MainDoc" >}} +[Variable Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.VariableEditor.MainDoc" >}} +[Input]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.Input" >}} +[InputOutput]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.InputOutput" >}} +[Output]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.Output" >}} +[variable]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[connection string]: {{< url path="Cortex.Reference.DataTypes.Data.SqlServerConnectionDetails.ConnectionString" >}} +[Trusted Connection]: {{< url path="ConnectionStrings.SqlConnectionTrustedConnection" >}} + +[ConnectionStrings.com]: {{< url path="ConnectionStrings.SqlConnection" >}} +[SQL Injection]: {{< url path="W3.SqlInjection" >}} +[SqlException Error Codes]: {{< url path="MSDocs.SqlServer.ErrorCodes" >}} + +[SqlConnection]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlconnection +[DbConnection]: https://learn.microsoft.com/en-us/dotnet/api/system.data.common.dbconnection +[SqlException]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlexception +[Microsoft]: https://learn.microsoft.com/en-us/sql/connect/ado-net/sql/sqlclient-support-lifecycle diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/what-is-a-data-source.md b/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/what-is-a-data-source.md index 9cc1efc2d..4aef0b6df 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/what-is-a-data-source.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/data-sources/what-is-a-data-source.md @@ -1,53 +1,210 @@ --- title: "What is a Data Source?" linkTitle: "What is a Data Source?" -description: "Information regarding what a data source is." +description: "Information regarding what a data source is, which data sources are supported, and how flows connect to them and run commands." weight: 1 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO +In {{% ctx %}}, a **data source** is an external database or ODBC-backed system that a [flow][] connects to so it can read or change data. Flows do not talk to data sources directly; they use Data blocks—primarily [Execute Data Command][]—with two pieces of configuration: + +| Piece | Data type | Purpose | +| --- | --- | --- | +| Connection | [ConnectionDetails][] | How to connect (connection string and provider-specific options) | +| Command | [DataCommand][] | What to run (SQL or PL/SQL text and parameters) | + +This model is similar to ADO.NET in C#, where a [DbConnection][] opens a session to a database and a [DataCommand][] runs statements against it. {{% ctx %}} wraps that pattern in platform data types and blocks so connection handling, parameter binding, and result mapping work consistently across supported providers. + +| Term | Meaning | +| --- | --- | +| [Data source][Supported Data Sources] | The external database or ODBC-backed system | +| [ConnectionDetails][] | Configuration for establishing and maintaining a connection | +| [DataCommand][] | Configuration for a command to run against the data source | +| [Query Statement][] | Retrieves data (returns rows as a [List][]<[Structure][]>) | +| [Non Query Statement][] | Changes data (returns the number of rows affected as [Int32][]) | + +For how to construct connection strings and provider-specific behaviour, see [Supported Data Sources][] and the [ConnectionDetails][] data type pages listed below. ## Supported Data Sources -TODO: See Supported Data Sources +{{% ctx %}} currently supports a fixed set of data source types. Each type maps to a [ConnectionDetails][] implementation and is identified in [CommandException][] messages by category (`SQL`, `Oracle`, or `ODBC`). + +| Data source | [ConnectionDetails][] type | Concept page | +| --- | --- | --- | +| SQL Server | [SqlServerConnectionDetails][] | [SQL Server][] | +| Oracle | [OracleConnectionDetails][] | [Oracle][] | +| ODBC | [OdbcConnectionDetails][] | [ODBC][] | + +[ODBC][] can reach many drivers and backends (for example Microsoft Access, Excel, PostgreSQL, or MySQL) depending on the ODBC driver installed on the execution environment. Native SQL Server and Oracle connections use their dedicated connection-details types instead of ODBC when possible. + +Connection strings use provider-specific formats. See each [ConnectionDetails][] type page and [ConnectionStrings.com][] for examples. For verified versions and setup notes, see the pages under [Supported Data Sources][]. + +## Commands and statements + +Commands are defined with [DataCommand][] types and passed to [Execute Data Command][] on the [Command][] property. + +| [DataCommand][] type | Use when | +| --- | --- | +| [QueryCommand][] | Running [Query Statements][] (SELECT and similar); returns rows | +| [NonQueryCommand][] | Running [Non Query Statements][] (INSERT, UPDATE, DELETE, and similar); returns rows affected | +| [Command][] | A single statement that may be query or non-query; the block parses the statement type | +| [Commands][] | Multiple statements in one command; results are collected per statement | + +It is recommended to use [parameterised commands][] so values are bound safely and [SQL Injection][] is avoided. The `@` prefix marks parameters in command text (for example `"SELECT * FROM Table WHERE Id = @Id"`). See [Parameterised Commands][] on the [Execute Data Command][] block for examples and provider-specific parameter rules. + +[QueryCommand][] or [NonQueryCommand][] are preferred over [Command][] or [Commands][] when performance matters, because they skip statement parsing. For PL/SQL blocks and other statements with dependencies between parts of the script, use [QueryCommand][] or [NonQueryCommand][] rather than [Commands][]; see [Complex Commands][] on the [Execute Data Command][] block. + +## Managing connections to a data source + +Connections are managed by [Execute Data Command][]. The block creates, opens, reuses, and closes connections based on the [Connection Details][] property, the [Close Connection][] property, and how connection details are supplied. + +### Connection details -## Managing Connections to a Data Source +[ConnectionDetails][] holds the information required to connect, chiefly the [connection string][]. The connection-details type must match the data source (for example [SqlServerConnectionDetails][] for SQL Server). -TODO: +Create connection details in the [Expression Editor][] (for example `new SqlServerConnectionDetails("Server=...;Database=...;Trusted_Connection=True;")`) or pass a [variable][] that already holds a [ConnectionDetails][] object. -- Connections are managed by the block -- See Working with Connections (connection life cycle, using a variable for connections over literal/expression) +### Opening connections {#opening-connections} + +[Execute Data Command][] applies these rules when it runs: + +* If no connection exists for the supplied [Connection Details][], a new connection is created and opened. +* If a connection exists but is closed, it is opened and used. +* If a connection exists and is already open, it is reused. + +When [Connection Details][] are supplied through a [variable][], the same connection can stay open across multiple [Execute Data Command][] blocks if [Close Connection][] is `false`. Reusing a connection avoids the cost of opening a new connection on every block execution. + +When [Connection Details][] are supplied through a [literal][] or [expression][], a new connection is created each time the block runs. If [Close Connection][] is `false`, that connection is still closed automatically after the block finishes and before the flow ends—it cannot be shared with later blocks the way a variable-backed connection can. + +To connect as a Windows user instead of a SQL or Oracle user, set an appropriate trusted or integrated-security connection string and use the block's [Run As][] property with [UserCredentials][]; see [Execute Data Command][] for local versus remote [LogonType][] requirements. + +### Closing connections {#closing-connections} + +Set [Close Connection][] to `true` to close the connection immediately after the command runs. + +When [Close Connection][] is `false`: + +* If [Connection Details][] were supplied through a [variable][], the connection stays open until the variable goes out of [scope][] or the flow ends, whichever comes first. +* If [Connection Details][] were supplied through a [literal][] or [expression][], the connection is closed automatically after the block finishes and before the flow ends. + +For full property defaults, exceptions, and examples, see [Execute Data Command][]. ## Remarks -### Known Limitations +### Known limitations + +#### Limited set of supported data sources + +Only the data source types listed in [Supported Data Sources][] are supported natively today. Additional providers may be added in future releases. -#### Limited Set of Supported Data Sources +#### Provider-specific limitations -Currently, there are a limited set of Supported Data Sources. +Some limitations apply to particular connection types: -In the future this limitation may be removed. +* [OracleConnectionDetails][] — [Integrated Security][] connection string format is not supported. When running multiple statements with [QueryCommand][] or [NonQueryCommand][], Oracle PL/SQL requires [block statement][] syntax. +* [OdbcConnectionDetails][] — string values cannot be used as parameters when connected to a Microsoft Access data source. +* [Execute Data Command][] — when using a [parameterised command][] to execute a stored procedure, output parameters cannot be written back. + +#### Statement parsing and errors + +For [OdbcConnectionDetails][] and [SqlServerConnectionDetails][], invalid SQL is typically reported at runtime (`DataCommandErrorCode.Runtime`) rather than during parsing (`DataCommandErrorCode.Statement`), because those providers attempt execution instead of throwing during parse. Oracle uses a parser and may surface `DataCommandErrorCode.Statement` errors earlier. See [DataCommandErrorCode][] for error code details. ## See Also ### Related Concepts -TODO +* [Supported Data Sources][] +* [SQL Server][] +* [Oracle][] +* [ODBC][] +* [What is a Variable?][] +* [What is a Scope?][] ### Related Data Types -TODO +* [ConnectionDetails][] +* [SqlServerConnectionDetails][] +* [OracleConnectionDetails][] +* [OdbcConnectionDetails][] +* [DataCommand][] +* [QueryCommand][] +* [NonQueryCommand][] +* [Command][] +* [Commands][] +* [DataCommandErrorCode][] ### Related Blocks -TODO +* [Execute Data Command][] + +### Related Exceptions + +* [CommandException][] +* [InvalidConnectionStringException][] ### External Documentation -TODO +* [DbConnection Class (System.Data.Common)][DbConnection] +* [SqlConnection Class (Microsoft.Data.SqlClient)][SqlConnection] +* [OdbcConnection Class (System.Data.Odbc)][OdbcConnection] +* [Connection strings][ConnectionStrings.com] + +[flow]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Flows.WhatIsAFlow.MainDoc" >}} +[Execute Data Command]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.MainDoc" >}} +[Supported Data Sources]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.MainDoc" >}} +[SQL Server]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.SqlServer.MainDoc" >}} +[Oracle]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.Oracle.MainDoc" >}} +[ODBC]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.Odbc.MainDoc" >}} + +[ConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.ConnectionDetails.MainDoc" >}} +[SqlServerConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.SqlServerConnectionDetails.MainDoc" >}} +[OracleConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.OracleConnectionDetails.MainDoc" >}} +[OdbcConnectionDetails]: {{< url path="Cortex.Reference.DataTypes.Data.OdbcConnectionDetails.MainDoc" >}} +[DataCommand]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommand.MainDoc" >}} +[QueryCommand]: {{< url path="Cortex.Reference.DataTypes.Data.QueryCommand.MainDoc" >}} +[NonQueryCommand]: {{< url path="Cortex.Reference.DataTypes.Data.NonQueryCommand.MainDoc" >}} +[Command]: {{< url path="Cortex.Reference.DataTypes.Data.Command.MainDoc" >}} +[Commands]: {{< url path="Cortex.Reference.DataTypes.Data.Commands.MainDoc" >}} +[DataCommandErrorCode]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}} + +[List]: {{< url path="Cortex.Reference.DataTypes.Collections.List.MainDoc" >}} +[Structure]: {{< url path="Cortex.Reference.DataTypes.Collections.Structure.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} + +[Connection Details]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ConnectionDetailsProperty" >}} +[Close Connection]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.CloseConnectionProperty" >}} +[Parameterised Commands]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ParameterisedCommands" >}} +[parameterised command]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ParameterisedCommands" >}} +[Complex Commands]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.ComplexCommands" >}} +[Query Statement]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.QueryStatements" >}} +[Query Statements]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.QueryStatements" >}} +[Non Query Statement]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.NonQueryStatements" >}} +[Non Query Statements]: {{< url path="Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand.NonQueryStatements" >}} +[Run As]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.CommonProperties.RunAsProperty" >}} + +[CommandException]: {{< url path="Cortex.Reference.Exceptions.Data.CommandException.MainDoc" >}} +[InvalidConnectionStringException]: {{< url path="Cortex.Reference.Exceptions.Data.InvalidConnectionStringException.MainDoc" >}} + +[What is a Variable?]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[What is a Scope?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Scopes.WhatIsAScope.MainDoc" >}} +[scope]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Scopes.WhatIsAScope.MainDoc" >}} +[variable]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[literal]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.LiteralEditor.MainDoc" >}} +[expression]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} + +[UserCredentials]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.MainDoc" >}} +[LogonType]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.LogonType" >}} +[connection string]: {{< url path="Cortex.Reference.DataTypes.Data.ConnectionDetails.MainDoc" >}} + +[Integrated Security]: {{< url path="ConnectionStrings.OracleConnectionIntegratedSecurity" >}} +[block statement]: {{< url path="Oracle.PL-SQL.BlockStatement" >}} +[ConnectionStrings.com]: {{< url path="ConnectionStrings.MainDoc" >}} +[SQL Injection]: {{< url path="W3.SqlInjection" >}} + +[DbConnection]: https://learn.microsoft.com/en-us/dotnet/api/system.data.common.dbconnection +[SqlConnection]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.data.sqlclient.sqlconnection +[OdbcConnection]: https://learn.microsoft.com/en-us/dotnet/api/system.data.odbc.odbcconnection diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/date-and-time/date-and-time-formatting.md b/content/en/docs/2026.3/Reference/Concepts/working-with/date-and-time/date-and-time-formatting.md index 2edcfc4d4..deac651af 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/date-and-time/date-and-time-formatting.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/date-and-time/date-and-time-formatting.md @@ -1,149 +1,271 @@ --- title: "Date and Time Formatting" linkTitle: "Date and Time Formatting" -description: "This page describes the concept of Date and Time Formatting, including Format Providers, Format Templates and Format Specifiers." +description: "How format providers, format templates, and format specifiers control the text representation of DateTime and DateTimeOffset values in CORTEX flows." +weight: 2 --- # {{% param title %}} -## Overview +## Summary -TODO: +[DateTime][] and [DateTimeOffset][] values are often converted to and from text—for example when reading user input, writing log messages, or exchanging data with external systems. In .NET and {{% ctx %}}, that conversion is controlled by: -* Overview/summary -* Common formats ISO8601, Invariant Culture +* A **format provider** — supplies culture-specific patterns and names (via [CultureInfo][] and [IFormatProvider][]) +* A **format template** — a standard or custom string that defines which parts of the date and time appear in the output +* **Format specifiers** — the individual characters or sequences inside a format template (for example `dd`, `MM`, `yyyy`) + +[Convert Text To DateTime][] and [Convert Date Time To Text][] expose **Format Template** and **Format Provider** properties for explicit control. Expressions can use `DateTime.ToString`, `DateTimeOffset.ToString`, `DateTime.Parse`, and related .NET APIs with the same concepts. + +For an overview of date and time types and when to use them, see [What is Date and Time?][]. For culture types in general, see [Culture][]. + +| Topic | Typical choice | +| --- | --- | +| Cross-server persistence and block defaults | [Invariant Culture][] | +| Server-local presentation in expressions | [Current Culture][] | +| Fixed regional format for users | [Specific Culture][] (for example `new CultureInfo("en-GB")`) | +| Round-trip and sortable text | [ISO 8601 Standard][] (`"O"` / `"o"`, `"s"`) | ## Format providers -TODO: +A **format provider** implements [IFormatProvider][] and supplies the cultural rules used when a [DateTime][] or [DateTimeOffset][] value is formatted or parsed. In practice, the provider is almost always a [CultureInfo][] instance whose `DateTimeFormat` property defines short and long date/time patterns, month and day names, AM/PM designators, and separators. + +Pass a format provider to: -* what are they -* ways of creating - * CultureInfo.InvariantCulture - * CultureInfo.CurrentCulture - * new CultureInfo("") - * new CultureInfo("en-GB") +* Block properties named **Format Provider** on [Convert Text To DateTime][] and [Convert Date Time To Text][] +* .NET methods such as `ToString(string, IFormatProvider)`, `DateTime.Parse(string, IFormatProvider)`, and `DateTimeOffset.ParseExact(string, string, IFormatProvider)` -or +### Obtaining a format provider -https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#datetimeformatinfo-properties +| Approach | Expression | Notes | +| --- | --- | --- | +| Invariant culture | `CultureInfo.InvariantCulture` | Culture-insensitive; fixed patterns. See [Invariant Culture][] | +| Empty culture name | `new CultureInfo("")` | Equivalent to the invariant culture | +| Current culture | `CultureInfo.CurrentCulture` | Reflects the server's regional settings. See [Current Culture][] | +| Specific culture | `new CultureInfo("en-GB")` | Fixed locale regardless of server settings. See [Specific Cultures][] | -https://learn.microsoft.com/en-us/dotnet/api/system.globalization.datetimeformatinfo?view=net-5.0 +The `DateTimeFormat` property on a [CultureInfo][] object exposes pattern properties such as `ShortDatePattern`, `LongDatePattern`, `ShortTimePattern`, and `LongTimePattern`. Standard format specifiers (for example `"d"` and `"G"`) resolve to these patterns. For the full list of `DateTimeFormatInfo` properties, see [DateTimeFormatInfo](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.datetimeformatinfo) in the .NET documentation. ### Invariant Culture -TODO: +When [Invariant Culture][] is used as the format provider: + +* Standard format specifiers such as `"d"` and `"G"` use **invariant** custom patterns (for example short date `"MM/dd/yyyy"`), not the patterns of the server's current locale. +* With no format template, [Convert Date Time To Text][] and [Convert Text To DateTime][] use the invariant default pattern `"MM/dd/yyyy HH:mm:ss zzz"` (for example `12/31/2021 00:00:00 +00:00` for `2021-12-31T00:00:00+00:00`). +* Standard format specifier `"F"` (full date long time) uses the invariant long pattern `"dddd, dd MMMM yyyy HH:mm:ss"` (for example `Friday, 31 December 2021 00:00:00`). + +Four standard format specifiers — `"O"` / `"o"`, `"R"` / `"r"`, `"s"`, and `"u"` — are defined by the invariant culture and produce representations intended to be **identical across cultures**. See [Invariant format templates](#invariant-format-templates). + +[Convert Date Time To Text][] and [Convert Text To DateTime][] default **Format Provider** to `CultureInfo.InvariantCulture` when the property is omitted or `null`. Many other formatting blocks do the same; see [Invariant Culture][]. -* default format `"MM/dd/yyyy HH:mm:ss zzz"` -* rules https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.parse?view=net-5.0 +Parsing with invariant culture expects US-style month/day order in the default pattern (for example `"12/31/2021 00:00:00 +00:00"`). For culture-independent storage, prefer [ISO 8601 Standard][] text instead. ### Current Culture -TODO +[Current Culture][] (`CultureInfo.CurrentCulture`) supplies patterns from the **server's configured locale**. Use it in expressions when formatted output should follow the machine where the flow runs—for example `dateTime.ToString("d", CultureInfo.CurrentCulture)`. + +Standard format specifiers resolve to the current culture's `DateTimeFormatInfo` patterns. On Windows, those patterns can reflect **Control Panel** regional customizations when they apply to `CurrentCulture`. See [Operating System Settings][]. + +{{% ctx %}} blocks do **not** automatically use the current culture when **Format Provider** is omitted; they typically default to [Invariant Culture][]. To format or parse using the server's locale in a block, set **Format Provider** explicitly to `CultureInfo.CurrentCulture`. + +Expressions that call `DateTime.Parse` or `DateTimeOffset.Parse` without a format provider use the current culture's rules. `DateTime.Now.ToString()` and `DateTimeOffset.Now.ToString()` without a format template also use the current culture's short date and long time patterns. ## Format Templates -Format templates use characters called [format specifiers][] to define the text representation of [DateTimeOffset][] and [DateTime][] values. +**Format templates** define how [DateTime][] and [DateTimeOffset][] values are converted to and from text. Templates contain [format specifiers](#format-specifiers); characters that are not specifiers are copied to the output as literal text. There are two types of format template: -* [Standard Format Templates][] - use a single character [format specifier][format specifiers] -* [Custom Format Templates][] - use multiple character [format specifiers][] - -Both can be used to define how [DateTimeOffset][] and [DateTime][] values are converted to and from their text representation. +* [Standard format templates](#standard-format-templates) — a **single** character [format specifier](#format-specifiers) (for example `"d"`, `"G"`, `"O"`) +* [Custom format templates](#custom-format-templates) — **two or more** characters, including white space (for example `"dd/MM/yyyy"`, `"yyyy-MM-ddTHH:mm:ss"`) -TODO: +A format template is always interpreted together with a format provider. The provider determines which custom pattern a standard specifier expands to, and supplies localized month and day names when custom specifiers such as `MMMM` or `ddd` are used. -* *How do they relate to format providers +| Scenario | Format template | Format provider | Result | +| --- | --- | --- | --- | +| [Convert Date Time To Text][] — block default | `null` or empty | `null` | [ISO 8601 Standard][] (`yyyy-MM-ddTHH:mm:ss.fffffffzzz`) | +| [Convert Date Time To Text][] — invariant default | `null` or empty | `CultureInfo.InvariantCulture` | Invariant default (`MM/dd/yyyy HH:mm:ss zzz`) | +| Expression `ToString()` | `null` (overload without template) | [Current Culture][] | Culture short date + long time patterns | +| Explicit standard specifier | `"G"` | `new CultureInfo("en-GB")` | General date long time for `en-GB` | ### Standard Format Templates -A standard format template uses a single character [format specifier][format specifiers] to define the text representation of [DateTimeOffset][] and [DateTime][] values and can be used to define how [DateTimeOffset][] and [DateTime][] values are converted to and from their text representation. - -The following table shows all of the standard format templates. - -All examples are for a system configured with British [culture][] (i.e. `en-GB`), and use a local time of `2PM 1st of July 2022` with `1` hour UTC offset, honouring British Summer Time (BST). - -| Pattern | Format Specifier | DateTimeOffset Example | DateTime Example | Notes | -|-|-|-|-|-| -| Short Date Pattern | `d` | `2022-07-01T14:00:00.0000000+01:00` -> `1/7/2022` | TODO | See [short date ("d") format specifier] for further information | -| Short Time Pattern | `t` | `2022-07-01T14:00:00.0000000+01:00` -> `2:00 PM` | TODO | See [short time ("t") format specifier] for further information | -| Long Date Pattern | `D` | `2022-07-01T14:00:00.0000000+01:00` -> `5 April 2022` | TODO | See [long date ("D") format specifier] for further information | -| Long Time Pattern | `T` | `2022-07-01T14:00:00.0000000+01:00` -> `2:00:00 PM` | TODO | See [long time ("T") format specifier] for further information | -| Full Date/Time Pattern (Short Time) | `f` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [full date short time ("f") format specifier] for further information | -| Full Date/Time Pattern (Long Time) | `F` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [full date long time ("F") format specifier] for further information | -| General Date/Time Pattern (Short Time) | `g` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [general date short time ("g") format specifier] for further information | -| General Date/Time Pattern (Long Time) | `G` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [general date long time ("G") format specifier] for further information | -| Round-Trip Date/Time Pattern | `O`, `o` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [round-trip ("O", "o") format specifier] for further information | -| RFC1123 Pattern | `R`, `r` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [RFC1123 ("R", "r") format specifier] for further information | -| Sortable Date/Time Pattern | `s` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [sortable ("s") format specifier] for further information | -| Universal Sortable Date/Time Pattern | `u` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [universal sortable ("u") format specifier] for further information | -| Universal Full Date/Time Pattern | `U` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [universal full ("U") format specifier] for further information | -| Month Day Pattern | `M`, `m` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [month ("M", "m") format specifier] for further information | -| Year Month Pattern | `Y`, `y` | `2022-07-01T14:00:00.0000000+01:00` -> `TODO` | TODO | See [year month ("Y") format specifier] for further information | -| Unknown Pattern | Any other single character | N/A | N/A | Throws a [FormatException][] | - -#### Invariant Format Templates - -TODO: - -* Invariant format templates - - * In some cases, the standard format string serves as a convenient abbreviation for a longer custom format string that is invariant. Four standard format strings fall into this category: "O" (or "o"), "R" (or "r"), "s", and "u". These strings correspond to custom format strings defined by the invariant culture. They produce string representations of date and time values that are intended to be identical across cultures. The following table provides information on these four standard date and time format strings. - * TABLE 2 - * Standard format string Defined by DateTimeFormatInfo.InvariantInfo property Custom format string - * "O" or "o" None yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK - * "R" or "r" RFC1123Pattern ddd, dd MMM yyyy HH':'mm':'ss 'GMT' - * "s" SortableDateTimePattern yyyy'-'MM'-'dd'T'HH':'mm':'ss - * "u" UniversalSortableDateTimePattern yyyy'-'MM'-'dd HH':'mm':'ss'Z' - * https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings +A standard format template is a single-character alias for a culture-specific custom pattern defined by `DateTimeFormatInfo`. The same specifier can produce different text for different cultures—for example `"d"` yields `6/15/2009` for `en-US` and `15/06/2009` for `en-GB`. + +The following table lists all standard format templates. Examples use a system configured with British [culture][Culture] (`en-GB`), with a local time of **2 PM on Friday 1 July 2022** and a **+01:00** UTC offset (British Summer Time). + +| Pattern | Format specifier | DateTimeOffset example | DateTime example | Notes | +| --- | --- | --- | --- | --- | +| Short date | `d` | `2022-07-01T14:00:00.0000000+01:00` → `01/07/2022` | `2022-07-01T14:00:00` → `01/07/2022` | See [short date ("d") format specifier][] | +| Short time | `t` | `2022-07-01T14:00:00.0000000+01:00` → `14:00` | `2022-07-01T14:00:00` → `14:00` | See [short time ("t") format specifier][] | +| Long date | `D` | `2022-07-01T14:00:00.0000000+01:00` → `Friday, 1 July 2022` | `2022-07-01T14:00:00` → `Friday, 1 July 2022` | See [long date ("D") format specifier][] | +| Long time | `T` | `2022-07-01T14:00:00.0000000+01:00` → `14:00:00` | `2022-07-01T14:00:00` → `14:00:00` | See [long time ("T") format specifier][] | +| Full date/time (short time) | `f` | `2022-07-01T14:00:00.0000000+01:00` → `Friday, 1 July 2022 14:00` | `2022-07-01T14:00:00` → `Friday, 1 July 2022 14:00` | See [full date short time ("f") format specifier][] | +| Full date/time (long time) | `F` | `2022-07-01T14:00:00.0000000+01:00` → `Friday, 1 July 2022 14:00:00` | `2022-07-01T14:00:00` → `Friday, 1 July 2022 14:00:00` | See [full date long time ("F") format specifier][] | +| General date/time (short time) | `g` | `2022-07-01T14:00:00.0000000+01:00` → `01/07/2022 14:00` | `2022-07-01T14:00:00` → `01/07/2022 14:00` | See [general date short time ("g") format specifier][] | +| General date/time (long time) | `G` | `2022-07-01T14:00:00.0000000+01:00` → `01/07/2022 14:00:00` | `2022-07-01T14:00:00` → `01/07/2022 14:00:00` | See [general date long time ("G") format specifier][] | +| Round-trip | `O`, `o` | `2022-07-01T14:00:00.0000000+01:00` → `2022-07-01T14:00:00.0000000+01:00` | `2022-07-01T14:00:00` → `2022-07-01T14:00:00.0000000` | See [round-trip ("O", "o") format specifier][]; [ISO 8601 Standard][] | +| RFC1123 | `R`, `r` | `2022-07-01T14:00:00.0000000+01:00` → `Fri, 01 Jul 2022 13:00:00 GMT` | `2022-07-01T14:00:00` → `Fri, 01 Jul 2022 14:00:00 GMT` | See [RFC1123 ("R", "r") format specifier][]; offset converted to GMT for `DateTimeOffset` | +| Sortable | `s` | `2022-07-01T14:00:00.0000000+01:00` → `2022-07-01T14:00:00` | `2022-07-01T14:00:00` → `2022-07-01T14:00:00` | See [sortable ("s") format specifier][]; [ISO 8601 Standard][] | +| Universal sortable | `u` | `2022-07-01T14:00:00.0000000+01:00` → `2022-07-01 13:00:00Z` | `2022-07-01T14:00:00` → `2022-07-01 14:00:00Z` | See [universal sortable ("u") format specifier][]; displays UTC | +| Universal full | `U` | `2022-07-01T14:00:00.0000000+01:00` → `Friday, 1 July 2022 13:00:00` | `2022-07-01T14:00:00` → `Friday, 1 July 2022 13:00:00` | See [universal full ("U") format specifier][]; converts to UTC before formatting | +| Month day | `M`, `m` | `2022-07-01T14:00:00.0000000+01:00` → `1 July` | `2022-07-01T14:00:00` → `1 July` | See [month ("M", "m") format specifier][] | +| Year month | `Y`, `y` | `2022-07-01T14:00:00.0000000+01:00` → `July 2022` | `2022-07-01T14:00:00` → `July 2022` | See [year month ("Y") format specifier][] | +| Unknown | Any other single character | N/A | N/A | Throws a [FormatException][] | + +`DateTimeOffset.ToString()` without a format template uses the [current culture][Current Culture] short date pattern, long time pattern, and a `zzz` offset suffix (for example `1/7/2022 1:00:00 PM +00:00` for `en-GB` when the offset is zero). `DateTime.ToString()` without a format template uses the short date and long time patterns without an offset suffix. + +#### Invariant format templates + +Some standard format strings are abbreviations for **invariant** custom patterns. They produce the same representation regardless of the format provider's culture: + +| Standard format | `DateTimeFormatInfo.InvariantInfo` property | Custom format string | +| --- | --- | --- | +| `"O"` or `"o"` | (none) | `yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK` | +| `"R"` or `"r"` | `RFC1123Pattern` | `ddd, dd MMM yyyy HH':'mm':'ss 'GMT'` | +| `"s"` | `SortableDateTimePattern` | `yyyy'-'MM'-'dd'T'HH':'mm':'ss` | +| `"u"` | `UniversalSortableDateTimePattern` | `yyyy'-'MM'-'dd HH':'mm':'ss'Z'` | + +For `"O"` / `"o"`, the `K` specifier in the custom pattern emits `Z` for UTC, an offset for `DateTimeOffset`, or nothing for `DateTime` with `DateTimeKind.Unspecified`. #### ISO 8601 Standard -TODO: +[ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) is a widely used date and time text format. In {{% ctx %}} and .NET: + +* [Convert Date Time To Text][] outputs [ISO 8601 Standard][] text by default when **Format Template** and **Format Provider** are not set (pattern `yyyy-MM-ddTHH:mm:ss.fffffffzzz` for [DateTimeOffset][], for example `2021-12-31T00:00:00.0000000+00:00`). +* [Convert Text To DateTime][] expects [ISO 8601 Standard][] input when **Format Template** is not set; if that parse fails, it falls back to the default pattern of the **Format Provider** (or [Invariant Culture][] rules). +* [Convert Object To Json][] serializes date and time values using ISO 8601-compatible patterns. + +Common ISO 8601-related format templates: + +| Template | Custom pattern | Typical use | +| --- | --- | --- | +| Round-trip `"O"` / `"o"` | `yyyy-MM-ddTHH:mm:ss.fffffffK` | Preserve kind and offset; preferred for round-tripping | +| Sortable `"s"` | `yyyy-MM-ddTHH:mm:ss` | Sortable, culture-invariant (no offset in output) | +| Block default | `yyyy-MM-ddTHH:mm:ss.fffffffzzz` | {{% ctx %}} date and time block output for `DateTimeOffset` | +| `DateTime` in JSON | `yyyy-MM-ddTHH:mm:ss.fffffffK` | `Z` suffix when kind is UTC | -* round-trip "O", "o" and sortable "s" complies with ISO -* `yyyy-MM-ddTHH:mm:ss.fffffffK` for `DateTime` -* `yyyy-MM-ddTHH:mm:ss.fffffffzzz` for `DateTimeOffset` +The `"O"` / `"o"` and `"s"` standard specifiers comply with ISO 8601. When exchanging data between systems or storing values in a culture-independent form, prefer these patterns or the block default over locale-specific patterns such as `dd/MM/yyyy`. ### Custom Format Templates -TODO: +A **custom format template** contains two or more characters. Any template that is not exactly one standard format specifier character is treated as custom—including templates that look like standard specifiers but include extra characters (for example `"dd/MM/yyyy"`). -* What are they - multiple character format specifiers -* Some examples -* https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings +Examples for a [DateTimeOffset][] value of `2022-07-01T14:00:00+01:00` with `en-GB` format provider: + +| Format template | Result | Notes | +| --- | --- | --- | +| `dd/MM/yyyy` | `01/07/2022` | Day and month with leading zeros | +| `dddd, d MMMM yyyy` | `Friday, 1 July 2022` | Full day and month names from the provider | +| `HH:mm:ss zzz` | `14:00:00 +01:00` | 24-hour clock with offset | +| `yyyy-MM-ddTHH:mm:ss.fffffffzzz` | `2022-07-01T14:00:00.0000000+01:00` | [ISO 8601 Standard][] pattern used by blocks | +| `'Week' ww` | `Week 26` | Literal text in single quotes | + +To include literal characters that would otherwise be interpreted as specifiers, escape them with single quotes (for example `HH'h'mm'min'` → `14h00min`). + +Custom templates are used by [Convert Date Time To Text][] and [Convert Text To DateTime][] when **Format Template** is set, and by `ToString(string, IFormatProvider)` and `ParseExact` in expressions. For the full list of custom specifiers, see [custom date and time format strings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings) in the .NET documentation. ## Format specifiers -TODO: - -* what are they -* Table of them -* Some of the commonly used format specifiers are: - * dd: The day of the month, from 01 through 31 - * MM: The month, from 01 through 12 - * yyyy: The year as a four-digit number - * HH: The hour, using a 24-hour clock from 00 to 23 - * hh: The hour, using a 12-hour clock from 01 to 12 - * mm: The minute, from 00 through 59 - * ss: The second, from 00 through 59 - * fff: The number of milliseconds - * tttt: The AM/PM designator - * The full set of allowed format specifiers are as per standard Microsoft .NET Custom Date and Time Format Strings. +**Format specifiers** are the placeholders inside a format template that are replaced with parts of a date and time value. In a **standard** template, a single character is the specifier. In a **custom** template, one or more characters form each specifier (for example `dd`, `yyyy`, `fff`). + +Commonly used custom format specifiers: + +| Specifier | Description | Example output (`2022-07-01T14:30:45.123+01:00`, `en-GB`) | +| --- | --- | --- | +| `d`, `dd` | Day of the month (`d` = no leading zero) | `1`, `01` | +| `M`, `MM`, `MMM`, `MMMM` | Month | `7`, `07`, `Jul`, `July` | +| `y`, `yy`, `yyyy` | Year | `22`, `22`, `2022` | +| `H`, `HH` | Hour, 24-hour clock | `14`, `14` | +| `h`, `hh` | Hour, 12-hour clock | `2`, `02` | +| `m`, `mm` | Minute | `30`, `30` | +| `s`, `ss` | Second | `45`, `45` | +| `f`, `ff`, `fff`, `ffffff`, `fffffff` | Fractions of a second | `1`, `12`, `123`, … | +| `t`, `tt` | AM/PM designator | `P`, `PM` | +| `z`, `zz`, `zzz` | UTC offset | `+1`, `+01`, `+01:00` | +| `K` | Time zone information | `+01:00` for `DateTimeOffset`; `Z` or empty for `DateTime` depending on `Kind` | +| `ddd`, `dddd` | Abbreviated or full day name | `Fri`, `Friday` | + +If a custom format template contains **no** valid specifiers and the input text matches the template exactly, [Convert Text To DateTime][] sets the output to the **current** date and time (documented block behaviour). + +A single invalid standard format character (for example `"a"`) throws a [FormatException][] in both blocks and .NET formatting APIs. ## Operating System Settings -TODO: +Regional settings on the server operating system affect [Current Culture][] and therefore any formatting or parsing that uses `CultureInfo.CurrentCulture` or parameterless `Parse` / `ToString` calls in expressions. -* How can these affect datetime formats etc. -* https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#control-panel-settings +On **Windows**, custom short and long date/time patterns configured in **Settings → Time & language → Language & region → Regional format** (or legacy **Control Panel → Region**) can override the default patterns for `CultureInfo.CurrentCulture` when those customizations apply. Standard invariant specifiers (`"O"`, `"s"`, `"u"`, `"R"`) are not affected. -[Standard Format Templates]: {{< ref "#standard-format-templates" >}} -[Custom Format Templates]: {{< ref "#custom-format-templates" >}} -[Format Specifiers]: {{< ref "#format-specifiers" >}} -[Operating System Settings]: {{< ref "#operating-system-settings" >}} +Implications for {{% ctx %}} flows: +* Block examples in this documentation that use `en-GB` assume a server configured with that culture. Results on a server set to `en-US` or another locale will differ for culture-sensitive patterns. +* Flows that must produce consistent text across servers should use [Invariant Culture][], [ISO 8601 Standard][], or an explicit [Specific Culture][]—not an unspecified **Format Provider** combined with culture-sensitive templates. +* In clustered deployments, align operating system culture and regional settings on every node so `CultureInfo.CurrentCulture` behaves the same everywhere. See [Current Culture][]. + +[Convert Text To DateTime][] and [Convert Date Time To Text][] note that changes to operating system date and time formats can change example results when a culture-sensitive provider is used. + +For more information, see [Control Panel settings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#control-panel-settings) in the .NET documentation. + +## Remarks + +### Known limitations + +* Formatting and parsing depend on cultures installed on the server. An invalid culture name (for example `new CultureInfo("enaa")`) throws [CultureInfoNotFoundException][]. +* [DateTime][] values without a clear `DateTimeKind` can produce ambiguous text and parsing results; prefer [DateTimeOffset][] when the offset must be preserved. See [What is Date and Time?][]. +* Custom format templates are culture-sensitive for month and day **names** but use invariant digits for numeric components unless a provider supplies different rules. +* {{% ctx %}} date and time blocks do not apply time zone or daylight saving rules; only the offset on the value affects `zzz` / `K` output. + +## See Also + +### Related Concepts + +* [What is Date and Time?][] — date and time types and block overview +* [Culture][] — culture types and when to use each +* [Invariant Culture][] — culture-insensitive formatting defaults +* [Current Culture][] — server regional settings +* [Specific Cultures][] — fixed locale formatting +* [Formatting][] — composite text formatting with format providers + +### Related Data Types + +* [DateTime][] +* [DateTimeOffset][] +* [CultureInfo][] +* [IFormatProvider][] + +### Related Blocks + +* [Convert Text To DateTime][] +* [Convert Date Time To Text][] +* [Convert Object To Text][] +* [Convert Object To Json][] + +### External Documentation + +* [Standard date and time format strings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings) +* [Custom date and time format strings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings) +* [Parsing dates and times in .NET](https://learn.microsoft.com/en-us/dotnet/standard/base-types/parsing-datetime) +* [Date and time values and their use in .NET](https://learn.microsoft.com/en-us/dotnet/standard/datetime/) +* [Composite formatting](https://learn.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting) + +[What is Date and Time?]: {{< ref "what-is-date-and-time.md" >}} [Culture]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.MainDoc" >}} +[Invariant Culture]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.InvariantCulture.MainDoc" >}} +[Current Culture]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.CurrentCulture.MainDoc" >}} +[Specific Cultures]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.SpecificCultures.MainDoc" >}} +[Specific Culture]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.SpecificCultures.MainDoc" >}} +[Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Text.Formatting.MainDoc" >}} + +[DateTime]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTime.MainDoc" >}} +[DateTimeOffset]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTimeOffset.MainDoc" >}} +[CultureInfo]: {{< url path="Cortex.Reference.DataTypes.Text.CultureInfo.MainDoc" >}} +[IFormatProvider]: {{< url path="Cortex.Reference.DataTypes.Text.IFormatProvider.MainDoc" >}} +[CultureInfoNotFoundException]: {{< url path="MSDocs.DotNet.Api.System.Globalization.CultureInfoNotFoundException" >}} + +[Convert Text To DateTime]: {{< url path="Cortex.Reference.Blocks.DateAndTime.ConvertDateTime.ConvertTextToDateTime.MainDoc" >}} +[Convert Date Time To Text]: {{< url path="Cortex.Reference.Blocks.DateAndTime.ConvertDateTime.ConvertDateTimeToText.MainDoc" >}} +[Convert Object To Text]: {{< url path="Cortex.Reference.Blocks.Objects.ConvertObject.ConvertObjectToText.MainDoc" >}} +[Convert Object To Json]: {{< url path="Cortex.Reference.Blocks.Json.ConvertJson.ConvertObjectToJson.MainDoc" >}} [short date ("d") format specifier]: {{< url path="MSDocs.DotNet.Api.System.DateTimeOffset.ShortDateFormat" >}} [short time ("t") format specifier]: {{< url path="MSDocs.DotNet.Api.System.DateTimeOffset.ShortTimeFormat" >}} @@ -163,19 +285,5 @@ TODO: [FormatException]: {{< url path="MSDocs.DotNet.Api.System.FormatException" >}} -[DateTime]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTime.MainDoc" >}} -[DateTimeOffset]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTimeOffset.MainDoc" >}} - -TODO Add concepts page for string/object formatting that links to: - -* https://learn.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting -* https://learn.microsoft.com/en-us/dotnet/standard/base-types/best-practices-display-data - -TODO Add concepts page for formatting numbers that links to: - -* https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings -* https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-numeric-format-strings - -TODO: https://learn.microsoft.com/en-us/dotnet/standard/base-types/parsing-datetime - -TODO: Update examples for most common en-gb os formats +[ISO 8601 Standard]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.ISO8601Standard" >}} +[Operating System Settings]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.OperatingSystemSettings" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/date-and-time/what-is-date-and-time.md b/content/en/docs/2026.3/Reference/Concepts/working-with/date-and-time/what-is-date-and-time.md index 01d8fae95..0b7f50d8b 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/date-and-time/what-is-date-and-time.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/date-and-time/what-is-date-and-time.md @@ -1,51 +1,189 @@ --- title: "What is Date and Time?" linkTitle: "What is Date and Time?" -description: "Information regarding what Date and Time is." +description: "Overview of date and time data types in CORTEX, including DateTime, DateTimeOffset, TimeSpan, and TimePeriod, and how to work with them in flows and blocks." weight: 1 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: - -- Date and Time are represented by different data types - - DateTime - - DateTimeOffset - - TimeSpan - - TimePeriod -- What is the difference between DateTime and DateTimeOffset? - - When should they be used -- What is the difference between TimeSpan and TimePeriod? - - When should they be used -- We don't deal with timezone issues within the blocks -- Offsets are handled within DateTimeOffset and can be dealt with using our blocks -- Useful link - https://learn.microsoft.com/en-us/dotnet/api/system.datetime?view=net-6.0 +Date and time values in {{% ctx %}} are represented by several related data types. Most date and time blocks work with [DateTimeOffset][] values that include a UTC offset. Intervals and durations use [TimeSpan][] or the {{% ctx %}}-specific [TimePeriod][] type. Supporting [enum][] types such as [DayOfWeek][] and [DateTimeComponentType][] are used when extracting or comparing parts of a date and time. + +How dates and times are formatted, parsed, and compared depends on [culture][Culture] settings. For format patterns and providers, see [Date and Time Formatting][]. + +| Data type | Full name | Purpose | More information | +| --- | --- | --- | --- | +| [DateTimeOffset][] | `System.DateTimeOffset` | A calendar date and time with an explicit UTC offset | Preferred type for date and time blocks | +| [DateTime][] | `System.DateTime` | A calendar date and time without a stored UTC offset | [Implicitly cast][Implicit Casting] to `DateTimeOffset` where required | +| [TimePeriod][] | `Cortex.DataTypes.DateAndTime.TimePeriod` | A calendar-aware interval (years through milliseconds) | Used by add/subtract time period blocks | +| [TimeSpan][] | `System.TimeSpan` | A fixed elapsed interval (days through milliseconds) | Automatically converted to `TimePeriod` where required | +| [DayOfWeek][] | `System.DayOfWeek` | A day of the week (`Sunday` through `Saturday`) | [Enum][enum] used with date components | +| [DateTimeComponentType][] | `Cortex.DataTypes.DateAndTime.DateTimeComponentType` | Identifies a component of a date and time (for example `Year`, `Month`, `Day`) | Used by [Get Date Time Component][] | + +## DateTime and DateTimeOffset + +Both [DateTime][] and [DateTimeOffset][] represent a date and time in the Gregorian calendar between `0001-01-01` and `9999-12-31`. The difference is whether a UTC offset is stored with the value. + +| | [DateTime][] | [DateTimeOffset][] | +| --- | --- | --- | +| **UTC offset** | Not stored as part of the value | Stored as part of the value (for example `+01:00`) | +| **Typical use** | Legacy .NET APIs, or when the offset is implied by context | Storing and comparing instants that may fall in different offsets | +| **In {{% ctx %}} blocks** | Accepted where a date and time is expected; converted when needed | Primary type for date and time block properties | +| **Text in blocks** | Converted using [ISO 8601 Standard][] when output by date and time blocks | [ISO 8601 Standard][] (for example `2021-11-05T08:48:08.0307614+00:00`) | + +A [DateTime][] value can be used wherever a [DateTimeOffset][] is expected and will be [implicitly cast][Implicit Casting]. When a `DateTime` is converted to a `DateTimeOffset`, the offset applied depends on how the `DateTime` was created (for example `DateTime.UtcNow` uses a zero offset, while `DateTime.Now` uses the local offset of the server). + +### When to use each type + +* Prefer [DateTimeOffset][] when working with date and time blocks, comparing values across offsets, or when the offset must be preserved in persisted or exchanged data. +* Use [DateTime][] when calling .NET APIs that expect `DateTime`, or when only the calendar date and clock time matter and the offset is handled separately. Be aware that comparisons and arithmetic without an explicit offset can be ambiguous. + +For creation, conversion, and formatting examples, see the [DateTime][] and [DateTimeOffset][] data type pages. + +## TimeSpan and TimePeriod + +Both [TimeSpan][] and [TimePeriod][] represent a length of time, but they model different kinds of intervals. + +| | [TimeSpan][] | [TimePeriod][] | +| --- | --- | --- | +| **Source** | .NET (`System.TimeSpan`) | {{% ctx %}} (`Cortex.DataTypes.DateAndTime.TimePeriod`) | +| **Components** | Days, hours, minutes, seconds, milliseconds | Years, months, days, hours, minutes, seconds, milliseconds | +| **Calendar awareness** | Fixed duration only (no years or months) | Supports calendar units (years and months) | +| **In {{% ctx %}}** | Used for fixed elapsed time; converted to `TimePeriod` where a block expects it | Used by [Add Time Period][], [Subtract Time Period][], [Get Time Period Between Date Times][], and [Wait For Duration][] | + +A [TimeSpan][] can be supplied wherever a [TimePeriod][] is expected and will be converted automatically. The conversion maps the `TimeSpan` day, hour, minute, second, and millisecond components into the corresponding `TimePeriod` fields; year and month components remain at their default of `0`. + +### When to use each type + +* Use [TimePeriod][] when adding or subtracting **years** or **months** from a date and time, or when you need the structured year/month/day/hour/minute/second/millisecond components used by date and time blocks. +* Use [TimeSpan][] for a **fixed elapsed duration** (for example the UTC offset on a [DateTimeOffset][] literal, or a duration expressed only in days and smaller units). For elapsed time between two instants without calendar months or years, [Get Time Period Between Date Times][] returns a `TimePeriod` measured in days and smaller units only (year and month components are not used because their length varies). + +When [Add Time Period][] or [Subtract Time Period][] adds months, if the day of the month exceeds the last day of the resultant month, the day is adjusted to the last valid day (for example adding one month to `2021-01-31` yields `2021-02-28`). + +## UTC offsets, time zones, and daylight saving + +[DateTimeOffset][] stores a **UTC offset** (the difference from Coordinated Universal Time), not a **time zone** identifier (such as `Europe/London`). {{% ctx %}} date and time blocks work with offsets and do not resolve time zone rules. + +* Blocks can create, compare, and adjust date and time values that include an offset. +* Blocks do **not** apply time zone or daylight saving time (DST) rules. [Add Time Period][] and [Subtract Time Period][] adjust by offset but cannot account for DST transitions, because those depend on a time zone definition rather than a fixed offset. +* For consistent behaviour across servers, use explicit offsets or UTC (`DateTimeOffset.UtcNow`) rather than assuming the server's local time zone matches end-user expectations. See also [Current Culture][] and [Operating System Settings][]. + +If flows must reflect a specific regional time zone (including DST), convert to and from UTC using an explicit offset or an external time zone library in an expression; do not rely on date and time blocks alone to infer time zone rules. + +## Formatting and parsing + +Text representation of date and time values depends on [culture][Culture] and format templates. Common cases include: + +* Date and time blocks default to [ISO 8601 Standard][] text for `DateTimeOffset` output. +* [Convert Text To DateTime][] and [Convert Date Time To Text][] accept optional format template and format provider properties. +* Parsing and formatting without an explicit provider typically follow [Invariant Culture][] rules in blocks; expressions using `DateTime.Parse` or `ToString()` follow the [Current Culture][] of the server unless a [CultureInfo][] is supplied. + +See [Date and Time Formatting][] for standard and custom format templates, format specifiers, and the effect of operating system regional settings. + +## Working with date and time in blocks + +Date and time blocks are grouped under [Date & Time blocks][]. Typical operations include: + +| Operation | Blocks | +| --- | --- | +| Get current date and time | [Get Current Date Time][] | +| Read a component (year, month, day, and so on) | [Get Date Time Component][] | +| Compare two date and times | [Is Date Time Equal][], [Is Date Time Before][], [Is Date Time After][], [Is Date Time Between][] | +| Add or subtract an interval | [Add Time Period][], [Subtract Time Period][] | +| Elapsed time between two date and times | [Get Time Period Between Date Times][] | +| Convert to or from text | [Convert Text To DateTime][], [Convert Date Time To Text][] | + +Most of these blocks use [DateTimeOffset][] for date and time properties and [TimePeriod][] for interval properties. See each block's remarks for defaults, exceptions, and edge cases. ## Remarks ### Known Limitations -TODO +* Date and time blocks do not resolve **time zone** or **daylight saving time** rules; only UTC **offsets** stored on [DateTimeOffset][] values are used. +* [Get Time Period Between Date Times][] does not populate year or month components in the result, because calendar years and months have variable lengths. +* [DateTime][] values without a clear offset context can produce unexpected results when [implicitly cast][Implicit Casting] to [DateTimeOffset][] or compared across servers with different regional settings. +* Formatting and parsing depend on cultures installed on the server; see [Culture][] and [Date and Time Formatting][]. ## See Also ### Related Concepts -TODO +* [Date and Time Formatting][] — standard and custom format templates, ISO 8601, and operating system settings +* [Culture][] — how regional settings affect date and time format and parse patterns +* [Implicit Casting][] — automatic conversion from [DateTime][] to [DateTimeOffset][] +* [Working with Enums][] — [DayOfWeek][] and [DateTimeComponentType][] ### Related Data Types -TODO +* [DateTime][] +* [DateTimeOffset][] +* [TimeSpan][] +* [TimePeriod][] +* [DayOfWeek][] +* [DateTimeComponentType][] ### Related Blocks -TODO +* [Get Current Date Time][] +* [Get Date Time Component][] +* [Is Date Time Equal][] +* [Is Date Time Before][] +* [Is Date Time After][] +* [Is Date Time Between][] +* [Add Time Period][] +* [Subtract Time Period][] +* [Get Time Period Between Date Times][] +* [Convert Text To DateTime][] +* [Convert Date Time To Text][] ### External Documentation -TODO +* [System.DateTime][] +* [System.DateTimeOffset][] +* [System.TimeSpan][] +* [Date and time values and their use in .NET](https://learn.microsoft.com/en-us/dotnet/standard/datetime/) +* [Choosing between DateTime, DateTimeOffset, TimeSpan, and DateOnly](https://learn.microsoft.com/en-us/dotnet/standard/datetime/choosing-between-datetime) +* [Standard date and time format strings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings) +* [Custom date and time format strings](https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings) +* [Parsing dates and times in .NET](https://learn.microsoft.com/en-us/dotnet/standard/base-types/parsing-datetime) + +[Date & Time blocks]: {{< ref "../../../Blocks/date-and-time/_index.md" >}} + +[Get Current Date Time]: {{< ref "../../../Blocks/date-and-time/get-date-time/get-current-date-time-block.md" >}} +[Get Date Time Component]: {{< ref "../../../Blocks/date-and-time/get-date-time/get-date-time-component-block.md" >}} +[Is Date Time Equal]: {{< ref "../../../Blocks/date-and-time/is-date-time/is-date-time-equal-block.md" >}} +[Is Date Time Before]: {{< ref "../../../Blocks/date-and-time/is-date-time/is-date-time-before-block.md" >}} +[Is Date Time After]: {{< ref "../../../Blocks/date-and-time/is-date-time/is-date-time-after-block.md" >}} +[Is Date Time Between]: {{< ref "../../../Blocks/date-and-time/is-date-time/is-date-time-between-block.md" >}} +[Add Time Period]: {{< url path="Cortex.Reference.Blocks.DateAndTime.AddTimePeriod.AddTimePeriod.MainDoc" >}} +[Subtract Time Period]: {{< url path="Cortex.Reference.Blocks.DateAndTime.SubtractTimePeriod.SubtractTimePeriod.MainDoc" >}} +[Get Time Period Between Date Times]: {{< ref "../../../Blocks/date-and-time/get-time-period/get-time-period-between-date-times-block.md" >}} +[Convert Text To DateTime]: {{< url path="Cortex.Reference.Blocks.DateAndTime.ConvertDateTime.ConvertTextToDateTime.MainDoc" >}} +[Convert Date Time To Text]: {{< url path="Cortex.Reference.Blocks.DateAndTime.ConvertDateTime.ConvertDateTimeToText.MainDoc" >}} +[Wait For Duration]: {{< url path="Cortex.Reference.Blocks.Schedules.WaitFor.WaitForDuration.MainDoc" >}} + +[DateTime]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTime.MainDoc" >}} +[DateTimeOffset]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTimeOffset.MainDoc" >}} +[TimeSpan]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.TimeSpan.MainDoc" >}} +[TimePeriod]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.TimePeriod.MainDoc" >}} +[DayOfWeek]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DayOfWeek.MainDoc" >}} +[DateTimeComponentType]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTimeComponentType.MainDoc" >}} + +[Date and Time Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.MainDoc" >}} +[ISO 8601 Standard]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.ISO8601Standard" >}} +[Operating System Settings]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.OperatingSystemSettings" >}} + +[Culture]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.MainDoc" >}} +[Current Culture]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.CurrentCulture.MainDoc" >}} +[Invariant Culture]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.InvariantCulture.MainDoc" >}} +[CultureInfo]: {{< url path="Cortex.Reference.DataTypes.Text.CultureInfo.MainDoc" >}} + +[Implicit Casting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectCasting.ImplicitCast" >}} +[Working with Enums]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Enums.MainDoc" >}} +[enum]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Enums.MainDoc" >}} + +[System.DateTime]: {{< url path="MSDocs.DotNet.Api.System.DateTime.MainDoc" >}} +[System.DateTimeOffset]: {{< url path="MSDocs.DotNet.Api.System.DateTimeOffset.MainDoc" >}} +[System.TimeSpan]: {{< url path="MSDocs.DotNet.Api.System.TimeSpan.MainDoc" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/email/authentication.md b/content/en/docs/2026.3/Reference/Concepts/working-with/email/authentication.md index ec6522f86..a26b250bd 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/email/authentication.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/email/authentication.md @@ -1,20 +1,329 @@ --- title: "Authentication" linkTitle: "Authentication" -description: "Information regarding authentication when working with email" -weight: 1 +description: "How to configure authentication when sending email from flows, including Gmail app passwords, Gmail OAuth, and Microsoft 365 OAuth." +weight: 2 --- # {{% param title %}} -{{< workinprogress >}} +## Summary + +Mail servers require authentication before they accept outgoing messages. In {{% ctx %}}, the send-email block you use determines which credentials data type to configure: + +| Block | Credentials data type | Authentication | +| --- | --- | --- | +| [Send Email Using SMTP Server][] | [UserCredentials][] in [BasicEmailSessionDetails][] | Username and password ([SASL][] negotiated with the server) | +| [Send Email Using Gmail][] | [UserCredentials][] or [GmailOAuthCertificateCredentials][] in [GmailSessionDetails][] | Basic (app password recommended) or OAuth (two-legged) | +| [Send Email Using Microsoft 365][] | [Microsoft365OAuthCredentials][] or [Microsoft365OAuthCertificateCredentials][] | OAuth only (client credentials or certificate) | + +Sensitive values such as [Password][], client secrets, and certificate passwords must be stored as [EncryptedText][]. Some credential properties may optionally use [EncryptableText][]. + +Unauthenticated [SMTP][] servers are not supported. For an overview of sessions, [SSL][]/[TLS][], and connection reuse, see [What is Email?][]. ## Overview +The sections below describe how to prepare provider accounts and map registration details to {{% ctx %}} credential properties. After setup, pass the credentials to the appropriate send-email block as shown in the block reference pages. + ### Setting up an app password for a Gmail account +Use an app password when [Send Email Using Gmail][] authenticates with [UserCredentials][] inside [GmailSessionDetails][]. + +{{% alert title="Note" %}} +Gmail does not allow most accounts to send mail with a normal account password. Sending with a username and password currently works only for Google Workspace accounts that still have access enabled for {{< ahref path="Google.Authentication.LessSecureApps.MainDoc" title="less secure apps" >}}. For other accounts, use an **app password** or [OAuth](#setting-up-an-app-password-for-a-gmail-account). +{{% /alert %}} + +#### Prerequisites + +* A [Gmail][] or Google Workspace account that will send mail. +* Two-step verification enabled on the Google account (required before Google allows app passwords). + +#### Create an app password + +1. Sign in to the Google account and open **Google Account** > **Security**. +1. Under **How you sign in to Google**, confirm **2-Step Verification** is turned on. +1. Open **App passwords** (this option appears only after 2-step verification is enabled). +1. Select **Mail** (or the app type your administrator recommends) and the device or custom name for {{% ctx %}}. +1. Click **Create** and copy the generated password. Google shows it only once. + +For current Google guidance, see [Sign in with app passwords][]. + +#### Use the app password in {{% ctx %}} + +1. Create [GmailSessionDetails][] with [UserCredentials][]: + * [Username][] — the full email address of the sending account (for example `sender@gmail.com`). This value may optionally be encrypted. + * [Password][] — the app password from the steps above, stored as [EncryptedText][]. +1. Set [ServerDetails][] for [Gmail][] [SMTP][] (typically host `smtp.gmail.com`, port `465` with [UseSsl][] `true`, or port `587` with [UseSsl][] `false`). +1. Pass the session details to [Send Email Using Gmail][]. + +Example expression: + +```csharp +new GmailSessionDetails( + serverDetails: new ServerDetails("smtp.gmail.com", 465, true), + credentials: new UserCredentials("sender@gmail.com", "encryptedAppPassword")) +``` + +The [Domain][] property on [UserCredentials][] is ignored by [Send Email Using Gmail][]. + ### Setting up a Gmail account for OAuth authentication +Use OAuth when [Send Email Using Gmail][] authenticates with [GmailOAuthCertificateCredentials][] inside [GmailSessionDetails][]. The flow is often called **two-legged OAuth**: a service account obtains access without an interactive user sign-in. + +{{% alert title="Note" %}} +OAuth for {{< ahref path="Cortex.Reference.Glossary.F-J.Gmail" title="Gmail" >}} requires a Google Workspace domain. Consumer `@gmail.com` accounts cannot use this mechanism. +{{% /alert %}} + +#### Prerequisites + +Before OAuth works in a flow: + +* A client application (Google Cloud project) must be set up in the Google Workspace. +* A **service account** must be created for that project. +* A private key (`.p12` file) must be generated for the service account. +* A Google Workspace administrator must grant the client application **domain-wide delegation** for the scope `https://mail.google.com/`. + +#### Configure Google Cloud and Workspace + +1. In [Google Cloud Console][], create or select a project for mail integration. +1. Enable the **Gmail API** for the project. +1. Create a **service account** ( **IAM & Admin** > **Service Accounts** > **Create service account**). +1. On the service account, open **Keys** > **Add key** > **Create new key**, choose **P12**, and download the key file. Note the key password Google provides. +1. Copy the service account **Client ID** (numeric); domain-wide delegation uses this value. +1. In Google Workspace Admin console, open **Security** > **Access and data control** > **API controls** > **Domain-wide delegation** > **Manage Domain Wide Delegation**. +1. Add a new API client with: + * **Client ID** — the service account client ID from step 5. + * **OAuth scopes** — `https://mail.google.com/` +1. Save the delegation entry. + +Place the `.p12` file on the **server that executes the flow** (or on a UNC path that server can read). See [File & Folder Paths][] and [Certificate Files][] on [Send Email Using Gmail][]. + +#### Map registration values to GmailOAuthCertificateCredentials + +After setup, map Google registration data to [GmailOAuthCertificateCredentials][] properties: + +| Property | Source | +| --- | --- | +| [CertificatePath][] | Path to the downloaded `.p12` file on the execution server | +| [CertificatePassword][] | Password for the `.p12` file ([EncryptedText][]) | +| [FromAddress][] | Email address of the mailbox that sends mail (must belong to the Workspace domain) | +| [ClientId][] | Client ID of the service account (may optionally be encrypted) | + +Example expression: + +```csharp +new GmailSessionDetails( + serverDetails: new ServerDetails("smtp.gmail.com", 465, true), + credentials: new GmailOAuthCertificateCredentials( + certificatePath: @"C:\Certificates\gmail-service-account.p12", + certificatePassword: "encryptedCertificatePassword", + fromAddress: "sender@example.com", + clientId: "123456789012345678901")) +``` + +For property details and validation errors, see [GmailOAuthCertificateCredentials][] and [EmailSessionException][]. + ### Setting up an Outlook account for OAuth authentication using client credentials +Use client credentials when [Send Email Using Microsoft 365][] receives [Microsoft365OAuthCredentials][] as its [Credentials][] property. The block retrieves OAuth access tokens automatically and connects to the [Outlook][] [SMTP][] server. + +{{% alert title="Note" %}} +Register and configure the application in Microsoft Entra ID (Azure AD) before using these credentials in a flow. An account with permission to create app registrations and grant admin consent is required. +{{% /alert %}} + +#### Register an application in Microsoft Entra ID + +1. Sign in to [Microsoft Entra admin center][] and open **Identity** > **Applications** > **App registrations** > **New registration**. +1. Enter a name for the application and select the supported account type for your tenant. +1. Click **Register** and note: + * **Application (client) ID** — maps to [ClientId M365][]. + * **Directory (tenant) ID** — maps to [TenantId M365][]. +1. Open **Certificates & secrets** > **Client secrets** > **New client secret**. Copy the secret **Value** when shown; it maps to [ClientSecret M365][] ([EncryptedText][]). +1. Open **API permissions** > **Add a permission** > **Microsoft Graph** > **Application permissions**. +1. Add the permissions required for your tenant to send mail as the application (commonly **Mail.Send**). Grant **admin consent** for the tenant. +1. Open **Enterprise applications**, locate the registered app, open **Properties**, and set **Assignment required?** according to your organization's policy if users must be assigned to the app. + +#### Identify the sending user Object ID + +[ObjectId M365][] is the Microsoft Entra **Object ID** of the user whose mailbox sends mail. To find it: + +1. In Microsoft Entra admin center, open **Identity** > **Users** > **All users**. +1. Select the sending user and copy **Object ID**. + +The [From][] address on the [EmailMessage][] must be an address the application is permitted to send as. If the application lacks permission, [Send Email Using Microsoft 365][] throws a [ServiceException][]. + +#### Use Microsoft365OAuthCredentials in {{% ctx %}} + +Create [Microsoft365OAuthCredentials][] with the values collected above: + +| Property | Source | +| --- | --- | +| [ClientId M365][] | Application (client) ID | +| [ClientSecret M365][] | Client secret value ([EncryptedText][]) | +| [TenantId M365][] | Directory (tenant) ID | +| [ObjectId M365][] | Object ID of the sending user | + +Example expression: + +```csharp +new Microsoft365OAuthCredentials( + clientId: "clientId", + clientSecret: "encryptedClientSecret", + tenantId: "tenantId", + objectId: "objectId") +``` + +Use a [variable][] for [Credentials][] when the same token should be reused across multiple [Send Email Using Microsoft 365][] blocks. See [Microsoft365OAuthCredentials][] for full property documentation. + +To send using basic username and password against a non-Microsoft host, use [Send Email Using SMTP Server][] instead. + ### Setting up an Outlook account for OAuth authentication using certificate credentials + +Use certificate credentials when [Send Email Using Microsoft 365][] receives [Microsoft365OAuthCertificateCredentials][] as its [Credentials][] property. This replaces the client secret with an X.509 certificate uploaded to the app registration. + +#### Register an application and upload a certificate + +1. Complete app registration in Microsoft Entra ID as in [client credentials setup][], through API permissions and admin consent. +1. Instead of (or in addition to) a client secret, open **Certificates & secrets** > **Certificates** > **Upload certificate**. +1. Upload a `.cer` or `.pfx` certificate and note any password used when exporting the private key. +1. Record **Application (client) ID**, **Directory (tenant) ID**, and the sending user's **Object ID** as for client credentials. + +#### Map registration values to Microsoft365OAuthCertificateCredentials + +| Property | Source | +| --- | --- | +| [CertificatePath M365][] | Path to the `.pfx` (or appropriate) certificate file on the execution server | +| [CertificatePassword M365][] | Password for the certificate file ([EncryptedText][]) | +| [ClientId M365 cert][] | Application (client) ID | +| [TenantId M365 cert][] | Directory (tenant) ID | +| [ObjectId M365 cert][] | Object ID of the sending user | + +Example expression: + +```csharp +new Microsoft365OAuthCertificateCredentials( + certificatePath: @"C:\Certificates\outlook-app.pfx", + certificatePassword: "encryptedCertificatePassword", + clientId: "clientId", + tenantId: "tenantId", + objectId: "objectId") +``` + +The certificate file must be readable from the server executing the flow. Invalid paths or passwords surface as [CryptographicException][] or [MsalServiceException][]; see [Send Email Using Microsoft 365][]. + +## Remarks + +### Encrypting secrets + +Store passwords, client secrets, and certificate passwords as [EncryptedText][] before using them in credential constructors or literals. See [EncryptedText][] for how to encrypt values in {{% ctx %}}. + +### Reusing credentials across blocks + +* [Send Email Using Gmail][] and [Send Email Using SMTP Server][] can reuse open [SMTP][] sessions when session details are supplied through a [variable][] and [Close Session][] is `false`. +* [Send Email Using Microsoft 365][] caches OAuth access tokens on the [Credentials][] object. Using a [variable][] for credentials allows token reuse across multiple blocks in the same flow. + +### Known limitations + +* **Unauthenticated SMTP** — [BasicEmailSessionDetails][] and [Send Email Using SMTP Server][] require credentials; anonymous SMTP is not supported. +* **Gmail username and password** — Plain account passwords are deprecated by Google; app passwords or OAuth are recommended. See [Less Secure Apps][]. +* **Gmail OAuth** — Requires Google Workspace and domain-wide delegation; consumer Gmail accounts are not supported for [GmailOAuthCertificateCredentials][]. +* **Microsoft 365 OAuth only** — [Send Email Using Microsoft 365][] does not support [UserCredentials][]; use [Send Email Using SMTP Server][] for basic authentication to other hosts. + +## See Also + +### Related concepts + +* [What is Email?][] +* [File & Folder Paths][] + +### Related data types + +* [BasicEmailSessionDetails][] +* [GmailSessionDetails][] +* [GmailOAuthCertificateCredentials][] +* [Microsoft365OAuthCredentials][] +* [Microsoft365OAuthCertificateCredentials][] +* [UserCredentials][] +* [EncryptedText][] + +### Related blocks + +* [Send Email Using SMTP Server][] +* [Send Email Using Gmail][] +* [Send Email Using Microsoft 365][] + +### Related exceptions + +* [EmailSessionException][] +* [MsalServiceException][] +* [ServiceException][] + +### External documentation + +* [Less Secure Apps][] +* [Sign in with app passwords][] +* [Google Cloud Console][] +* [Microsoft Entra admin center][] + +[client credentials setup]: {{< ref "#setting-up-an-outlook-account-for-oauth-authentication-using-client-credentials" >}} +[What is Email?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Email.WhatIsEmail.MainDoc" >}} + +[Send Email Using SMTP Server]: {{< url path="Cortex.Reference.Blocks.Email.SendEmail.SendEmailUsingSmtpServer.MainDoc" >}} +[Send Email Using Gmail]: {{< url path="Cortex.Reference.Blocks.GoogleWorkspace.Gmail.SendEmail.SendEmailUsingGmail.MainDoc" >}} +[Send Email Using Microsoft 365]: {{< url path="Cortex.Reference.Blocks.Microsoft365.Outlook.SendEmail.SendEmailUsingMicrosoft365.MainDoc" >}} +[Certificate Files]: {{< url path="Cortex.Reference.Blocks.GoogleWorkspace.Gmail.SendEmail.SendEmailUsingGmail.MainDoc" >}}#certificate-files +[Close Session]: {{< url path="Cortex.Reference.Blocks.Email.SendEmail.SendEmailUsingSmtpServer.CloseSessionProperty" >}} + +[File & Folder Paths]: {{< url path="Cortex.Reference.Concepts.WorkingWith.FilesAndFolders.Paths.MainDoc" >}} +[variable]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} + +[BasicEmailSessionDetails]: {{< url path="Cortex.Reference.DataTypes.Email.BasicEmailSessionDetails.MainDoc" >}} +[GmailSessionDetails]: {{< url path="Cortex.Reference.DataTypes.GoogleWorkspace.Gmail.GmailSessionDetails.MainDoc" >}} +[GmailOAuthCertificateCredentials]: {{< url path="Cortex.Reference.DataTypes.GoogleWorkspace.Gmail.Authentication.OAuth.GmailOAuthCertificateCredentials.MainDoc" >}} +[Microsoft365OAuthCredentials]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCredentials.MainDoc" >}} +[Microsoft365OAuthCertificateCredentials]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCertificateCredentials.MainDoc" >}} + +[CertificatePath]: {{< url path="Cortex.Reference.DataTypes.GoogleWorkspace.Gmail.Authentication.OAuth.GmailOAuthCertificateCredentials.CertificatePath" >}} +[CertificatePassword]: {{< url path="Cortex.Reference.DataTypes.GoogleWorkspace.Gmail.Authentication.OAuth.GmailOAuthCertificateCredentials.CertificatePassword" >}} +[FromAddress]: {{< url path="Cortex.Reference.DataTypes.GoogleWorkspace.Gmail.Authentication.OAuth.GmailOAuthCertificateCredentials.FromAddress" >}} +[ClientId]: {{< url path="Cortex.Reference.DataTypes.GoogleWorkspace.Gmail.Authentication.OAuth.GmailOAuthCertificateCredentials.ClientId" >}} + +[ClientId M365]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCredentials.ClientId" >}} +[ClientSecret M365]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCredentials.ClientSecret" >}} +[TenantId M365]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCredentials.TenantId" >}} +[ObjectId M365]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCredentials.ObjectId" >}} +[ClientId M365 cert]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCertificateCredentials.ClientId" >}} +[TenantId M365 cert]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCertificateCredentials.TenantId" >}} +[ObjectId M365 cert]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCertificateCredentials.ObjectId" >}} +[CertificatePath M365]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCertificateCredentials.CertificatePath" >}} +[CertificatePassword M365]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCertificateCredentials.CertificatePassword" >}} + +[EmailMessage]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.MainDoc" >}} +[From]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.From" >}} +[Credentials]: {{< url path="Cortex.Reference.Blocks.Microsoft365.Outlook.SendEmail.SendEmailUsingMicrosoft365.MainDoc" >}}#credentials + +[ServerDetails]: {{< url path="Cortex.Reference.DataTypes.SessionDetails.ServerDetails.MainDoc" >}} +[UseSsl]: {{< url path="Cortex.Reference.DataTypes.SessionDetails.ServerDetails.UseSsl" >}} +[UserCredentials]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.MainDoc" >}} +[Username]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.Username" >}} +[Password]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.Password" >}} +[Domain]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.Domain" >}} +[EncryptedText]: {{< url path="Cortex.Reference.DataTypes.Text.EncryptedText.MainDoc" >}} +[EncryptableText]: {{< url path="Cortex.Reference.DataTypes.Text.EncryptableText.MainDoc" >}} + +[EmailSessionException]: {{< url path="Cortex.Reference.Exceptions.Email.EmailSessionException.MainDoc" >}} +[MsalServiceException]: {{< url path="MSDocs.DotNet.Api.Microsoft.Identity.Client.MsalServiceException.MainDoc" >}} +[ServiceException]: {{< url path="MSDocs.DotNet.Api.Microsoft.Graph.ServiceException.MainDoc" >}} +[CryptographicException]: {{< url path="MSDocs.DotNet.Api.System.Security.Cryptography.CryptographicException.MainDoc" >}} + +[SMTP]: {{< url path="Cortex.Reference.Glossary.P-T.SMTP" >}} +[SASL]: {{< url path="Cortex.Reference.Glossary.P-T.SASL" >}} +[SSL]: {{< url path="Cortex.Reference.Glossary.P-T.SSL" >}} +[TLS]: {{< url path="Cortex.Reference.Glossary.P-T.TLS" >}} +[Gmail]: {{< url path="Cortex.Reference.Glossary.F-J.Gmail" >}} +[Outlook]: {{< url path="Cortex.Reference.Glossary.K-O.Outlook" >}} + +[Less Secure Apps]: {{< url path="Google.Authentication.LessSecureApps.MainDoc" >}} +[Sign in with app passwords]: https://support.google.com/accounts/answer/185833 +[Google Cloud Console]: https://console.cloud.google.com/ +[Microsoft Entra admin center]: https://entra.microsoft.com/ diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/email/what-is-email.md b/content/en/docs/2026.3/Reference/Concepts/working-with/email/what-is-email.md index cc8fedaaf..66ff04ff4 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/email/what-is-email.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/email/what-is-email.md @@ -1,68 +1,294 @@ --- title: "What is Email?" linkTitle: "What is Email?" -description: "Information regarding what email is." +description: "How flows send email over SMTP, which data types and blocks to use, and how mail-server sessions, authentication, and attachments work." weight: 1 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: - -- SMTP (Sending) - - Attachments (best practices): - - where should attachments be in relation to the server the flow is executing on - - should we link to the file path stuff (working with files and folders)? -- IMAP (Retreiving) - - Mailboxes - - Folders: - - Deleted? - - Emails: - - Attachments - - Status: - - Read - - Unread - - Priority - - Body Format - - Flagged -- SSL/TLS -- Authentication: - - Unauthenticated Servers - - SASL - - SMTP Server - - Gmail? - - OAuth: - - Gmail OAuth - - Microsoft365 OAuth - -## Managing Connections to a Mail Server - -TODO: +In {{% ctx %}}, **email** support focuses on **sending** messages from a [flow][] to a mail server over [SMTP][]. Flows do not talk to mail servers directly; they use send-email blocks with two pieces of configuration: + +| Piece | Data type | Purpose | +| --- | --- | --- | +| Session | [BasicEmailSessionDetails][], [GmailSessionDetails][], or provider-specific credentials | How to connect and authenticate to the mail server | +| Message | [EmailMessage][] | What to send (recipients, subject, body, attachments, and related options) | + +This model is similar to using [MailKit][] in C#, where an [SmtpClient][] opens a session to a mail server and sends a [MimeMessage][] built from addresses, headers, and content. {{% ctx %}} wraps that pattern in platform data types and blocks so connection handling, authentication, and message construction work consistently across supported providers. + +| Term | Meaning | +| --- | --- | +| [SMTP][] | Internet protocol used to **send** email between mail servers and clients | +| [IMAP][] | Internet protocol used by email clients to **retrieve** email from a mail server | +| [EmailMessage][] | The message to send (recipients, subject, body, attachments, and so on) | +| Session details | Configuration for opening and maintaining a connection to a mail server | +| [Close Session][] | Whether the block closes the mail-server session after sending | + +{{% ctx %}} currently provides blocks for sending email. Retrieving email over [IMAP][] is not supported in this release. + +## Sending email + +Mail servers use [SMTP][] to accept outgoing messages. In {{% ctx %}}, a [flow][] sends email by passing an [EmailMessage][] and session configuration to one of the send-email blocks below. + +| Block | Session / credentials | Typical use | +| --- | --- | --- | +| [Send Email Using SMTP Server][] | [BasicEmailSessionDetails][] with [UserCredentials][] | Any [SMTP][] server that supports username and password authentication | +| [Send Email Using Gmail][] | [GmailSessionDetails][] with [UserCredentials][] or [GmailOAuthCertificateCredentials][] | [Gmail][] and Google Workspace accounts | +| [Send Email Using Microsoft 365][] | [Microsoft365OAuthCredentials][] or [Microsoft365OAuthCertificateCredentials][] | [Outlook][] / Microsoft 365 accounts (OAuth only) | + +For step-by-step examples, property defaults, and block-specific exceptions, see the block pages listed above. + +### Email messages + +An [EmailMessage][] holds everything the block needs to construct and send a message: + +| Property | Data type | Purpose | +| --- | --- | --- | +| [To][] | [IList][]<[EmailAddress][]> | Primary recipients (required) | +| [From][] | [EmailAddress][] | Sender (required) | +| [Cc][] | [IList][]<[EmailAddress][]> | [CC][Cc] recipients | +| [Bcc][] | [IList][]<[EmailAddress][]> | [BCC][Bcc] recipients | +| [Priority][] | [EmailMessagePriority][] | [Normal][], [NonUrgent][], or [Urgent][] | +| [Subject][] | [String][] | Message subject | +| [BodyFormat][] | [EmailMessageBodyFormat][] | [Text][] or [HTML][] body | +| [Body][] | [String][] | Message body | +| [Attachments][] | [IList][]<[String][]> | File paths to attach | + +Each [EmailAddress][] has an [Address][] (required, must follow [RFC 5321][]) and an optional display [Name][]. + +When [Priority][] or [BodyFormat][] is `null` on an [EmailMessage][] created with a constructor, send blocks treat the message as [Normal][] priority with a [Text][] body. How priority and HTML display in the recipient's client depends on that client; see the remarks on [Send Email Using SMTP Server][] for examples. + +### Attachments + +Attachments are file paths in the [Attachments][] property. Each path must point to a file that the **server executing the flow** can read. Supported path formats include absolute, relative, and UNC paths; see [File & Folder Paths][]. + +Best practices: + +* Store attachment files on the execution server (or on a UNC share it can access), not only on a designer machine. +* Escape backslashes in paths (for example `@"C:\Attachments\file.pdf"` or `"C:\\Attachments\\file.pdf"`). +* Keep the combined attachment size within the provider limit (for example `25 MB` for [Gmail][] and `20 MB` for [Outlook][]). + +Invalid, missing, or inaccessible paths throw exceptions at runtime. For the full list of attachment validation rules, see [Send Email Using SMTP Server][]. + +## SSL and TLS + +Session configuration includes [ServerDetails][] with [Host][], [Port][], and [UseSsl][]. The [UseSsl][] value must match how the target server expects [SSL][]/[TLS][] to be negotiated: + +| Connection style | [UseSsl][] | Typical ports | +| --- | --- | --- | +| [SSL][]/[TLS][] from the start of the connection | `true` | `465` | +| [TLS][] via STARTTLS after connecting in plain text | `false` | `25`, `587` | + +If [UseSsl][] does not match the server and port, the block throws an [EmailSessionException][] with error code [SslRequired][] or [SslUnsupported][]. Certificate trust, expiry, and anti-virus TLS interception can also cause connection failures; see [EmailSessionException][]. + +Supported [Host][] formats include fully qualified domain names, machine names, IP addresses, and `localhost`. See [Send Email Using SMTP Server][] for details. + +## Authentication + +Mail servers require authentication before they accept messages. {{% ctx %}} supports the mechanisms below; the exact option depends on the block and provider. + +| Mechanism | Used with | Notes | +| --- | --- | --- | +| Username and password ([UserCredentials][]) | [Send Email Using SMTP Server][], [Send Email Using Gmail][] | [Password][] must be [EncryptedText][]. [Username][] may optionally be encrypted. | +| App password | [Send Email Using Gmail][] | Recommended over a plain account password; see [Setting up an app password for a Gmail account][] | +| OAuth (certificate) | [Send Email Using Gmail][] | [GmailOAuthCertificateCredentials][]; see [Setting up a Gmail account for OAuth authentication][] | +| OAuth (client credentials) | [Send Email Using Microsoft 365][] | [Microsoft365OAuthCredentials][]; see [Setting up an Outlook account for OAuth authentication using client credentials][] | +| OAuth (certificate) | [Send Email Using Microsoft 365][] | [Microsoft365OAuthCertificateCredentials][]; see [Setting up an Outlook account for OAuth authentication using certificate credentials][] | + +For [Send Email Using SMTP Server][] and [Send Email Using Gmail][], the [SASL][] mechanism is negotiated with the server based on what the server supports. [Send Email Using Microsoft 365][] supports OAuth only; use [Send Email Using SMTP Server][] for basic authentication to other hosts. + +Unauthenticated [SMTP][] servers are **not** supported. [BasicEmailSessionDetails][] and the SMTP send blocks require credentials today. + +For setup procedures and provider-specific guidance, see [Authentication][]. + +## Managing connections to a mail server + +Send-email blocks create, open, reuse, and close mail-server sessions based on session details, the [Close Session][] property, and how session details are supplied. + +### Session details + +Session details hold the [ServerDetails][] and credentials required to connect. The type must match the block: + +* [BasicEmailSessionDetails][] — generic [SMTP][] servers +* [GmailSessionDetails][] — [Gmail][] [SMTP][] +* [Microsoft365OAuthCredentials][] or [Microsoft365OAuthCertificateCredentials][] — passed directly to [Send Email Using Microsoft 365][] + +Create session details in the [Expression Editor][] (for example `new BasicEmailSessionDetails(serverDetails: new ServerDetails("smtp.gmail.com", 465, true), credentials: new UserCredentials("sender@gmail.com", "encryptedPassword"))`) or pass a [variable][] that already holds the session object. + +### Opening sessions {#opening-sessions} + +[Send Email Using SMTP Server][] and [Send Email Using Gmail][] apply these rules when they run: + +* If no session exists for the supplied session details, a new session is created, opened, and used. +* If a session exists but is closed, it is opened and used. +* If a session exists and is already open, it is reused. + +When session details are supplied through a [variable][], the same session can stay open across multiple send blocks if [Close Session][] is `false`. Reusing a session avoids the cost of opening a new connection on every block execution. + +When session details are supplied through a [literal][] or [expression][], a new session is created each time the block runs. If [Close Session][] is `false`, that session is still closed automatically after the block finishes and before the flow ends—it cannot be shared with later blocks the way a variable-backed session can. + +[Send Email Using Microsoft 365][] opens a connection per execution and does not expose a reusable session object in the same way. + +For [SSL][] connections, the protocol version is negotiated with the server. For [SASL][], the authentication mechanism is negotiated with the server. + +### Closing sessions {#closing-sessions} + +Set [Close Session][] to `true` to close the session immediately after the message is sent. + +When [Close Session][] is `false`: + +* If session details were supplied through a [variable][], the session stays open until the variable goes out of [scope][] or the flow ends, whichever comes first. +* If session details were supplied through a [literal][] or [expression][], the session is closed automatically after the block finishes and before the flow ends. + +For full property defaults, exceptions, and examples, see [Send Email Using SMTP Server][] and [Send Email Using Gmail][]. ## Remarks -### Known Limitations +### Known limitations + +#### Sending only + +{{% ctx %}} supports **sending** email over [SMTP][] in this release. There are no blocks or data types for retrieving email over [IMAP][] (mailboxes, folders, read/unread status, or downloaded attachments). + +#### Unauthenticated mail servers + +Unauthenticated [SMTP][] servers are not supported. [BasicEmailSessionDetails][] and [Send Email Using SMTP Server][] require credentials. This limitation may be removed in a future release. + +#### Provider and certificate errors + +Some [EmailSessionException][] conditions (expired or untrusted certificates, anti-virus TLS interception, unavailable certificate revocation lists) share the same error code as [SslUnsupported][]. See [EmailSessionErrorCode Limitations][]. + +#### Attachment and address validation -TODO +Send blocks validate attachment paths and [EmailAddress][] values at runtime. Invalid addresses, oversize attachments, and file access errors surface as .NET or [MimeKit][MimeKit SmtpCommandException] exceptions; see each block's **Exceptions** section. ## See Also -### Related Concepts +### Related concepts + +* [Authentication][] +* [What is a Variable?][] +* [What is a Scope?][] +* [File & Folder Paths][] + +### Related data types + +* [EmailMessage][] +* [EmailAddress][] +* [EmailMessagePriority][] +* [EmailMessageBodyFormat][] +* [BasicEmailSessionDetails][] +* [GmailSessionDetails][] +* [GmailOAuthCertificateCredentials][] +* [Microsoft365OAuthCredentials][] +* [Microsoft365OAuthCertificateCredentials][] +* [ServerDetails][] +* [UserCredentials][] +* [EncryptedText][] +* [EmailSessionErrorCode][] + +### Related blocks + +* [Send Email Using SMTP Server][] +* [Send Email Using Gmail][] +* [Send Email Using Microsoft 365][] + +### Related exceptions + +* [EmailSessionException][] + +### External documentation + +* [RFC 5321 — Simple Mail Transfer Protocol][RFC 5321] +* [MailKit documentation][MailKit] +* [MimeKit SmtpCommandException][MimeKit SmtpCommandException] +* [SmtpClient Class (MailKit)][SmtpClient] +* [MimeMessage Class (MimeKit)][MimeMessage] + +[flow]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Flows.WhatIsAFlow.MainDoc" >}} + +[Send Email Using SMTP Server]: {{< url path="Cortex.Reference.Blocks.Email.SendEmail.SendEmailUsingSmtpServer.MainDoc" >}} +[Send Email Using Gmail]: {{< url path="Cortex.Reference.Blocks.GoogleWorkspace.Gmail.SendEmail.SendEmailUsingGmail.MainDoc" >}} +[Send Email Using Microsoft 365]: {{< url path="Cortex.Reference.Blocks.Microsoft365.Outlook.SendEmail.SendEmailUsingMicrosoft365.MainDoc" >}} + +[Authentication]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Email.Authentication.MainDoc" >}} +[Setting up an app password for a Gmail account]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Email.Authentication.SettingUpAppPassword" >}} +[Setting up a Gmail account for OAuth authentication]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Email.Authentication.SettingUpOAuthGmail" >}} +[Setting up an Outlook account for OAuth authentication using client credentials]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Email.Authentication.SettingUpClientCredentialsOutlook" >}} +[Setting up an Outlook account for OAuth authentication using certificate credentials]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Email.Authentication.SettingUpCertificateCredentialsOutlook" >}} + +[File & Folder Paths]: {{< url path="Cortex.Reference.Concepts.WorkingWith.FilesAndFolders.Paths.MainDoc" >}} +[What is a Variable?]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[What is a Scope?]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Scopes.WhatIsAScope.MainDoc" >}} +[scope]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Scopes.WhatIsAScope.MainDoc" >}} +[variable]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[literal]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.LiteralEditor.MainDoc" >}} +[expression]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} + +[EmailMessage]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.MainDoc" >}} +[To]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.To" >}} +[From]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.From" >}} +[Cc]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.Cc" >}} +[Bcc]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.Bcc" >}} +[Priority]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.Priority" >}} +[Subject]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.Subject" >}} +[BodyFormat]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.BodyFormat" >}} +[Body]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.Body" >}} +[Attachments]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessage.Attachments" >}} + +[EmailAddress]: {{< url path="Cortex.Reference.DataTypes.Email.EmailAddress.MainDoc" >}} +[Address]: {{< url path="Cortex.Reference.DataTypes.Email.EmailAddress.Address" >}} +[Name]: {{< url path="Cortex.Reference.DataTypes.Email.EmailAddress.Name" >}} + +[EmailMessagePriority]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessagePriority.MainDoc" >}} +[Normal]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessagePriority.Normal" >}} +[NonUrgent]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessagePriority.NonUrgent" >}} +[Urgent]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessagePriority.Urgent" >}} + +[EmailMessageBodyFormat]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessageBodyFormat.MainDoc" >}} +[Text]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessageBodyFormat.Text" >}} +[HTML]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessageBodyFormat.HTML" >}} + +[BasicEmailSessionDetails]: {{< url path="Cortex.Reference.DataTypes.Email.BasicEmailSessionDetails.MainDoc" >}} +[GmailSessionDetails]: {{< url path="Cortex.Reference.DataTypes.GoogleWorkspace.Gmail.GmailSessionDetails.MainDoc" >}} +[GmailOAuthCertificateCredentials]: {{< url path="Cortex.Reference.DataTypes.GoogleWorkspace.Gmail.Authentication.OAuth.GmailOAuthCertificateCredentials.MainDoc" >}} +[Microsoft365OAuthCredentials]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCredentials.MainDoc" >}} +[Microsoft365OAuthCertificateCredentials]: {{< url path="Cortex.Reference.DataTypes.Microsoft365.Authentication.OAuth.Microsoft365OAuthCertificateCredentials.MainDoc" >}} -TODO +[ServerDetails]: {{< url path="Cortex.Reference.DataTypes.SessionDetails.ServerDetails.MainDoc" >}} +[Host]: {{< url path="Cortex.Reference.DataTypes.SessionDetails.ServerDetails.Host" >}} +[Port]: {{< url path="Cortex.Reference.DataTypes.SessionDetails.ServerDetails.Port" >}} +[UseSsl]: {{< url path="Cortex.Reference.DataTypes.SessionDetails.ServerDetails.UseSsl" >}} -### Related Data Types +[UserCredentials]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.MainDoc" >}} +[Username]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.Username" >}} +[Password]: {{< url path="Cortex.Reference.DataTypes.Credentials.UserCredentials.Password" >}} +[EncryptedText]: {{< url path="Cortex.Reference.DataTypes.Text.EncryptedText.MainDoc" >}} -TODO +[IList]: {{< url path="Cortex.Reference.DataTypes.Collections.IList.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} -### Related Blocks +[EmailSessionException]: {{< url path="Cortex.Reference.Exceptions.Email.EmailSessionException.MainDoc" >}} +[SslRequired]: {{< url path="Cortex.Reference.Exceptions.Email.EmailSessionException.SslRequired" >}} +[SslUnsupported]: {{< url path="Cortex.Reference.Exceptions.Email.EmailSessionException.SslUnsupported" >}} +[EmailSessionErrorCode]: {{< url path="Cortex.Reference.DataTypes.Email.EmailSessionErrorCode.MainDoc" >}} +[EmailSessionErrorCode Limitations]: {{< url path="Cortex.Reference.DataTypes.Email.EmailSessionErrorCode.Limitations" >}} -TODO +[Close Session]: {{< url path="Cortex.Reference.Blocks.Email.SendEmail.SendEmailUsingSmtpServer.CloseSessionProperty" >}} -### External Documentation +[SMTP]: {{< url path="Cortex.Reference.Glossary.P-T.SMTP" >}} +[IMAP]: {{< url path="Cortex.Reference.Glossary.F-J.IMAP" >}} +[SASL]: {{< url path="Cortex.Reference.Glossary.P-T.SASL" >}} +[SSL]: {{< url path="Cortex.Reference.Glossary.P-T.SSL" >}} +[TLS]: {{< url path="Cortex.Reference.Glossary.P-T.TLS" >}} +[Gmail]: {{< url path="Cortex.Reference.Glossary.F-J.Gmail" >}} +[Outlook]: {{< url path="Cortex.Reference.Glossary.K-O.Outlook" >}} +[RFC 5321]: {{< url path="IETF.Email.RFC5321" >}} -TODO +[MailKit]: http://www.mimekit.net/docs/html/N_MailKit.htm +[SmtpClient]: http://www.mimekit.net/docs/html/T_MailKit_Net_Smtp_SmtpClient.htm +[MimeMessage]: http://www.mimekit.net/docs/html/T_MimeKit_MimeMessage.htm +[MimeKit SmtpCommandException]: {{< url path="MimeKit.Docs.SmtpCommandException" >}} diff --git a/content/en/docs/2026.3/Reference/Concepts/working-with/enums/what-is-an-enum.md b/content/en/docs/2026.3/Reference/Concepts/working-with/enums/what-is-an-enum.md index e81332ae9..d7defa677 100644 --- a/content/en/docs/2026.3/Reference/Concepts/working-with/enums/what-is-an-enum.md +++ b/content/en/docs/2026.3/Reference/Concepts/working-with/enums/what-is-an-enum.md @@ -1,60 +1,239 @@ --- title: "What is an Enum?" linkTitle: "What is an Enum?" -description: "Information regarding what an Enum is." +description: "Overview of enumeration (enum) data types in CORTEX, including how they are defined, edited in flows, cast, and displayed when debugging." weight: 1 --- # {{% param title %}} -{{< workinprogress >}} - ## Summary -TODO: - -- Anatomy of an Enum -- Enum literal editor -- Display in studio variable details viewer - - Default - - Overridden types - - Out of range ints cast (DayOfWeek)1000 -- Casting - dayofweek<->Int and unable to cast string - use parse - - Need to cast int to enum or enum to int if comparing -- Enum Types - - DateTimeComponentType - - SearchOptions - - Etc. -- Flagged enums - - Currently not possible to have combined values in the literal editor (is possible in expression) -- Enums names are not localised - e.g. DayOfWeek.Sunday cannot display Dimanche for french -- Links to microsoft docs - - https://learn.microsoft.com/en-us/dotnet/api/system.enum - - https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/enum - - probably not: - - https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/enums - - https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/enum +An **enum** (enumeration) is a [value type][] that defines a fixed set of named constants, each backed by an underlying numeric value (typically [Int32][]). In {{% ctx %}}, enums follow C# and .NET rules: they represent a single choice from a defined list of options and cannot be `null` unless wrapped in [Nullable<T>][]. + +Enum data types are used throughout {{% ctx %}} for block properties that accept a specific set of values — for example a day of the week ([DayOfWeek][]), a date and time component ([DateTimeComponentType][]), how text is matched ([SearchOptions][]), or an HTTP result code ([HttpStatusCode][]). Each enum is documented on its own data type page with a full list of members, default value, and supported casts. + +For how enums relate to other classifications of data types, see [What is a Data Type?][]. For casting between enums and other types, see [Object Casting][]. + +## Anatomy of an Enum + +Every enum documented in {{% ctx %}} shares the same structure. Each member has: + +| Part | Description | Example ([DayOfWeek][]) | +| --- | --- | --- | +| **Type name** | The enum data type | `DayOfWeek` | +| **Full name** | The .NET namespace and type | `System.DayOfWeek` | +| **Member name** | A [String][] identifier for one option | `Sunday` | +| **Member value** | The underlying [Int32][] integer | `0` | +| **Qualified member** | Type and member combined | `DayOfWeek.Sunday` | +| **Default value** | Value used when none is specified | `DayOfWeek.Sunday` (value `0`) | + +On each enum's data type page, the **Summary** table lists the category, full name, default value, **Can be used as** (implicit casts, usually [Object][] and [dynamic][]), and **Can be cast to** (explicit casts to numeric types). The **Values** section (where present) describes each member's name, numeric value, and purpose. + +Enums are [value types][value type]: assigning an enum from one [variable][] to another copies the value. Equality compares the underlying numeric value. See [Object Equality][]. + +## Enum Types in {{% ctx %}} + +{{% ctx %}} includes enums from the .NET base class library and enums defined for the platform. They are grouped by category under [All Data Types][]. Common examples include: + +| Category | Examples | Typical use | +| --- | --- | --- | +| Date & Time | [DayOfWeek][], [DateTimeComponentType][] | Extract or compare parts of a date and time | +| Text | [SearchOptions][], [StringSplitOptions][], [StringComparison][] | Control search, split, and comparison behaviour | +| HTTP | [HttpStatusCode][], [RequestVerb][] | Represent status codes and request methods | +| Email | [EmailMessagePriority][], [EmailMessageBodyFormat][] | Message options and formats | +| Data | [DataCommandErrorCode][], [OracleMappingType][] | Database command and mapping options | +| Files & Folders | [ContentOptions][] | Options for folder content blocks | +| Logs | [EventSeverity][] | Severity levels for events | + +For a complete list, browse [All Data Types][] or search for pages that reference [Working with Enums][]. + +## Working with Enums in Flows + +### Literal Editor + +When a block [Input][] property expects an enum, the [Literal Editor][] is usually available. The editor lists the defined members by name so you can pick a value without writing expression syntax. + +For example, a property of type [DayOfWeek][] accepts literals such as `Sunday` or `Monday` (the member name only, not `DayOfWeek.Sunday`). The same pattern applies to platform enums such as `LiteralText` for [SearchOptions][]. + +Whether the Literal Editor is available for a given enum is documented under **Property Editor Support** on that enum's data type page. + +### Expression Editor + +The [Expression Editor][] supports full enum syntax: + +* **Member access** — `DayOfWeek.Sunday`, `SearchOptions.Regex` +* **Numeric cast** — `(DayOfWeek)6` yields `DayOfWeek.Saturday` +* **Parse from text** — `(DayOfWeek)Enum.Parse(typeof(DayOfWeek), "Sunday")` +* **Create from number** — `(DayOfWeek)Enum.ToObject(typeof(DayOfWeek), 0)` +* **Convert to text** — `DayOfWeek.Sunday.ToString()` yields `"Sunday"` +* **Convert to number** — `(Int32)DayOfWeek.Sunday` yields `0` + +See [Enum expressions][] in the Expression Editor documentation and the **Create**, **Convert to Text**, and **Convert to a Number** tables on individual enum data type pages (for example [DayOfWeek][]). + +### Variable Details Viewer + +When [debugging a flow][], enum values are shown in the [Variables Viewer][]: + +* In the [Variables List][], a variable holding an enum at its native type is displayed as a [basic data type][] — typically the member name (for example `Sunday` for [DayOfWeek][]). +* In the [Variable Details Viewer][], the selected variable's name, type, and value are shown in JSON format. + +How the value appears in the Variable Details Viewer depends on the stored type: + +| Stored as | Typical display | Notes | +| --- | --- | --- | +| Native enum type (for example `DayOfWeek`) | Member name in the Variables List; JSON value in the Variable Details Viewer | Default behaviour for enum [variables][] | +| [Object][] or [dynamic][] holding an enum | May show the underlying numeric value or a generic object representation | The viewer reflects the runtime type of the stored value, not the original enum type | +| Out-of-range cast (for example `(DayOfWeek)1000`) | Numeric value `1000` with no matching member name | Valid in C# but does not correspond to a defined member; `ToString()` may return `"1000"` instead of a member name | + +To convert an enum to text or JSON explicitly, use `ToString()`, [Convert Object To Text][], or [Convert Object To Json][] — see the conversion tables on each enum's data type page. [Convert Object To Text][] returns the member name (for example `"Sunday"`); [Convert Object To Json][] returns the underlying number as text (for example `"0"`). + +## Casting and Converting Enums + +Enums integrate with {{% ctx %}} casting rules as described in [Object Casting][]. + +### Enum and numeric types + +Enum members are stored as integers. You can [explicitly cast][Explicit Casting] between an enum and its underlying numeric type: + +| Direction | Example | Result | +| --- | --- | --- | +| Number to enum | `(DayOfWeek)6` | `DayOfWeek.Saturday` | +| Enum to number | `(Int32)DayOfWeek.Saturday` | `6` | +| Variable to enum | `(DayOfWeek)($)Int` where `($)Int` is `6` | `DayOfWeek.Saturday` | + +When comparing an enum to a number, cast one side so both operands share a type — for example `(Int32)DayOfWeek.Sunday == 0` or `($)MyDay == (DayOfWeek)($)DayNumber`. + +Casting a number to an enum when no member has that underlying value (for example `(DayOfWeek)1000`) still produces a value at runtime in C#, but it does not match a named member. Prefer defined members or validate the result when using numeric casts. + +### Enum and text + +You cannot [explicitly cast][Explicit Casting] a [String][] directly to an enum. Parse the text instead: + +```csharp +(DayOfWeek)Enum.Parse(typeof(DayOfWeek), "Sunday") +``` + +To convert an enum to text, use `ToString()`, `Convert.ToString()`, or [Convert Object To Text][]. + +### Enum and Object or dynamic + +Any enum can be used where [Object][] or [dynamic][] is expected without a cast. To recover the enum type from [Object][], use an explicit cast — for example `(DayOfWeek)($)MyObject`. Values held as [dynamic][] usually do not require a cast for member access. + +## Flag Enums + +Some .NET enums are **flag enums**: members are assigned powers of two so multiple options can be combined with the bitwise OR operator (`|`). [StringSplitOptions][] is an example — `StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries` combines both options. + +In {{% ctx %}}: + +* Combined flag values can be created in the [Expression Editor][] using `|` (see [StringSplitOptions][]). +* The [Literal Editor][] typically allows only a **single** member to be selected, not a combined flag value. For combined flags, use an expression. + +For general C# guidance on flag enums, see [Enumeration types (C#)][MS Enumeration Types]. ## Remarks +### Enum names are not localised + +Enum member names are fixed C# identifiers (for example `DayOfWeek.Sunday`). They are not translated for different cultures — `DayOfWeek.Sunday` always displays as `Sunday`, not `Dimanche` for French. Localized display applies to date and time **formatting** (see [Date and Time Formatting][] and [Culture][]), not to enum member names. + ### Known Limitations -TODO +* The [Literal Editor][] does not support combined flag enum values; use the [Expression Editor][] instead (for example [StringSplitOptions][]). +* Enum member names are not localised. +* Casting a numeric value outside the defined members produces a value with no named member (for example `(DayOfWeek)1000`). +* You cannot cast [String][] to enum directly; use `Enum.Parse` or equivalent. +* Whether the Literal Editor is available varies by enum; see each data type page. ## See Also ### Related Concepts -TODO +* [What is a Data Type?][] +* [Object Casting][] +* [Object Equality][] +* [What is a Variable?][] +* [Enum expressions][] — expression syntax for enums +* [Date and Time Formatting][] — localized date and time text (distinct from enum names) ### Related Data Types -TODO +* [All Data Types][] +* [DayOfWeek][] +* [DateTimeComponentType][] +* [SearchOptions][] +* [StringSplitOptions][] +* [HttpStatusCode][] +* [Int32][] +* [String][] ### Related Blocks -TODO +* [Set Variable][] +* [Convert Object To Text][] +* [Convert Object To Json][] +* [Get Date Time Component][] ### External Documentation -TODO +* [System.Enum class][MS System Enum] +* [Enumeration types (C#)][MS Enumeration Types] +* [Instantiating an enumeration type][MS Instantiating Enum] +* [Formatting enumeration values][MS Formatting Enum] + +[value type]: {{< ref "#anatomy-of-an-enum" >}} + +[What is a Data Type?]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.WhatIsADataType.MainDoc" >}} +[basic data type]: {{< url path="Cortex.Reference.Concepts.Fundamentals.DataTypes.WhatIsADataType.MainDoc" >}} +[Object Casting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectCasting.MainDoc" >}} +[Explicit Casting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectCasting.ExplicitCast" >}} +[dynamic]: {{< url path="Cortex.Reference.DataTypes.All.dynamic.MainDoc" >}} +[Object Equality]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Objects.ObjectEquality.MainDoc" >}} +[What is a Variable?]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[variable]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.WhatIsAVariable.MainDoc" >}} +[variables]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Variables.MainDoc" >}} +[Object]: {{< url path="Cortex.Reference.DataTypes.All.Object.MainDoc" >}} +[Working with Enums]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Enums.MainDoc" >}} + +[Literal Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.LiteralEditor.MainDoc" >}} +[Expression Editor]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.MainDoc" >}} +[Enum expressions]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.PropertyEditors.ExpressionEditor.EnumExpressions" >}} +[Input]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Blocks.BlockProperties.WhatIsABlockProperty.Input" >}} + +[debugging a flow]: {{< url path="Cortex.Reference.Concepts.Fundamentals.Executions.ExecutionsInDevelopment.MainDoc" >}} +[Variables Viewer]: {{< url path="Cortex.Guides.UserGuides.UserInterfaces.Gateway.Dev.FlowEditor.RightPanel.ExecutionViewer.VariablesViewer" >}} +[Variables List]: {{< url path="Cortex.Guides.UserGuides.UserInterfaces.Gateway.Dev.FlowEditor.RightPanel.ExecutionViewer.VariablesList" >}} +[Variable Details Viewer]: {{< url path="Cortex.Guides.UserGuides.UserInterfaces.Gateway.Dev.FlowEditor.RightPanel.ExecutionViewer.VariableDetailsViewer" >}} + +[Date and Time Formatting]: {{< url path="Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting.MainDoc" >}} +[Culture]: {{< url path="Cortex.Reference.Concepts.WorkingWith.Culture.MainDoc" >}} + +[All Data Types]: {{< url path="Cortex.Reference.DataTypes.MainDoc" >}} + +[Nullable<T>]: {{< url path="Cortex.Reference.DataTypes.Other.Nullable.MainDoc" >}} +[Int32]: {{< url path="Cortex.Reference.DataTypes.Numbers.Int32.MainDoc" >}} +[String]: {{< url path="Cortex.Reference.DataTypes.Text.String.MainDoc" >}} + +[DayOfWeek]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DayOfWeek.MainDoc" >}} +[DateTimeComponentType]: {{< url path="Cortex.Reference.DataTypes.DateAndTime.DateTimeComponentType.MainDoc" >}} +[SearchOptions]: {{< url path="Cortex.Reference.DataTypes.Text.SearchOptions.MainDoc" >}} +[StringSplitOptions]: {{< url path="Cortex.Reference.DataTypes.Text.StringSplitOptions.MainDoc" >}} +[StringComparison]: {{< url path="Cortex.Reference.DataTypes.Text.StringComparison.MainDoc" >}} +[HttpStatusCode]: {{< url path="Cortex.Reference.DataTypes.Http.HttpStatusCode.MainDoc" >}} +[RequestVerb]: {{< url path="Cortex.Reference.DataTypes.Http.RequestVerb.MainDoc" >}} +[EmailMessagePriority]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessagePriority.MainDoc" >}} +[EmailMessageBodyFormat]: {{< url path="Cortex.Reference.DataTypes.Email.EmailMessageBodyFormat.MainDoc" >}} +[DataCommandErrorCode]: {{< url path="Cortex.Reference.DataTypes.Data.DataCommandErrorCode.MainDoc" >}} +[OracleMappingType]: {{< url path="Cortex.Reference.DataTypes.Data.OracleMappingType.MainDoc" >}} +[ContentOptions]: {{< url path="Cortex.Reference.DataTypes.FilesAndFolders.ContentOptions.MainDoc" >}} +[EventSeverity]: {{< url path="Cortex.Reference.DataTypes.Logs.EventSeverity.MainDoc" >}} + +[Set Variable]: {{< url path="Cortex.Reference.Blocks.Variables.SetVariable.SetVariable.MainDoc" >}} +[Convert Object To Text]: {{< url path="Cortex.Reference.Blocks.Objects.ConvertObject.ConvertObjectToText.MainDoc" >}} +[Convert Object To Json]: {{< url path="Cortex.Reference.Blocks.Json.ConvertJson.ConvertObjectToJson.MainDoc" >}} +[Get Date Time Component]: {{< ref "../../../Blocks/date-and-time/get-date-time/get-date-time-component-block.md" >}} + +[MS System Enum]: {{< url path="MSDocs.DotNet.Api.System.Enum.MainDoc" >}} +[MS Enumeration Types]: {{< url path="MSDocs.CSharp.EnumerationTypes" >}} +[MS Instantiating Enum]: {{< url path="MSDocs.DotNet.Api.System.Enum.InstantiatingAnEnum" >}} +[MS Formatting Enum]: {{< url path="MSDocs.DotNet.Api.System.Enum.FormattingEnumerationValues" >}} diff --git a/data/urls.toml b/data/urls.toml index 5c8bdc77e..4c16cf48d 100644 --- a/data/urls.toml +++ b/data/urls.toml @@ -128,6 +128,8 @@ DisableCodeAnalyser = "/docs/faqs/configure-code-analyser/multi-server-with-ha/disable-code-analyser" [Cortex.Faqs.ConfigureCodeAnalyser.SingleServerWithoutHA] DisableCodeAnalyser = "/docs/faqs/configure-code-analyser/single-server-without-ha/disable-code-analyser" + [Cortex.Faqs.ConfigureGlobalRunAs] + MainDoc = "/docs/faqs/configure-global-runas/" [Cortex.Faqs.ConfigureOidcAuthentication] MainDoc = "/docs/faqs/configure-oidc-authentication/" [Cortex.Faqs.ConfigureOidcAuthentication.MicrosoftEntra] @@ -1103,7 +1105,9 @@ [Cortex.Reference.Blocks.Data.ExecuteDataCommand] [Cortex.Reference.Blocks.Data.ExecuteDataCommand.ExecuteDataCommand] MainDoc = "/docs/reference/blocks/data/execute-data-command/execute-data-command-block-1" + CloseConnectionProperty = "/docs/reference/blocks/data/execute-data-command/execute-data-command-block-1/#close-connection" CommandProperty = "/docs/reference/blocks/data/execute-data-command/execute-data-command-block-1/#command" + ComplexCommands = "/docs/reference/blocks/data/execute-data-command/execute-data-command-block-1/#complex-commands" ConnectionDetailsProperty = "/docs/reference/blocks/data/execute-data-command/execute-data-command-block-1/#connection-details" ExecutingMultipleCommandsSafe = "/docs/reference/blocks/data/execute-data-command/execute-data-command-block-1/#executing-multiple-commands-safe" ExecutingMultipleCommandsUnsafe = "/docs/reference/blocks/data/execute-data-command/execute-data-command-block-1/#executing-multiple-commands-unsafe" @@ -1222,6 +1226,7 @@ [Cortex.Reference.Blocks.Email.SendEmail] [Cortex.Reference.Blocks.Email.SendEmail.SendEmailUsingSmtpServer] MainDoc = "/docs/reference/blocks/email/send-email/send-email-using-smtp-server-block" + CloseSessionProperty = "/docs/reference/blocks/email/send-email/send-email-using-smtp-server-block/#close-session" SupportedServerAddressFormats = "/docs/reference/blocks/email/send-email/send-email-using-smtp-server-block/#supported-formats-for-serverdetailshost" SettingUseSsl = "/docs/reference/blocks/email/send-email/send-email-using-smtp-server-block/#setting-usessl" SettingCredentials = "/docs/reference/blocks/email/send-email/send-email-using-smtp-server-block/#setting-credentials" @@ -1805,8 +1810,20 @@ MainDoc = "/docs/reference/concepts/working-with/culture/current-culture" [Cortex.Reference.Concepts.WorkingWith.Culture.InvariantCulture] MainDoc = "/docs/reference/concepts/working-with/culture/invariant-culture" + [Cortex.Reference.Concepts.WorkingWith.Culture.SpecificCultures] + MainDoc = "/docs/reference/concepts/working-with/culture/specific-cultures" [Cortex.Reference.Concepts.WorkingWith.DataSources] MainDoc = "/docs/reference/concepts/working-with/data-sources/" + [Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources] + MainDoc = "/docs/reference/concepts/working-with/data-sources/supported-data-sources/" + [Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.SqlServer] + MainDoc = "/docs/reference/concepts/working-with/data-sources/supported-data-sources/sql-server" + [Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.Oracle] + MainDoc = "/docs/reference/concepts/working-with/data-sources/supported-data-sources/oracle" + [Cortex.Reference.Concepts.WorkingWith.DataSources.SupportedDataSources.Odbc] + MainDoc = "/docs/reference/concepts/working-with/data-sources/supported-data-sources/odbc" + [Cortex.Reference.Concepts.WorkingWith.DataSources.WhatIsADataSource] + MainDoc = "/docs/reference/concepts/working-with/data-sources/what-is-a-data-source/" [Cortex.Reference.Concepts.WorkingWith.DateAndTime] MainDoc = "/docs/reference/concepts/working-with/date-and-time/" [Cortex.Reference.Concepts.WorkingWith.DateAndTime.DateAndTimeFormatting] @@ -1819,10 +1836,13 @@ [Cortex.Reference.Concepts.WorkingWith.Email] MainDoc = "/docs/reference/concepts/working-with/email/" [Cortex.Reference.Concepts.WorkingWith.Email.Authentication] + MainDoc = "/docs/reference/concepts/working-with/email/authentication/" SettingUpAppPassword = "/docs/reference/concepts/working-with/email/authentication/#setting-up-an-app-password-for-a-gmail-account" SettingUpOAuthGmail = "/docs/reference/concepts/working-with/email/authentication/#setting-up-a-gmail-account-for-oauth-authentication" SettingUpClientCredentialsOutlook = "/docs/reference/concepts/working-with/email/authentication/#setting-up-an-outlook-account-for-oauth-authentication-using-client-credentials" SettingUpCertificateCredentialsOutlook = "/docs/reference/concepts/working-with/email/authentication/#setting-up-an-outlook-account-for-oauth-authentication-using-certificate-credentials" + [Cortex.Reference.Concepts.WorkingWith.Email.WhatIsEmail] + MainDoc = "/docs/reference/concepts/working-with/email/what-is-email/" [Cortex.Reference.Concepts.WorkingWith.Enums] MainDoc = "/docs/reference/concepts/working-with/enums/" [Cortex.Reference.Concepts.WorkingWith.Enums.WhatIsAnEnum] @@ -1976,8 +1996,10 @@ MainDoc = "/docs/reference/data-types/data/connectiondetails" [Cortex.Reference.DataTypes.Data.OdbcConnectionDetails] MainDoc = "/docs/reference/data-types/data/odbcconnectiondetails" + ConnectionString = "/docs/reference/data-types/data/odbcconnectiondetails/#connection-string" [Cortex.Reference.DataTypes.Data.OracleConnectionDetails] MainDoc = "/docs/reference/data-types/data/oracleconnectiondetails" + ConnectionString = "/docs/reference/data-types/data/oracleconnectiondetails/#connection-string" [Cortex.Reference.DataTypes.Data.OracleMappingType] MainDoc = "/docs/reference/data-types/data/oraclemappingtype" [Cortex.Reference.DataTypes.Data.OracleParameter] @@ -1990,6 +2012,7 @@ Convert = "/docs/reference/data-types/data/oracleparameters/#convert-oracleparameters-to-text" [Cortex.Reference.DataTypes.Data.SqlServerConnectionDetails] MainDoc = "/docs/reference/data-types/data/sqlserverconnectiondetails" + ConnectionString = "/docs/reference/data-types/data/sqlserverconnectiondetails/#connection-string" [Cortex.Reference.DataTypes.DateAndTime] [Cortex.Reference.DataTypes.DateAndTime.DateTime] MainDoc = "/docs/reference/data-types/date-and-time/datetime" @@ -2018,6 +2041,7 @@ [Cortex.Reference.DataTypes.Email.EmailAddress] MainDoc = "/docs/reference/data-types/email/emailaddress" Address = "/docs/reference/data-types/email/emailaddress/#address" + Name = "/docs/reference/data-types/email/emailaddress/#name" [Cortex.Reference.DataTypes.Email.EmailMessage] MainDoc = "/docs/reference/data-types/email/emailmessage" To = "/docs/reference/data-types/email/emailmessage/#to" @@ -2353,6 +2377,7 @@ [Cortex.Reference.Exceptions.Concurrency] [Cortex.Reference.Exceptions.Concurrency.Semaphores] [Cortex.Reference.Exceptions.Concurrency.Semaphores.SemaphoreCouldNotBeAcquiredException] + MainDoc = "/docs/reference/exceptions/concurrency/semaphores/semaphore-could-not-be-acquired-exception" ConcurrencyLimitReached = "/docs/reference/exceptions/concurrency/semaphores/semaphore-could-not-be-acquired-exception/#concurrency-limit-reached" QueueTimeoutReached = "/docs/reference/exceptions/concurrency/semaphores/semaphore-could-not-be-acquired-exception/#queue-timeout-reached" Queued = "/docs/reference/exceptions/concurrency/semaphores/semaphore-could-not-be-acquired-exception/#queued" @@ -3071,7 +3096,6 @@ [MSDocs.DotNet.Fundamentals] [MSDocs.DotNet.Fundamentals.RuntimeLibraries] CultureAndRegionInfoBuilder = "https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-globalization-cultureandregioninfobuilder" - [MSDocs.PowerShell] WhatIsPowerShell = "https://learn.microsoft.com/en-us/powershell/scripting/overview?view=powershell-5.1" InstallModule = "https://learn.microsoft.com/en-us/powershell/scripting/developer/module/installing-a-powershell-module?view=powershell-7.2"