Use elastic tables using code
This article describes how to use code to perform data operations on elastic tables.
Work with the session token
As was mentioned in Consistency level, you can achieve session-level consistency by passing the current session token with your requests. If you don't include the session token, the data that you retrieve might not include data changes that you've just made.
Getting the session token
The session token is available in the response of all write operations. Look for the x-ms-session-token
value.
For any OrganizationResponse that performs a write operation, you can capture the x-ms-session-token
value in the Results collection.
Note
DeleteResponse doesn't currently return the x-ms-session-token
value. For more information, go to Known issue: No x-ms-session-token value is returned for delete operations.
string sessionToken = response.Results["x-ms-session-token"].ToString();
Sending the session token
The way that you send the session token in a read operation depends on whether you're using the SDK or Web API.
When you perform an operation that retrieves data, set the SessionToken
optional parameter on OrganizationRequest.
var request = new RetrieveRequest
{
Target = new EntityReference("contoso_sensordata", sensordataid),
ColumnSet = new ColumnSet("contoso_value"),
["partitionId"] = deviceId,
["SessionToken"] = sessionToken
};
Specify PartitionId
As was mentioned in Partitioning and horizontal scaling, each elastic table has a partitionid
column that you must use if you choose to apply a partitioning strategy for the table. Otherwise, don't set a value for the partitionid
column.
Important
If you choose to use a partitioning strategy for your elastic table, all operations on that table or referring to records in that table MUST specify the partitionid
column value to uniquely identify the record. There is no error thrown if partitionid
is not specified in the lookup value of referencing table, but the lookup will fail to locate the record when you use it. You must document and enforce this requirement via code reviews to ensure that your data is consistent and partitionid
is used appropriately for all the operations.
After you specify a non-null value for the partitionid
column when you create a row, you must specify it when you perform any other data operation on that row. You can't change the value later.
If you don't set a partitionid
value for a record when you create it, the partitionid
column value remains null, and you can't change it later. In this case, you can identify records by using the primary key, just as you do with standard tables. Specifying a partitionid
value isn't required.
Note
The examples in this article assume that you specify a non-null value for the partitionid
column.
You can set the partitionid
value in following ways when you perform various data operations.
Using the alternate key
As was mentioned in Alternate keys, every elastic table has an alternate key that is named KeyForNoSqlEntityWithPKPartitionId
. This alternate key combines the primary key of the table with the partitionid
column.
If you are using a partitioning strategy, you must specify an alternate key to specify the partitionid
value when you use Retrieve
, Update
, or Delete
operations, or when you set a lookup column for another table that refers to an elastic table record.
This example shows how you can use the alternate key to specify the partitionid
value when you use Retrieve
, Update
, and Delete
requests on elastic tables.
var keys = new KeyAttributeCollection() {
{ "contoso_sensordataid", sensordataid },
{ "partitionid", deviceId }
};
var entity = new Entity("contoso_sensordata", keys)
Using the partitionId parameter
Currently, you can use a partitionId
parameter to specify the value of the partitionid
column only for Retrieve
and Delete
operations. For more information, go to Known issue: The partitionId optional parameter isn't available for all messages.
Note
The partitionId
parameter doesn't work with Create
, Update
, or Upsert
messages, and it is ignored if it's sent.
request["partitionId"] = "device-001"
Using the partitionid column directly
For Create
, Upsert
, or Update
operations, you can directly specify the value of the partitionid
column.
This example shows how you can directly specify the value of the partitionid
column in Entity
when you perform a Create
, Upsert
, or Update
operation.
var entity = new Entity("contoso_sensordata", sensordataid)
{
Attributes = {
{ "partitionid", "device-001" }
}
};
Create a record in an elastic table
This example creates a row in the contoso_SensorData
table, where partitionid
is set to deviceid
. It also sets the ttlinseconds
column to ensure that the row expires after one day (86,400 seconds) and is automatically deleted from Dataverse.
This example also captures the x-ms-session-token
value that you can use when you retrieve the created record.
/// <summary>
/// Demonstrates creating a record with a partitionid and capturing the session token
/// </summary>
/// <param name="service">Authenticated client implementing the IOrganizationService interface</param>
/// <param name="deviceId">The value used as partitionid for the contoso_sensordata table. </param>
/// <param name="sessionToken">The current session token</param>
/// <returns>The Id of the created record.</returns>
public static Guid CreateExample(
IOrganizationService service,
string deviceId,
ref string sessionToken )
{
var entity = new Entity("contoso_sensordata")
{
Attributes =
{
{ "contoso_deviceid", deviceId },
{ "contoso_sensortype", "Humidity" },
{ "contoso_value", 40 },
{ "contoso_timestamp", DateTime.UtcNow},
{ "partitionid", deviceId },
{ "ttlinseconds", 86400 } // 86400 seconds in a day
}
};
var request = new CreateRequest {
Target = entity
};
var response = (CreateResponse)service.Execute(request);
// Capture the session token
sessionToken = response.Results["x-ms-session-token"].ToString();
return response.id;
}
Use the x-ms-session-token
value that is returned to set the SessionToken
optional parameter when you retrieve the record that you created. Learn more about sending the session token.
Note
Deep insert is not supported with elastic tables. Each related record needs to be created independently. Only standard tables support deep insert
Setting the primary key value
If you don't specify a primary key value, Dataverse sets a primary key value for the record when you create it. Letting Dataverse set this value is the normal practice. You can specify the primary key value if you need to. For elastic tables, there's no performance benefit in letting Dataverse set the primary key value.
Elastic tables don't return an error when you create a record with a primary key value that isn't unique. By setting the primary key values with elastic tables, you can create records with that have the same primary key values and different partitionid
values. However, this pattern isn't compatible with Power Apps. Don't create records with duplicate primary key values when people need to use this data in canvas or model-driven apps.
Update a record in an elastic table
This example updates the contoso_value
and contoso_timestamp
values of an existing row in the contoso_SensorData
table by using the contoso_sensordataid
primary key and partitionid
= 'device-001'
.
If you're using a partitioning strategy, the primary key and partitionid
columns must uniquely identify an existing elastic table row. The partitionid
value of an existing row can't be updated and is used only to uniquely identify the row to update.
This example uses the KeyForNoSqlEntityWithPKPartitionId
alternate key to uniquely identify the record by using both the primary key and partitionid
values. Learn more about alternate keys.
This example shows how to use the partitionid
value as an alternate key.
/// <summary>
/// Demonstrates updating elastic table row with partitionid as alternate key.
/// </summary>
/// <param name="service">Authenticated client implementing the IOrganizationService interface</param>
/// <param name="sensordataid">The unique identifier of the contoso_sensordata table.</param>
/// <param name="deviceId">The value used as partitionid for the contoso_sensordata table. </param>
/// <param name="sessionToken">The current session token</param>
public static void UpdateExample(
IOrganizationService service,
Guid sensordataid,
string deviceId,
ref string sessionToken)
{
var keys = new KeyAttributeCollection() {
{ "contoso_sensordataid", sensordataid },
{ "partitionid", deviceId }
};
var entity = new Entity("contoso_sensordata", keys)
{
Attributes = {
{ "contoso_value", 60 },
{ "contoso_timestamp", DateTime.UtcNow }
}
};
var request = new UpdateRequest {
Target = entity,
["SessionToken"] = sessionToken
};
var response = (UpdateResponse)service.Execute(request);
// Capture the session token
sessionToken = response.Results["x-ms-session-token"].ToString();
}
Learn more about using the Entity class to set alternate keys.
Retrieve a record in an elastic table
If the partitionid
value was set when an elastic table record was created, you must use it together with the primary key value to uniquely identify a record.
If the partitionid
wasn't set, you can retrieve the record in the usual way, by using only the primary key value.
There are two different ways to compose a request to retrieve a record by using the partitionid
value.
This example uses the RetrieveRequest class. The Target
property is set to an EntityReference that is created by using the constructor that accepts a KeyAttributeCollection to use the KeyForNoSqlEntityWithPKPartitionId
alternate key. Learn more about using the EntityReference class with alternate keys.
public static void RetrieveExampleAlternateKey(IOrganizationService service, Guid sensorDataId, string deviceId) {
var keys = new KeyAttributeCollection() {
{ "contoso_sensordataid", sensorDataId },
{ "partitionid", deviceId }
};
var entityReference = new EntityReference("contoso_sensordata", keys);
var request = new RetrieveRequest {
ColumnSet = new ColumnSet("contoso_value"),
Target = entityReference
};
var response = (RetrieveResponse)service.Execute(request);
Console.WriteLine($"contoso_value: {response.Entity.GetAttributeValue<int>("contoso_value")}");
}
This example uses an optional parameter that is named partitionId
on the RetrieveRequest class. Learn more about using optional parameters.
public static void RetrieveExampleOptionalParameter(IOrganizationService service, Guid sensorDataId, string deviceId)
{
var entityReference = new EntityReference("contoso_sensordata", sensorDataId);
var request = new RetrieveRequest
{
ColumnSet = new ColumnSet("contoso_value"),
Target = entityReference,
["partitionId"] = deviceId
};
var response = (RetrieveResponse)service.Execute(request);
Console.WriteLine($"contoso_value: {response.Entity.GetAttributeValue<int>("contoso_value")}");
}
Query rows of an elastic table
When you query the rows of an elastic table, you get the best performance if you limit your query to a specific partition. Otherwise, your query returns data across all logical partitions, which isn't as fast.
Note
When you use this approach, the parameter must use the name partitionId
(with a capital I) instead of partitionid
(in all lowercase letters).
When you specify a filter this way, you don't have to specify the filter criteria on partitionid
in your query in the usual manner (that is, by using FetchXML condition
, QueryExpression ConditionExpression, or Web API $filter
).
Specifying a filter on the partitionid
value in the usual manner doesn't have the same performance benefits as specifying it through the partitionId
parameter as shown in the following examples.
These examples retrieve the first 5,000 rows in the contoso_SensorData
table that belong to the logical partition where partitionid
= 'deviceid-001'
.
public static EntityCollection RetrieveMultipleExample(IOrganizationService service)
{
var request = new RetrieveMultipleRequest
{
Query = new QueryExpression("contoso_sensordata")
{
ColumnSet = new ColumnSet("contoso_value")
},
["partitionId"] = "deviceid-001"
};
var response = (RetrieveMultipleResponse)service.Execute(request);
return response.EntityCollection;
}
Return related rows in a query
Elastic tables don't currently support returning related rows when a query is run. If you try to return related rows, Dataverse throws an error with code 0x80048d0b
and the following message:
Link entities are not supported
.
However, elastic tables do support returning related rows when a single row is retrieved.
Upsert a record in an elastic table
Important
Upsert operations with elastic tables differ from upsert operations with standard tables. Upsert operations are expected to contain the full payload and will overwrite any existing record data. They don't call the Create
or Update
messages. Learn more about elastic table upsert.
With elastic tables, if a record that has a given ID and partitionid
doesn't exist, it's created. If it already exists, it's replaced.
This example upserts a row in the contoso_SensorData
table with the specified id
value and partitionid
= deviceid-001
.
/// <summary>
/// Demonstrates an upsert operation
/// </summary>
/// <param name="service">Authenticated client implementing the IOrganizationService interface</param>
/// <param name="id">The id of the record to update or create.</param>
/// <param name="sessionToken">The current session token</param>
/// <returns>Whether a record was created or not</returns>
public static bool UpsertExample(IOrganizationService service, Guid id, ref string sessionToken)
{
var entity = new Entity("contoso_sensordata", id)
{
Attributes = {
{ "contoso_deviceid", "deviceid-001" },
{ "contoso_sensortype", "Humidity" },
{ "contoso_value", 60 },
{ "contoso_timestamp", DateTime.UtcNow },
{ "partitionid", "deviceid-001" },
{ "ttlinseconds", 86400 }
}
};
var request = new UpsertRequest
{
Target = entity,
["SessionToken"] = sessionToken
};
var response = (UpsertResponse)service.Execute(request);
// Capture the session token
sessionToken = response.Results["x-ms-session-token"].ToString();
return response.RecordCreated;
}
Delete a record in an elastic table
When you delete a record that uses a custom partitionid
value, you must include the partitionid
value.
This example deletes a row in the contoso_SensorData
table with the specified ID and partitionid
= 'deviceid-001'
.
/// <summary>
/// Demonstrates a delete operation
/// </summary>
/// <param name="service">Authenticated client implementing the IOrganizationService interface</param>
/// <param name="sensordataid">The unique identifier of the contoso_sensordata table.</param>
/// <param name="sessionToken">The current session token</param>
public static void DeleteExample(
IOrganizationService service,
Guid sensordataid,
ref string sessionToken)
{
var request = new DeleteRequest
{
Target = new EntityReference("contoso_sensordata", sensordataid),
["partitionId"] = "deviceid-001"
};
var response = service.Execute(request);
// Known issue: Value not currently being returned.
// sessionToken = response.Results["x-ms-session-token"].ToString();
}
Associate elastic table records
When a table record refers to an elastic table record where the partitionid
column value is null, you can associate a record in that table to a elastic table record just like standard records. Refer SDK for .NET, or the Web API.
When a table record refers to an elastic table record which has partitionid
column value set, you must include the partitionid
column value of the elastic table record when you set the lookup column of the referencing table. You can do this by including the value as an alternate key.
As described in Partitionid value column on referencing table, when a one-to-many relationship is created and the elastic table is the referenced table, a string column and a lookup column is created on the referencing table. The string column stores the partitionid
value of the referenced elastic table record.
You can set both the lookup and the string column values with their respective values by:
- Using an alternate key reference to set only the lookup
- Setting the two column values together in one update
How you do this depends on whether you are using the SDK for .NET or Web API
This example associates an elastic contoso_SensorData
table record with the specified ID and partitionid
to an existing account record by setting the lookup column with an alternate key:
/// <summary>
/// Demonstrates associate to elastic table operation.
/// </summary>
/// <param name="service">Authenticated client implementing the IOrganizationService interface</param>
/// <param name="sensordataId">The unique identifier of the contoso_sensordata table.</param>
/// <param name="deviceId">The deviceId. PartitionId of sensor data record.</param>
/// <param name="accountId">The unique identifier of the account record to update.</param>
public static void AssociateAccountAlternateKeyExample(
IOrganizationService service,
Guid sensordataId,
string deviceId,
Guid accountId)
{
var keys = new KeyAttributeCollection() {
{ "contoso_sensordataid", sensordataId },
{ "partitionid", deviceId }
};
var sensorDataReference = new EntityReference("contoso_sensordata", keys);
Entity account = new("account", accountId)
{
Attributes =
{
{"contoso_sensordata", sensorDataReference}
}
};
service.Update(account);
}
This example does the same thing, but sets both of the columns together:
/// <summary>
/// Demonstrates associate to elastic table operation.
/// </summary>
/// <param name="service">Authenticated client implementing the IOrganizationService interface</param>
/// <param name="sensordataId">The unique identifier of the contoso_sensordata table.</param>
/// <param name="deviceId">The deviceId. PartitionId of sensor data record.</param>
/// <param name="deviceId">The unique identifier of the account record to update.</param>
public static void AssociateAccountBothColumnsExample(
IOrganizationService service,
Guid sensordataId,
string deviceId,
Guid accountId)
{
Entity account = new("account", accountId) {
Attributes =
{
{"contoso_sensordata", new EntityReference("contoso_sensordata", sensordataId)},
{"contoso_sensordatapid", deviceId }
}
};
service.Update(account);
}
Finally, this example shows using the AssociateRequest to associate a collection of account
records to the same contoso_SensorData
table record in one operation.
/// <summary>
/// Demonstrates associating multiple accounts to a contoso_sensordata elastic table record
/// </summary>
/// <param name="service">Authenticated client implementing the IOrganizationService interface</param>
/// <param name="sensordataId">The unique identifier of the contoso_sensordata table.</param>
/// <param name="deviceId">The deviceId. PartitionId of sensor data record.</param>
/// <param name="relatedEntities">A collection of references to account records to associate to the contoso_sensordata elastic table record</param>
public static void AssociateMultipleElasticTableExample(
IOrganizationService service,
Guid sensordataId,
string deviceId,
EntityReferenceCollection relatedEntities)
{
// The keys to the elastic table record including the partitionid
var keys = new KeyAttributeCollection() {
{ "contoso_sensordataid", sensordataId },
{ "partitionid", deviceId }
};
AssociateRequest request = new()
{
Target = new EntityReference("contoso_sensordata", keys),
Relationship = new Relationship("contoso_SensorData_contoso_SensorData_Acc"),
RelatedEntities = relatedEntities
};
service.Execute(request);
}
Bulk operations with elastic tables
Often, applications must ingest a large amount of data into Dataverse in a short time. Dataverse has a group of messages that are designed to achieve high throughput. With elastic tables, the throughput can be even higher.
Bulk operations are optimized for performance when multiple write operations are performed on the same table by taking a batch of rows as input in a single write operation. Learn more about bulk Operation messages (preview).
Use CreateMultiple with elastic tables
You can use the CreateMultiple
message with either the SDK for .NET or Web API.
This example uses the CreateMultipleRequest class to create multiple rows in the contoso_SensorData
elastic table.
public static Guid CreateMultiple(IOrganizationService service)
{
string tableLogicalName = "contoso_sensordata";
List<Microsoft.Xrm.Sdk.Entity> entityList = new List<Microsoft.Xrm.Sdk.Entity>
{
new Microsoft.Xrm.Sdk.Entity(tableLogicalName)
{
Attributes =
{
{ "contoso_deviceId", "deviceid-001" },
{ "contoso_sensortype", "Humidity" },
{ "contoso_value", "40" },
{ "contoso_timestamp", "2023-05-01Z05:00:00"},
{ "partitionid", "deviceid-001" },
{ "ttlinseconds", 86400 }
}
},
new Microsoft.Xrm.Sdk.Entity(tableLogicalName)
{
Attributes =
{
{ "contoso_deviceId", "deviceid-002" },
{ "contoso_sensortype", "Humidity" },
{ "contoso_value", "10" },
{ "contoso_timestamp", "2023-05-01Z09:30:00"},
{ "partitionid", "deviceid-002" },
{ "ttlinseconds", 86400 }
}
}
new Microsoft.Xrm.Sdk.Entity(tableLogicalName)
{
Attributes =
{
{ "contoso_deviceId", "deviceid-002" },
{ "contoso_sensortype", "Pressure" },
{ "contoso_value", "20" },
{ "contoso_timestamp", "2023-05-01Z07:20:00"},
{ "partitionid", "deviceid-002" },
{ "ttlinseconds", 86400 }
}
}
};
// Create an EntityCollection populated with the list of entities.
EntityCollection entities = new(entityList)
{
EntityName = tableLogicalName
};
// Use CreateMultipleRequest
CreateMultipleRequest createMultipleRequest = new()
{
Targets = entities,
};
return service.Execute(request);
}
Use UpdateMultiple with elastic tables
You can use the UpdateMultiple
message with either the SDK for .NET or Web API.
This example uses the UpdateMultipleRequest class to update multiple rows of the contoso_SensorData
elastic table. These updates set the contoso_value
column.
public static Guid UpdateMultiple(IOrganizationService service)
{
string tableLogicalName = "contoso_sensordata";
List<Microsoft.Xrm.Sdk.Entity> entityList = new List<Microsoft.Xrm.Sdk.Entity>
{
new Microsoft.Xrm.Sdk.Entity(tableLogicalName)
{
Attributes =
{
{ "contoso_value", "45" },
{ "partitionid", "deviceid-001" }
}
},
new Microsoft.Xrm.Sdk.Entity(tableLogicalName)
{
Attributes =
{
{ "contoso_value", "15" },
{ "partitionid", "deviceid-002" }
}
}
new Microsoft.Xrm.Sdk.Entity(tableLogicalName)
{
Attributes =
{
{ "contoso_value", "25" },
{ "partitionid", "deviceid-002" }
}
}
};
// Create an EntityCollection populated with the list of entities.
EntityCollection entities = new(entityList)
{
EntityName = tableLogicalName
};
// Use UpdateMultipleRequest
UpdateMultipleRequest updateMultipleRequest = new()
{
Targets = entities,
};
return service.Execute(request);
}
Use DeleteMultiple with elastic tables
You can use the DeleteMultiple
message with either the SDK for .NET or Web API.
Note
With the SDK, you must use the OrganizationRequest class because the SDK doesn't currently have a DeleteMultipleRequest
class. Learn more about using messages with the SDK for .NET.
The following DeleteMultipleExample
static method uses the DeleteMultiple
message with the OrganizationRequest class to delete multiple rows from the contoso_SensorData
elastic table. The alternate key is used to include the partitionid
value to uniquely identify the rows.
public static void DeleteMultipleExample(IOrganizationService service)
{
string tableLogicalName = "contoso_sensordata";
List<EntityReference> entityReferences = new() {
{
new EntityReference(logicalName: tableLogicalName,
keyAttributeCollection: new KeyAttributeCollection
{
{ "contoso_sensordataid", "3f56361a-b210-4a74-8708-3c664038fa41" },
{ "partitionid", "deviceid-001" }
})
},
{ new EntityReference(logicalName: tableLogicalName,
keyAttributeCollection: new KeyAttributeCollection
{
{ "contoso_sensordataid", "e682715b-1bba-415e-b2bc-de9327308423" },
{ "partitionid", "deviceid-002" }
})
}
};
OrganizationRequest request = new(requestName:"DeleteMultiple")
{
Parameters = {
{"Targets", new EntityReferenceCollection(entityReferences)}
}
};
service.Execute(request);
}
Next steps
Learn how to use code to create and query JavaScript Object Notation (JSON) data in JSON columns in elastic tables.
See also
Elastic tables for developers
Create elastic tables using code
Query JSON columns in elastic tables
Elastic table sample code
Bulk operation messages (preview)