Use OrganizationServiceContext

In Microsoft Dataverse, you can use several classes that implement the IOrganizationService interface to access the Web services. Alternatively, you can use the OrganizationServiceContext generated by the Power Platform CLI pac modelbuilder build command to gain access to additional functionality. The OrganizationServiceContext class lets you track changes, manage identities and relationships, and gives you access to the LINQ provider. This class also contains a OrganizationServiceContext.SaveChanges() method that you use to submit the changes to data that the context is tracking. This class is based on the same concept as the DataServiceContext class in Windows Communication Foundation (WCF) Data Services.

To generate this class, provide a value for the --serviceContextName parameter when you generate early bound types. The Power Platform CLI pac modelbuilder build command uses this name as the name of the generated class. For more information about how to use the pac modelbuilder build command, see Generate classes for early-bound programming using the SDK for .NET. You can use the context instance when you develop applications, plug-ins, and workflow activities.

How to use the OrganizationServiceContext class

To instantiate the context class, you must pass the class constructor an object that implements the IOrganizationService interface. One option is to pass an instance of the CrmServiceClient class. For more information about the IOrganizationService interface, see IOrganizationService Interface.

The following code example shows how to create a new instance of the context class. In this example, the context class was named AdventureWorksCycleServiceContext by specifying that name using the --serviceContextName parameter when running the pac modelbuilder build command:

// For early bound types to work correctly, they have to be enabled on the
// client connection. Here, _service is a reference to a ServiceClient or
// CrmServiceClient object. Those types implement IOrganizationService.
_service.EnableProxyTypes();
AdventureWorksCycleServiceContext context = new   
    AdventureWorksCycleServiceContext(_service);  

After you create the context instance, you can begin to track creating, modifying, or deleting table rows (entity records).

The context must track any table row or relationship that you want to submit to Dataverse. For example, you could retrieve a row with a LINQ query and the context would track that row or you could use the OrganizationServiceContext.Attach(Entity) method to cause the context to begin tracking the row. You can work with data in a client application and create new rows, create related rows, and modify existing rows, but you must call the SaveChanges method on tracked rows to commit changes to Dataverse.

Track changes

To determine how a table row is tracked by the context, you can check the EntityState property on the derived Entity instance. You must notify the context to track a row from Dataverse by calling various methods or by using a LINQ query. All rows returned from a LINQ query are tracked by the service context.

You can add objects to the service context by calling one of the following methods in OrganizationServiceContext.

Method Use
AddObject(Entity) Adds a table row to the set of rows the service context is tracking. The status of the row in the context is set to Created. If the SaveChanges() method is called, this row will be created or added to the server.
Attach(Entity) Adds a table row to the set of rows the service context is tracking. The status of the row in the context is set to Unchanged. If the SaveChanges() method is called, this row will not be sent to the server unless its status changes.
CreateQuery(String) Adds the results of a query to the set of rows the service context is tracking.

In Dataverse, the service context lets you create and update relationships between table rows. The navigation properties generated by the Power Platform CLI pac modelbuilder build command and located in the early-bound classes let you access and change related row properties and relationships. The service context must be tracking the related row for the related row to be available to be updated on the server.

Use the following methods in OrganizationServiceContext to work with related rows and to add the row to the service context:

Method Use
AddRelatedObject(Entity, Relationship, Entity) Adds the target to the context. Calls the Attach(Entity) method on the target table row and then calls the AddLink(Entity, Relationship, Entity) method between the source row and the target (related) row.
AttachLink(Entity, Relationship, Entity) Adds the related row to the context for tracking. The status of the row in the context is set to Unchanged.
AddLink(Entity, Relationship, Entity) Creates a relationship between the source and target rows. Adds the target to the context. The status of the target row in the context is set to Created.
LoadProperty(Entity, String) Loads the related entity set for the specified relationship. Gives access to related rows by using the navigation property. Call the AddObject(Entity) method on the related row after you have accessed the row by using a navigation property on the parent row.
UpdateObject(Entity) Changes the state of the specified row in the OrganizationServiceContext to Modified.
DeleteObject(Entity) Changes the state of the specified row to be deleted in the OrganizationServiceContext.

Related rows for rows that you have retrieved using LINQ will be null until you use LoadProperty(Entity, Relationship) to retrieve them. The following code sample shows how to access Task rows associated with a specific Contact row.

Contact pam = context.ContactSet.Where(c => c.FirstName == "Pamela").FirstOrDefault();  
if (pam != null)  
{  
// pam.Contact_Tasks is null until you use LoadProperty  
    context.LoadProperty(pam, "Contact_Tasks");  
    Task firstTask = pam.Contact_Tasks.FirstOrDefault();  
}  

You can use the AddLink(Entity, Relationship, Entity) method to create associations. You must call the SaveChanges() method before the server is updated with the new link information.

The following code example shows how to create an association between a contact and an account.

Relationship relationship = new Relationship("account_primary_contact");  
context.AddLink(contact, relationship, account);  
context.SaveChanges();  

Save changes

The service context holds a graph of the table rows it is tracking. The order in which the service context processes table changes and submits them to the server is important. Updates to primary table rows are processed, and then related table rows are processed. If a value is set on the primary table row by the related table row, that value is used when updating data on the server.

If an error occurs when entity information is being saved, a new exception type that contains the SaveChangesResult is thrown by the OrganizationServiceContext.SaveChanges() method, regardless of the value of the SaveChangesOptions parameter that is passed into the method.

Use virtual methods when the context is changed

Sometimes it may be necessary to take actions based on changes in the OrganizationServiceContext. To facilitate this, virtual methods are provided to allow you to intercept or be notified of an operation. To take advantage of these methods, you either have to derive from OrganizationServiceContext or change the generated service context.The following table lists the virtual methods.

Method Description
OnBeginEntityTracking(Entity) Called after a table row is attached to the OrganizationServiceContext.
OnBeginLinkTracking(Entity, Relationship, Entity) Called after a link is attached to the OrganizationServiceContext.
OnEndEntityTracking(Entity) Called after a table row is detached from the OrganizationServiceContext.
OnEndEntityTracking(Entity) Called after a link is detached from the OrganizationServiceContext.
OnExecuting(OrganizationRequest) Called immediately before a request is submitted to Dataverse.
OnExecute(OrganizationRequest, OrganizationResponse) Called immediately after a request is submitted to the Dataverse, regardless of whether an exception occurred or not.
OnSavingChanges(SaveChangesOptions) Called before any operations occur after a call to SaveChanges.
OnSaveChanges(SaveChangesResultCollection) Called when all of the operations for a call to SaveChanges have completed, or when there is a failure.

Data Operations

You can modify, create, and delete objects in the service context, and Dataverse tracks the changes that you made to these objects. When the OrganizationServiceContext.SaveChanges() method is called, Dataverse generates and executes commands that perform the equivalent insert, update, or delete statements against data in Dataverse.

When working with early-bound Entity derived classes, you use the table name and column schema name to specify a table row or column to work with. Schema names are defined in EntityMetadata.SchemaName and AttributeMetadata.SchemaName, or you can use the class and property names shown in the code-generated file.The following sample shows how to assign a value to the email attribute of a new contact instance.

Contact contact = new Contact();
contact.EMailAddress1 = "sonny@contoso.com";  

Create a new table row

When you want to insert table data into Dataverse, you must create an instance of an entity type and add the object to an service context. The service context must be tracking the object before it can save the object to Dataverse.

When creating a new table row, you add the entity object to the service context by using the AddObject(Entity) method.

The following sample shows how to instantiate and save a new contact by using the entity data model. It also demonstrates how to access a custom attribute.

OrganizationServiceContext orgContext =new OrganizationServiceContext(svc);  
Contact contact = new Contact()     
 {  
   FirstName = "Charles",  
   LastName = "Brown",  
   Address1_Line1 = "123 Main St.",  
   Address1_City = "Des Moines",  
   Address1_StateOrProvince = "IA",  
   Address1_PostalCode = "21254",  
   new_twittername = "Chuck",  
   Telephone1 = "123-234-5678"  
 };   
orgContext.AddObject(contact);
orgContext.SaveChanges();  

There are several points to note in the previous code example. First, after a new contact is instantiated, you pass that contact object to the OrganizationServiceContext.AddObject(Entity) method so the context can begin tracking the object. The second point to note is that the new object is saved to the server by using the OrganizationServiceContext.SaveChanges() method.

After you add an object to the context and before the OrganizationServiceContext.SaveChanges() method is called, the context generates an ID for the new object. An exception that contains the SaveChangesResults is thrown from the SaveChanges() method if any updates to the Dataverse data fail.

Update a table row

Dataverse tracks changes to objects that are attached to the service context. To modify an existing table row, you must first add the object to the context. To add an object to the context, you must first retrieve the table row from Dataverse and then add the object to the context by using the Attach(Entity) method. Once the object is being tracked by the context, you can update the row by setting its columns (entity attributes).

The following sample shows how to update an account column by using early bound classes.

Account.EMailAddress1 = "Contoso-WebMaster@contoso.com";  

The following sample shows how to delete a column value.

Account.EMailAddress1 = null;  

There are two partial methods named OnPropertyChanging and OnPropertyChanged for each table row. These methods are called in the property setter. You can extend these methods by using partial classes to insert custom business logic.

Delete a table row

To delete a table row, the service context must be tracking the object. Once the object is on the context, you can use the DeleteObject(Entity) method to mark the object on the context for deletion. Note that the table row in Dataverse is not deleted until the OrganizationServiceContext.SaveChanges() method is called.

See also

LINQ query examples using OrganizationServiceContext with Dataverse
Generate classes for early-bound programming using the SDK for .NET
IOrganizationService
OrganizationServiceContext