5.3 In-Memory DataTable Transformations & Column Operations
Key Takeaways
DataTable.Clone() copies only the schema (columns, types, constraints) with zero rows, while DataTable.Copy() copies the schema and all rows; assigning one variable to another only copies the reference.
DataColumn.Expression creates calculated columns (for example 'Quantity * UnitPrice' or IIF conditions), and DataTable.Compute returns a single aggregate such as Sum(Amount) for a filter.
Merge Data Table's MissingSchemaAction decides how extra source columns are handled: Add, Ignore, Error, or AddWithKey.
dt.DefaultView.Sort and RowFilter, followed by ToTable(), sort and filter in memory; ToTable(True, columns...) also removes duplicates across the chosen columns.
DataTable is safe for concurrent reads only, so row updates should run on a single thread.
5.3 In-Memory DataTable Transformations & Column Operations
While LINQ queries excel at filtering and querying data rows, enterprise automations frequently demand structural modifications to DataTable objects. Developers must dynamically add computed columns, modify data types, consolidate disparate vendor reports through table merges, and eliminate duplicate records prior to queue dispatching or database staging.
Executing these structural changes efficiently requires deep mastery of the ADO.NET architecture under the hood of UiPath Studio. This section covers schema cloning versus deep copying, dynamic expression columns, missing schema collision policies during table merges, and high-performance DataView operations for sorting and deduplication.
1. Schema Cloning vs. Deep Copying vs. Variable Aliasing
A frequent source of bugs in enterprise UiPath workflows is the improper duplication of DataTable variables. Developers often confuse structural replication with data replication, or unwittingly introduce reference aliasing bugs.
The Variable Assignment Trap (dt_Target = dt_Source)
In .NET, DataTable is a reference type. Assigning one variable to another (dt_Target = dt_Source) does not create a new table. Instead, it copies the memory reference pointer. Both variables point to the exact same object on the managed heap. Any row added, edited, or removed through dt_Target immediately alters dt_Source.
Structural Replication: dt.Clone()
The .Clone() method creates a new, independent DataTable with the identical schema as the source table, but zero rows:
- It duplicates all
DataColumndefinitions (column names,DataType,AllowDBNull,DefaultValue,MaxLength, andExpression). - It duplicates primary keys, table constraints, and extended properties.
- It allocates no row storage, making it the ideal pattern for initializing filtered target tables.
Deep Replication: dt.Copy()
The .Copy() method creates a completely independent deep copy of the table, replicating both the structural schema and all DataRow elements:
- All rows are cloned into the new table.
- Row states (
DataRowState.Added,Modified,Unchanged,Deleted) and row versions (DataRowVersion.Original,Current) are preserved. - Modifications made to rows in the copied table have no effect on the original source table.
| Operation | Duplicates Schema? | Duplicates Data Rows? | Preserves Constraints? | Common Automation Use Case |
|---|---|---|---|---|
dt.Clone() | Yes | No (0 rows) | Yes | Initializing empty output tables for LINQ .CopyToDataTable() fallback |
dt.Copy() | Yes | Yes (all rows) | Yes | Snapshotting raw input data prior to destructive transformations or filtering |
dt_B = dt_A | Pointer alias | Pointer alias | Pointer alias | Passing table references between workflow arguments (in/out) |
' VB.NET: Demonstrating Clone vs Copy
Dim dt_EmptyTemplate As DataTable = dt_RawData.Clone() ' Schema only; 0 rows
Dim dt_BackupSnapshot As DataTable = dt_RawData.Copy() ' Schema + all rows
2. Adding and Configuring Columns Programmatically
UiPath provides the visual Add Data Column activity, but configuring columns in code via an Invoke Code activity or within expressions grants precise control over column types and positions.
Specifying Native System Types
When adding columns, developers should assign strongly typed System.Type definitions rather than accepting default object representations:
' VB.NET: Adding strongly typed columns to a DataTable
dt_Transactions.Columns.Add("ProcessingTimestamp", GetType(DateTime))
dt_Transactions.Columns.Add("TaxRate", Type.GetType("System.Double"))
dt_Transactions.Columns.Add("IsAudited", GetType(Boolean))
// C#: Adding strongly typed columns to a DataTable
dt_Transactions.Columns.Add("ProcessingTimestamp", typeof(DateTime));
dt_Transactions.Columns.Add("TaxRate", Type.GetType("System.Double"));
dt_Transactions.Columns.Add("IsAudited", typeof(bool));
Modifying Column Ordinal Positions
By default, new columns are appended to the far right of the table. To position a column at a specific zero-based index (for instance, making a generated status column the first column in an export file), use .SetOrdinal():
' Move the "Status" column to the first column position (index 0)
dt_Transactions.Columns("Status").SetOrdinal(0)
Renaming and Deleting Columns
' Rename an imported header
dt_Transactions.Columns("Old_Vendor_Num").ColumnName = "VendorID"
' Remove temporary staging column
dt_Transactions.Columns.Remove("TempCalculation")
3. Dynamic Computed Expression Columns (DataColumn.Expression)
One of the most powerful and underutilized features of ADO.NET in UiPath automations is the DataColumn.Expression property. Setting an expression turns a column into a computed virtual column:
- Values are calculated dynamically by the ADO.NET engine whenever row values change.
- Calculations execute in native compiled code without procedural looping across rows.
- Computed columns do not consume storage space for raw cell values.
Arithmetic and Text Expressions
' Arithmetic calculation across numeric columns
dt_Orders.Columns("SubTotal").Expression = "Quantity * UnitPrice"
' Discounted total with conditional tax
dt_Orders.Columns("FinalTotal").Expression = "(Quantity * UnitPrice) * (1 - Discount) + TaxAmount"
' String concatenation combining names
dt_Staff.Columns("FullName").Expression = "LastName + ', ' + FirstName"
Conditional Logic (IIF Expressions)
The expression syntax supports the IIF(expr, truepart, falsepart) conditional operator, allowing complex status classification without a single visual activity:
' Classify transactions based on threshold
dt_Orders.Columns("AuditCategory").Expression = "IIF(FinalTotal > 50000, 'Tier 1 Review', IIF(FinalTotal > 10000, 'Tier 2 Review', 'Standard'))"
Aggregates with DataTable.Compute
For a single total or average over the whole table, or over a filtered subset, use the Compute method instead of adding a column:
' Total of the Amount column for one department
Dim itTotal As Object = dt_Expenses.Compute("Sum(Amount)", "Department = 'IT'")
' Number of open orders
Dim openCount As Object = dt_Orders.Compute("Count(OrderID)", "Status = 'Open'")
Compute returns an Object (or DBNull when no rows match), so convert the result before using it in arithmetic.
4. Transforming Row Values: LINQ vs. For Each Row vs. Invoke Code
When updating existing column values (e.g., trimming whitespace from every cell in a column, converting string dates to standardized ISO formats), developers must balance performance against implementation complexity:
- For Each Row in Data Table Activity: Suitable when updating rows requires invoking UI elements, orchestrator queue calls, or external REST APIs. However, for pure in-memory data sanitization across 20,000+ rows, the activity execution loop introduces significant lag.
- LINQ Projection into Cloned Table: Projects rows into modified structures and materializes via
.CopyToDataTable(). Fast, but requires recreating row instances. Invoke Codeloop: Updates rows in place inside one activity, which avoids the per-row activity overhead of For Each Row.
' VB.NET inside Invoke Code (argument: in_Customers As DataTable, direction In)
For Each row As DataRow In in_Customers.Rows
If Not row.IsNull("Email") Then
row("Email") = row("Email").ToString().Trim().ToLower()
End If
If Not row.IsNull("Phone") Then
row("Phone") = System.Text.RegularExpressions.Regex.Replace(row("Phone").ToString(), "[^0-9]", "")
End If
Next
Warning
Do not update a DataTable from several threads at once, for example with Parallel.ForEach. Microsoft documents DataTable as safe for multithreaded reads only; concurrent writes must be synchronized or they can corrupt the table's internal indexes.
5. Merging DataTables and MissingSchemaAction Policies
The Merge Data Table activity (or native dt_Target.Merge(dt_Source) method) combines rows from a source table into a target destination table. When the schemas of both tables do not align perfectly, the MissingSchemaAction property dictates how the engine resolves discrepancies.
' Method signature
dt_Target.Merge(dt_Source, preserveChanges:=False, missingSchemaAction:=MissingSchemaAction.Add)
The Four MissingSchemaAction Enum Behaviors
MissingSchemaAction Value | Schema Resolution Behavior | Exception Thrown? | Best Use Case |
|---|---|---|---|
MissingSchemaAction.Add | Appends missing columns from source table to target table schema; populates incoming data. | No | Consolidating disparate reports where all vendor-specific data fields must be preserved. |
MissingSchemaAction.Ignore | Discards any source columns that do not exist in the target schema. Only matching columns are transferred. | No | Standardizing unstructured input files into a rigid enterprise master template. |
MissingSchemaAction.Error | Requires the target to already have every source column. Throws an exception if the source contains extra columns. | Yes | Strict compliance automations where schema drift indicates an upstream file error. |
MissingSchemaAction.AddWithKey | Appends missing columns AND imports primary key constraints and unique index definitions from source. | No | Merging relational dataset snapshots where primary key uniqueness must be enforced. |
The preserveChanges Parameter
When merging tables that possess a primary key, existing records are updated rather than appended. The preserveChanges boolean parameter controls row version priority:
preserveChanges = True: Retains local changes made in the target table, ignoring incoming source updates for conflicting rows.preserveChanges = False(Default): Overwrites target table rows with incoming source data.
6. High-Performance Sorting and Deduplication with DataView
Every DataTable contains a DefaultView property returning a System.Data.DataView instance. The DataView provides high-speed native sorting, row filtering, and projection capabilities without requiring complex LINQ syntax or external loops.
In-Memory Sorting with DataView.Sort
Sorting a DataTable via its DefaultView uses the view's own index inside ADO.NET, so no workflow loop is needed. The sorted view can then be converted back to a DataTable via .ToTable():
' VB.NET: Sort table by Priority ascending, then by Amount descending
dt_Transactions.DefaultView.Sort = "Priority ASC, Amount DESC"
Dim dt_Sorted As DataTable = dt_Transactions.DefaultView.ToTable()
In-Memory Filtering with DataView.RowFilter
The RowFilter property applies expression-based filtering equivalent to an SQL WHERE clause:
' VB.NET: Filter rows using RowFilter
dt_Transactions.DefaultView.RowFilter = "Status = 'Open' AND Amount >= 5000 AND OrderDate >= #2026-01-01#"
Dim dt_Filtered As DataTable = dt_Transactions.DefaultView.ToTable()
Column Projection and Deduplication: ToTable(isDistinct, ...)
The .ToTable() method features an overload that accepts a boolean isDistinct flag and a ParamArray of column names:
' Signature:
dt.DefaultView.ToTable(isDistinct As Boolean, ParamArray columnNames As String())
This single method call performs two critical operations simultaneously:
- Column Filtering (Projection): It extracts only the specified columns, discarding all others.
- Deduplication (
Distinct): IfisDistinctis set toTrue, it eliminates duplicate rows, retaining only unique combinations of the selected column values.
' VB.NET: Extract unique combinations of CustomerID and Region into a new table
Dim dt_UniqueClients As DataTable = dt_Sales.DefaultView.ToTable(True, "CustomerID", "Region")
' C#: Extract unique combinations of CustomerID and Region into a new table
DataTable dt_UniqueClients = dt_Sales.DefaultView.ToTable(true, "CustomerID", "Region");
Tip
dt.DefaultView.ToTable(True, "Col1", "Col2") removes duplicates across a subset of columns in one call and keeps the column types, without writing a LINQ .GroupBy() query.
7. Memory Management and Enterprise Cleanup Patterns
DataTable objects can hold a large amount of managed memory, including the indexes behind views, sorting, and primary keys. In long-running unattended automations processing millions of rows, keeping large tables alive longer than needed can lead to excessive memory consumption.
- Call
dt.Clear()to remove all rows while retaining table schema. - Set
dt = Nothing(ornullin C#) once data processing is complete to allow the garbage collector to reclaim generation 2 heap memory. - When processing massive files in batches, initialize tables within localized inner scopes rather than maintaining global workflow variables.
What is the primary operational difference between invoking dt.Clone() and dt.Copy() on an existing DataTable variable in UiPath Studio?
dt.Clone() converts all string columns into integer values, while dt.Copy() retains original data types
dt.Clone() creates an asynchronous copy in a background thread, while dt.Copy() executes synchronously on the main UI thread
dt.Clone() copies the table data rows but resets column names, while dt.Copy() copies column names without data
dt.Clone() duplicates only the schema, column types, and constraints with zero rows, while dt.Copy() duplicates the schema along with all data rows
An automation workflow merges a secondary supplier DataTable into a primary master DataTable. The supplier table contains several supplemental metadata columns that do not exist in the master table. The developer must ensure that these extra supplier columns are discarded without throwing an exception, transferring only the data for columns that exist in the master schema. Which MissingSchemaAction property setting must be configured on the Merge Data Table activity?
MissingSchemaAction.Add
MissingSchemaAction.Error
MissingSchemaAction.Ignore
MissingSchemaAction.AddWithKey
Which native method call enables a UiPath developer to extract a subset of specific columns from an existing DataTable while simultaneously eliminating all duplicate rows across those columns in a single operation, without authoring a LINQ query or loop?
dt.Select().Distinct()
dt.DefaultView.ToTable(True, "Column1", "Column2")
dt.AsEnumerable().TakeDistinct("Column1", "Column2")
dt.Rows.RemoveDuplicates("Column1", "Column2")
Sections you finish are checked off in the contents.