Редагувати

Поділитися через


Use messages with the SDK for .NET

It's important to understand that all data operations in Dataverse are defined as messages and the definitions of these messages are stored in Dataverse.

Every message has:

  • A unique name
  • A collection of input parameters
  • A collection of output parameters

There are three different ways you can use a message with the SDK for .NET as explained in the following sections:

Method Description
OrganizationRequest and OrganizationResponse classes Use these classes when you don't have SDK Request and Response classes. You might prefer to use this approach rather than generating SDK Request and Response classes when you know the message name and details of the input and output parameters.
SDK Request and Response classes Using these classes is the most common way you use messages. Many messages already have classes defined in the SDK for .NET. For custom actions, you can generate classes.
IOrganizationService methods The IOrganizationService provides some methods for common data operations. These methods are the quickest and easiest ways to perform most common data operations.

OrganizationRequest and OrganizationResponse classes

Note

Using OrganizationRequest and OrganizationResponse classes isn't the most common way to use messages in Dataverse. However, these classes demonstrate how messages are implemented. Understanding this is important to understand how other parts of Dataverse work.

You can use a message without SDK Request and Response classes.

  1. Use the OrganizationRequest class.

  2. Send the request using the IOrganizationService.Execute method, which returns an OrganizationResponse instance.

    The items in the OrganizationResponse.Results collection contains the results.

For example, if you want to create an account record, you could do it this way:

public static Guid OrganizationRequestExample(IOrganizationService service) {

   // Instantiate an Entity instance of type 'account'
    var account = new Entity("account");
    account["name"] = "Test account";

   // Instantiate a collection of parameters with one item 'Target',
   // set to the account entity instance
    ParameterCollection parameters = new ParameterCollection
    {
        { "Target", account }
    };

   // Instantiate an OrganizationRequest instance setting the
   // RequestName and Parameters
    OrganizationRequest request = new OrganizationRequest()
    {
        RequestName = "Create",
        Parameters = parameters
    };

   // Send the request using the IOrganizationService.Execute method
    OrganizationResponse response = service.Execute(request);

   // Parse the output parameter 'id' from the Results
    return (Guid)response.Results["id"];
}

To create an account record using this method, you need to know:

  • The name of the message: Create.
  • The name and data type of each input parameter: A single parameter named Target that is an Entity.
  • The name and data type of each output parameter: A single parameter named id that is a Guid.

This information stored in Dataverse. The SdkMessage table contains information about all the messages.

Dataverse manages information about the input and output parameters in private tables. You don't need to retrieve it because there's an easier way: using the SDK Request and Response classes.

SDK Request and Response classes

SDK Request and Response classes reduce the complexity of using the OrganizationRequest and OrganizationResponse classes. You don't need to know the name of the message and the input and output parameters because the classes include them.

The SDK for .NET contains definitions for common Dataverse messages in these namespaces:

Namespace Description
Microsoft.Xrm.Sdk.Messages Messages for common data operations and messages used to create and modify schema data, also known as metadata.
Microsoft.Crm.Sdk.Messages Messages for business logic and operations to support special capabilities to support Application Lifecycle Management (ALM) and apps. Some messages in this namespace support capabilities only found in Microsoft Dynamics 365 business applications.

These classes contain properties for all the input and output parameters.

  • The classes ending with *Request contain the properties for input parameters.

    These classes inherit from the OrganizationRequest class.

  • The classes ending with *Response contain the properties for output parameters.

    These classes inherit from the OrganizationResponse class.

For example, to create a record, you can use the Microsoft.Xrm.Sdk.Messages.CreateRequest class to prepare the request. Use IOrganizationService.Execute to send the request and the results are in form of an Microsoft.Xrm.Sdk.Messages.CreateResponse class instance.

The following example uses the Microsoft.Xrm.Sdk.Messages.CreateRequest class with a generated early-bound class for the account entity:

public static Guid CreateRequestExample(IOrganizationService service)
{
    // Instantiate an Account using generated early-bound class
    var account = new Account
    {
        Name = "Test account"
    };

    // Instantiate a CreateRequest
    var request = new CreateRequest
    {
        // Set the Target property
        Target = account
    };

    // Send the request using the IOrganizationService.Execute method
    // Cast the OrganizationResponse into a CreateResponse
    var response = (CreateResponse)service.Execute(request);

    // Return the id property value
    return response.id;
}

Generate classes for custom actions

There are other messages that don't have classes in the SDK. For example, solutions installed frequently include new message definitions defined as custom actions (custom API or custom process actions). Learn to create your own messages

Developers can generate *Request and *Response classes for the messages found in their environment using the Power Platform CLI pac modelbuilder build command. Use the --generateActions parameter to generate *Request and *Response classes. Learn more about generating early-bound classes for the SDK for .NET

Passing optional parameters with a request

There are several optional parameters you can pass to apply special behaviors to messages. You can't use the IOrganizationService methods when you use optional parameters. You must use the SDK Request classes or the OrganizationRequest class.

More information: Use optional parameters

IOrganizationService methods

In addition to the IOrganizationService.Execute method, the IOrganizationService Interface specifies that the following methods must also be implemented. These methods encapsulate the corresponding Request and Response classes:

Method Request/Response classes
Create CreateRequest / CreateResponse
Retrieve RetrieveRequest / RetrieveResponse
RetrieveMultiple RetrieveMultipleRequest / RetrieveMultipleResponse
Update UpdateRequest / UpdateResponse
Delete DeleteRequest / DeleteResponse
Associate AssociateRequest / AssociateResponse
Disassociate DisassociateRequest / DisassociateResponse

These methods simplify performing these operations with fewer lines of code. The following example uses the IOrganizationService.Create method to create an account record:

public static Guid CreateMethodExample(IOrganizationService service)
{
   // Instantiate an Account using generated early-bound class
    var account = new Account
    {
        Name = "Test account"
    };

    // Use the Create method to get the id of the created account.
    return service.Create(account);
}

As you can see, common data operations are streamlined using the IOrganizationService methods and other messages are made easier to use with the Request and Response classes in the SDK assemblies or generated with tooling. Most of the time you don't need to use the underlying OrganizationRequest and OrganizationResponse classes, but it's important to understand the different options available to use messages, especially when working with custom API and plug-ins.

Working with messages in plug-ins

The data describing an operation in a plug-in are in the form of IExecutionContext.InputParameters and IExecutionContext.OutputParameters.

In the PreValidation and PreOperation stages before the main operation of the event pipeline, the IExecutionContext.InputParameters contain the OrganizationRequest.Parameters.

With a custom API, your plug-in reads the IExecutionContext.InputParameters and contains logic to set the IExecutionContext.OutputParameters as part of the main operation stage.

After the main operation stage, in the PostOperation stage, the IExecutionContext.OutputParameters contain the OrganizationResponse.Results.

Understanding the structure of the messages helps you understand where to find the data you want to check or change within the plug-in.

More information:

Private Messages

Microsoft Dataverse contains some messages that aren't intended for non-Microsoft developers to use. Microsoft added these messages enable feature functionality, but non-Microsoft solutions can also add them with the custom API feature. The SdkMessage.IsPrivate property tells you which messages are private.

Caution

You should not use private messages unless you created them as a custom API. By marking a message as private, the solution publisher is explicitly calling out that they do not support other apps to use the message. They may remove the message or introduce breaking changes at any time. Use of these messages by anyone other than the solution publisher are not supported. Calling private messages from plug-ins is not supported.

More information: Create and use custom APIs

Table support for messages

Some messages can be used with multiple tables. For example the Create, Update, and Delete messages can be used for most tables, but some tables may not support all the common messages.

This information is stored in the SdkMessageFilter table. You can query this table to determine if you can use a message for a table.

Use this static method to get a list of names of messages that can work with a table:

/// <summary>
/// Write the names of messages for a table to the console
/// </summary>
/// <param name="service">The authenticated IOrganizationService to use</param>
/// <param name="tableName">The logical name of the table</param>
static void OutputTableMessageNames(IOrganizationService service, string tableName)
{
    var query = new QueryExpression(entityName: "sdkmessagefilter")
    {
        Criteria =
        {
            Conditions =
            {
                new ConditionExpression(
                    attributeName:"primaryobjecttypecode",
                    conditionOperator: ConditionOperator.Equal,
                    value: tableName)
            }
        },
        // Link to SdkMessage to get the names
        LinkEntities =
        {
            new LinkEntity(
                linkFromEntityName:"sdkmessagefilter",
                linkToEntityName: "sdkmessage",
                linkFromAttributeName: "sdkmessageid",
                linkToAttributeName: "sdkmessageid",
                joinOperator: JoinOperator.Inner)
            {
                EntityAlias = "sdkmessage",
                Columns = new ColumnSet("name"),
                LinkCriteria =
                {
                    Conditions =
                    {
                        // Don't include private messages
                        new ConditionExpression("isprivate", ConditionOperator.Equal, false)
                    }
                }
            }
        }
    };

    EntityCollection results = service.RetrieveMultiple(query);

        foreach (var entity in results.Entities)
        {
            Console.WriteLine(((AliasedValue)entity["sdkmessage.name"]).Value);
        }
}

If you use this method setting the tableName parameter to account, you get results that include these message names:

Output:

Assign
Create
Delete
GrantAccess
Merge
ModifyAccess
Retrieve
RetrieveMultiple
RetrievePrincipalAccess
RetrieveSharedPrincipalsAndAccess
RevokeAccess
SetState
SetStateDynamicEntity
Update

Note

Some of these messages are deprecated. SetState and SetStateDynamicEntity still exist, but you should use Update instead.

Message support for tables

Some messages can only be used with specific tables. For example, you can only use the RetrieveUnpublishedMultiple message with a specific set of tables that contain data that can be published.

This information is stored in the SdkMessageFilter table. You can query this table to determine which tables can be used for a specific message.

Use this static method to get a list of names of tables that can be used with a message:

/// <summary>
/// Write the names of tables for a message to the console
/// </summary>
/// <param name="service">The authenticated IOrganizationService to use</param>
/// <param name="messageName">The name of the message</param>
static void OutputTablesForMessage(IOrganizationService service, string messageName) {

    var query = new QueryExpression(entityName: "sdkmessage")
    {
        Criteria = { 
            Conditions =
            {
                new ConditionExpression(
                        attributeName: "name",
                        conditionOperator: ConditionOperator.Equal,
                        value: messageName)
            }
        },
        LinkEntities = {
            new LinkEntity(
                linkFromEntityName:"sdkmessage",
                linkToEntityName: "sdkmessagefilter",
                linkFromAttributeName: "sdkmessageid",
                linkToAttributeName: "sdkmessageid",
                joinOperator: JoinOperator.Inner)
            {
                EntityAlias = "sdkmessagefilter",
                Columns = new ColumnSet("primaryobjecttypecode"),
            }
        }
    };

            EntityCollection results = service.RetrieveMultiple(query);
            List<string> names = new List<string>();

            foreach (var entity in results.Entities)
            {
                names.Add(((AliasedValue)entity["sdkmessagefilter.primaryobjecttypecode"]).Value as string);
            }

            names.Distinct().ToList().ForEach(name => Console.WriteLine(name));
}

If you use this method setting the messageName parameter to RetrieveUnpublishedMultiple, you get results that include these table names:

Output:

organizationui
systemform
savedquery
savedqueryvisualization
sitemap
hierarchyrule
appmodule
appconfig
appconfiginstance
webresource
mobileofflineprofile
mobileofflineprofileitem
mobileofflineprofileitemassociation
navigationsetting
appelement
appsetting
teammobileofflineprofilemembership
usermobileofflineprofilemembership

Note

  • For certain messages, you may find that placeholder values are returned, such as DeletedEntity for objectTypeCode=11478 or new_system_donotuseentity_rp53fd1p1ekxpa_t12_b71d6344c5. These aren't valid table names. Disregard these values.

  • This query will also return Private tables. In the previous example: organizationui,hierarchyrule, appelement, and appsetting are private tables and not supported for use.

See also

Table operations using the SDK for .NET
Use ExecuteAsync to execute messages asynchronously
Execute messages in a single database transaction
Execute multiple requests using the SDK for .NET