Apricot Framework

Defining operations

The XML format, the settings each level carries, provider compatibility, and conditional SQL fragments.

A definition file has optional file-wide settings and any number of groups:

<?xml version="1.0" encoding="utf-8" ?>
<DataOperations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:noNamespaceSchemaLocation="DataOps.xsd">

  <DataConfiguration Timeout="PT30S" Compatibility="Sqlite" AutoTransaction="ReadCommitted" />

  <OperationGroup Name="Authors">
    <SqlOperation Name="All">
      <TextCommand ExpectedResult="Table">SELECT id, full_name FROM authors</TextCommand>
    </SqlOperation>
    <SqlOperation Name="Refresh" Timeout="PT5M">
      <StoredProcedure Name="sp_refresh_authors" ExpectedResult="RowCount" />
    </SqlOperation>
  </OperationGroup>

</DataOperations>

The schema is published as DataOps.xsd and every file is validated against it unless a source is constructed with validation off. Validation reports every problem in the file at once.

xsi:noNamespaceSchemaLocation is a relative path from the file to a copy of the schema, and it is read by your editor only — the parser always validates against the copy embedded in the package, so the attribute can point anywhere convenient or be left off entirely.

DataOps.xsd ships at the root of ApricotFramework.DataOps, which restore extracts to disk, so getting a copy beside your operations files is one command:

cp ~/.nuget/packages/apricotframework.dataops/<version>/DataOps.xsd Operations/

Note

The schema is not injected into your project or your build output. Nothing reads it at runtime, so copying it is only ever for the editor, and a stale copy cannot make a valid file fail or an invalid one pass.

Settings and where they come from

Timeout, Compatibility and AutoTransaction may appear on DataConfiguration, on OperationGroup and on SqlOperation. The innermost one wins; anything unset is inherited.

AttributeValuesUnset means
Compatibilitywhitespace-separated list of SqlServer, MySql, PostgreSql, Sqlite, plus Any and NoneAny
Timeoutan xs:duration such as PT30S; a 00:00:30 TimeSpan is also acceptedthe provider's own default
AutoTransactionNo, ReadUncommitted, ReadCommitted, RepeatableRead, Serializableno transaction
ExpectedResultTable, MultipleTables, Scalar, RowCount, UnknownUnknown

AutoTransaction="No" is not the same as leaving it out: it stops the inheritance, so an operation inside a transactional group can opt out of one.

ExpectedResult decides which executor may run the operation — declaring Scalar and calling Query fails immediately rather than at the database. Unknown permits any of them, and the caller carries the risk.

Note

Provider names are read case-insensitively, so MySQL from an older file still parses. The spellings above are the ones the schema accepts.

Conditional fragments

An operation can switch between two pieces of SQL its author wrote:

<TextCommand ExpectedResult="Table">
  SELECT id, full_name FROM authors ORDER BY {if:byName{ full_name } else { id }}
</TextCommand>
dataOps.Connect().Query("Authors", "All").WithBinding("byName", true)

A false or absent binding takes the else branch; any other non-null value takes the if branch.

Warning

A binding chooses between two authored fragments and is never substituted into the command, so it cannot inject SQL. Values still belong in parameters — @FullName, passed to ExecuteAsync — never in a fragment.

Where files live

SourceReads fromUse for
EmbeddedXmlOperationsDefinitionSourceembedded resources of an assemblyoperations a library owns and ships
DirectXmlOperationsDefinitionSourcefiles under a directorySQL that should be editable without a rebuild
StaticOperationsDefinitionSourcedefinitions built in codetests, and generating operations

Subclass one of them so the source can be registered by type:

public class AuthorOperationsSource : EmbeddedXmlOperationsDefinitionSource
{
    public AuthorOperationsSource()
        : base(Assembly.GetExecutingAssembly(), new EmbeddedFinderOptions { Directories = { "Operations" } })
    {
    }
}

On this page