Update and Delete entities using the Organization Service

This topic will include examples using both late-bound and early-bound programming styles. More information: Early-bound and Late-bound programming using the Organization service

Each of the examples uses a svc variable that represents an instance of a class that implements the methods in the IOrganizationService interface. For information about the classes that support this interface see IOrganizationService Interface.

Important

When updating an entity, only include the attributes you are changing. Simply updating the attributes of an entity that you previously retrieved will update each attribute even though the value is unchanged. This can cause system events that can trigger business logic that expects that the values have actually changed. This can also cause attributes to appear to have been updated in auditing data when in fact they haven’t actually changed.

You should create a new entity instance, set the id attribute and any attribute values you are changing, and use that entity instance to update the record.

Note

The metadata for attributes includes a RequiredLevel property. When this is set to SystemRequired, you cannot set these attributes to a null value. If you attempt this you will get error code -2147220989 with the message Attribute: <attribute name> cannot be set to NULL.

More information: Attribute requirement level

Basic update

Both of the examples below uses the IOrganizationService.Update method to set attribute values for an entity that was previously retrieved.

Use the Entity.Id property to transfer the unique identifier value of the retrieved entity to the entity instance used to perform the update operation.

Note

If you attempt to update a record without a primary key value you will get the error: Entity Id must be specified for Update.

If you don't have a primary key value, you can also update records using alternate keys. More information: Update with Alternate Key

Late-bound example

The following example shows using the Entity class to create an account record using the IOrganizationService.Update method.

var retrievedAccount = new Entity("account", new Guid("a976763a-ba1c-e811-a954-000d3af451d6"));

//Use Entity class with entity logical name
var account = new Entity("account");
account.Id = retrievedAccount.Id;
// set attribute values
// Boolean (Two option)
account["creditonhold"] = true;
// DateTime
account["lastonholdtime"] = DateTime.Now;
// Double
account["address1_latitude"] = 47.642311;
account["address1_longitude"] = -122.136841;
// Int
account["numberofemployees"] = 400;
// Money
account["revenue"] = new Money(new Decimal(2000000.00));
// Picklist (Option set)
account["accountcategorycode"] = new OptionSetValue(2); //Standard customer

//Update the account
svc.Update(account);

Early-bound example

The following example shows using the generated Account class to update an account record using the IOrganizationService.Update method.

var retrievedAccount = new Account()
{ Id = new Guid("a976763a-ba1c-e811-a954-000d3af451d6") };

var account = new Account();
account.Id = retrievedAccount.Id;
// set attribute values
// Boolean (Two option)
account.CreditOnHold = true;
// DateTime
account.LastOnHoldTime = DateTime.Now;
// Double
account.Address1_Latitude = 47.642311;
account.Address1_Longitude = -122.136841;
// Int
account.NumberOfEmployees = 400;
// Money
account.Revenue = new Money(new Decimal(2000000.00));
// Picklist (Option set)
account.AccountCategoryCode = new OptionSetValue(2); //Standard customer

//Update the account
svc.Update(account);

Use the UpdateRequest class

Instead of using the IOrganizationService.Update method, you can use either the late-bound Entity class or the generated early-bound entity classes with the UpdateRequest class by setting the entity instance to the Target property and then using the IOrganizationService.Execute method.

Note

The UpdateResponse has no properties. While it is returned by the Execute method, it isn't necessary to refer to it.

var request = new UpdateRequest()
{ Target = account };
svc.Execute(request);

When to use the UpdateRequest class

You must use the UpdateRequest class if you want to pass optional parameters. There are two cases where you might need special parameters.

You must also use the UpdateRequest class if you want to specify an optimistic concurrency behavior. More information: Optimistic concurrency behavior

In a similar manner to how you can Create related entities in one operation, you can also update related entities.

To do this, you have to retrieve an entity with the related records so that you can access the id values. More information: Retrieve with related records

Important

Updates to records are made in a specific order. First, primary entities are processed, and then related entities are processed. If a change is made by the primary entity for a lookup or related entity attribute, and then a related entity updates the same attribute, the related entity value is retained. In general, a lookup attribute value and its equivalent in the Entity.RelatedEntities for the same relationship should not be used at the same time.

Late-bound example

var account = new Entity("account");
account.Id = retrievedAccount.Id;

//Define relationships
var primaryContactRelationship = new Relationship("account_primary_contact");
var AccountTasksRelationship = new Relationship("Account_Tasks");

//Update the account name
account["name"] = "New Account name";

//Update the email address for the primary contact of the account
var contact = new Entity("contact");
contact.Id = retrievedAccount.RelatedEntities[primaryContactRelationship]
.Entities.FirstOrDefault().Id;
contact["emailaddress1"] = "someone_a@example.com";

List<Entity> primaryContacts = new List<Entity>();
primaryContacts.Add(contact);  
account.RelatedEntities.Add(primaryContactRelationship, new EntityCollection(primaryContacts));

// Find related Tasks that need to be updated
List<Entity> tasksToUpdate = retrievedAccount
.RelatedEntities[AccountTasksRelationship].Entities
.Where(t => t["subject"].Equals("Example Task")).ToList();

// A list to put the updated tasks
List<Entity> updatedTasks = new List<Entity>();

//Fill the list of updated tasks based on the tasks that need to but updated
tasksToUpdate.ForEach(t => {
var updatedTask = new Entity("task");
updatedTask.Id = t.Id;
updatedTask["subject"] = "Updated Subject";

updatedTasks.Add(updatedTask);
});

//Set the updated tasks to the collection
account.RelatedEntities.Add(AccountTasksRelationship, new EntityCollection(updatedTasks));

//Update the account and related contact and tasks
svc.Update(account);

Early-bound example

var account = new Account();
account.Id = retrievedAccount.Id;

//Update the account
account.Name = "New Account name";

//Update the email address for the primary contact of the account
account.account_primary_contact = new Contact
{ Id = retrievedAccount.PrimaryContactId.Id, EMailAddress1 = "someone_a@example.com" };

// Find related Tasks that need to be updated
List<Task> tasksToUpdate = retrievedAccount.Account_Tasks
    .Where(t => t.Subject.Equals("Example Task")).ToList();

// A list to put the updated tasks
List<Task> updatedTasks = new List<Task>();

//Fill the list of updated tasks based on the tasks that need to but updated
tasksToUpdate.ForEach(t =>
{
    updatedTasks
    .Add(new Task() {
        ActivityId = t.ActivityId,
        Subject = "Updated Subject"
    });

});

//Set the updated tasks to the collection
account.Account_Tasks = updatedTasks;

//Update the account and related contact and tasks
svc.Update(account);

Check for duplicate records

When updating an entity you may change the values so that the record represents a duplicate of another record. More information: Detect duplicate data using the Organization service

Update with Alternate Key

If you have an alternate key defined for an entity, you can use that in place of the primary key to update a record. You cannot use the early-bound class to specify the alternate key. You must use the Entity(String, KeyAttributeCollection) constructor to specify the alternate key.

If you want to use early bound types, you can convert the Entity to an early bound class using the ToEntity<T>() method.

The following example shows how to update an Account entity using an alternate key defined for the accountnumber attribute.

Important

By default there are no alternate keys defined for any entities. This method can only be used when the environment is configured to define an alternate key for an entity.

var accountNumberKey = new KeyAttributeCollection();
accountNumberKey.Add(new KeyValuePair<string, object>("accountnumber", "123456"));

Account exampleAccount = new Entity("account", accountNumberKey).ToEntity<Account>();
exampleAccount.Name = "New Account Name";
svc.Update(exampleAccount);

More information:

Use Upsert

Typically in data integration scenarios you will need to create or update data in Common Data Service from other sources. Common Data Service may already have records with the same unique identifier, which may be an alternate key. If an entity record exists, you want to update it. If it doesn't exist, you want to create it so that the data being added is synchronized with the source data. This is when you want to use upsert.

The following example uses UpsertRequest twice. The first time the account entity record is created, and the second time it is updated because it has an accountnumber value and there is an alternate key using that attribute.

For both calls, the UpsertResponse.RecordCreated property indicates whether the operation created a record or not.

// This environment has an alternate key set for the accountnumber attribute.

//Instantiate account entity with accountnumber value
var account = new Entity("account", "accountnumber", "0003");
account["name"] = "New Account";

//Use Upsert the first time
UpsertRequest request1 = new UpsertRequest() {
Target = account
};

//The new entity is created
var response1 = (UpsertResponse)svc.Execute(request1);
Console.WriteLine("Record Created: {0}",response1.RecordCreated); //true

//Update the name of the existing account entity
account["name"] = "Updated Account";

//Use Upsert for the second time
UpsertRequest request2 = new UpsertRequest()
{
Target = account
};

//The existing entity is updated.
var response2 = (UpsertResponse)svc.Execute(request1);
Console.WriteLine("Record Created: {0}", response2.RecordCreated); //false

More information: Use Upsert to insert or update a record

Delete

The IOrganizationService.Delete method simply requires the logical name of the entity and the unique identifier. Regardless of whether you are using late-bound Entity class or a generated early-bound entity class, you can use the following syntax for a delete operation by passing the Entity.LogicalName and Entity.Id properties.

svc.Delete(retrievedEntity.LogicalName, retrievedEntity.Id);

Or you can use the values:

svc.Delete("account", new Guid("e5fa5509-2582-e811-a95e-000d3af40ae7"));

Important

Delete operations can initiate cascading operations that may delete child records to maintain data integity depending on logic defined for the relationships in the environment. More information: Entity relationship behavior

Use the DeleteRequest class

You can use the DeleteRequest instead of the IOrganizationService.Delete method, but it is only required when you want to specify optimistic concurrency behavior.

var retrievedEntity = new Entity("account")
{
    Id = new Guid("c81ffd82-cd82-e811-a95c-000d3af49bf8"),
    RowVersion = "986335"

};

var request = new DeleteRequest()
{
    Target = retrievedEntity.ToEntityReference(),
    ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches
};

svc.Execute(request);

Optimistic concurrency behavior

You can specify the optimistic concurrency behavior for the operation by setting the ConcurrencyBehavior property of the UpdateRequest or DeleteRequest classes.

The logic to update or delete the record may be based on stale data. If the current data is different because it has changed since it was retrieved, optimistic concurrency provides a way to cancel an update or delete operation so you might retrieve it again and use the current data to determine whether to proceed.

To determine whether the entity has been changed, you don't need to compare all the values, you can use the RowVersion property to see if it has changed.

The following example will succeed only when:

  • The RowVersion of the entity in the database equals 986323
  • The account entity is enabled for optimistic concurrency (EntityMetadata.IsOptimisticConcurrencyEnabled is true)
  • The RowVersion property is set on the entity passed with the request.

If the RowVersion doesn't match, an error with the message The version of the existing record doesn't match the RowVersion property provided. will occur.

var retrievedAccount = new Account()
{   
    Id = new Guid("a976763a-ba1c-e811-a954-000d3af451d6"), 
    RowVersion = "986323" 
};

var account = new Account();
account.Id = retrievedAccount.Id;
account.RowVersion = retrievedAccount.RowVersion;

// set attribute values
account.CreditOnHold = true;

//Update the account
var request = new UpdateRequest()
{ 
    Target = account,
    ConcurrencyBehavior = ConcurrencyBehavior.IfRowVersionMatches 
};

try
{
    svc.Execute(request);
}
catch (FaultException<OrganizationServiceFault> ex)
{
    switch (ex.Detail.ErrorCode)
    {
        case -2147088254: // ConcurrencyVersionMismatch 
        case -2147088253: // OptimisticConcurrencyNotEnabled 
            throw new InvalidOperationException(ex.Detail.Message);
        case -2147088243: // ConcurrencyVersionNotProvided
            throw new ArgumentNullException(ex.Detail.Message);
        default:
            throw ex;
    }
}

More information:

Legacy update messages

There are several deprecated specialized messages that perform update operations. In earlier versions it was required to use these messages, but now the same operations should be performed using IOrganizationService.Update or UpdateRequest class with IOrganizationService.Execute

Deprecated message request Attribute(s) to update
AssignRequest <entity>.OwnerId
SetStateRequest <entity>.StateCode
<entity>.StatusCode
SetParentSystemUserRequest SystemUser.ParentSystemUserId
SetParentTeamRequest Team.BusinessUnitId
SetParentBusinessUnitRequest BusinessUnit.ParentBusinessUnitId
SetBusinessEquipmentRequest Equipment.BusinessUnitId
SetBusinessSystemUserRequest SystemUser.BusinessUnitId

<entity> refers to any entity that provides this attribute.

More information: Behavior of specialized update operations

See also

Create entities using the Organization Service
Retrieve an entity using the Organization Service
Associate and disassociate entities using the Organization Service