Dynamics 365 Tooling – Object of type ‘Microsoft.Xrm.Sdk.Entity’ cannot be converted to type ‘Type’

I think that most of you who are writing applications connecting to Dynamics 365 already wrote thousands of some similar code:


var crmClient = new CrmServiceClient(ConfigurationManager.ConnectionStrings["D365"].ConnectionString);
if (crmClient.IsReady)
{
using (var ctx = new OrganizationServiceContext(crmClient))
{
var result = ctx.CreateQuery<Contact>()
.Where(a => a.FirstName == "Jan")
.Select(a => new Contact
{
Id = a.Id,
FirstName = a.FirstName
})
.ToList();
Console.WriteLine(result.Count);
}
}

view raw

ConnectToCrm.cs

hosted with ❤ by GitHub

It’s simply connecting to Dynamics 365 using connection string and querying contacts. Connection string looks like that:

<connectionStrings>
<add name=”D365″ connectionString=”Url=https://organization.crm4.dynamics.com; Username=user@domain.com; Password=password; authtype=Office365;” />
</connectionStrings>

For many years it was working like a charm. If you have upgraded to latest XrmTooling version which is 9.0.2.7 at the time of writing this post, this code will throw exception:

Capture

System.ArgumentException: ‘Object of type ‘Microsoft.Xrm.Sdk.Entity’ cannot be converted to type ‘Contact’.’

Some of you may remember this error and while googling for it you will come across some posts from years ago when it was simply a mistake of not calling “EnableProxyTypes” function. This is from times when we had to manually create OrganizationServiceProxy object. Without calling “EnableProxyTypes” service did not know how to deserialize result into proper object (so Contact in this example). Some more details were provided by James Wood and myslef in this StackOverflow question

So what is the fix, because I don’t believe anybody will rewrite the code to cast all Entities to Contacts manually. Fortunately there is a quick fox for that, simply add SkipDiscovery=false to your query string, because it looks like it’s default value changed from false to true between versions 9.0.2.5 and 9.0.2.7. So your connection string should look like this:

<connectionStrings>
<add name=”D365″ connectionString=”Url=https://organization.crm4.dynamics.com; Username=user@domain.com; Password=password; authtype=Office365; SkipDiscovery=false;” />
</connectionStrings>

And it should work 🙂

 

Creating empty XRM Environment in PowerApps

If you are following the latest news regarding Dynamics 365 ecosystem, you are probably aware of the fact that Microsoft decided to put everything upside down, change the naming of every technology and use the old names with some new things. Of course I’m exaggerating a little here, but some changes are really crucial and can cause confusion. I’m also pretty sure that some of them are not final, so I will probably have to go back to this post in few months to update it.

There are a lot of great blog posts already there but the really short summary of what we know so far:

  1. There will be a common data model for all Microsoft business apps (Dynamics 365 world apps), which is called currently Common Data Model. Basically, we want to have the same Account in CRM as the Account in Talent or Marketing, currently in every system, all the entities do not follow any common schema. CDM specification is held on github and you can check out its current state here: https://github.com/Microsoft/CDM
  2. There will also be a data service that would allow CRUD for all the entities across your Environment. Basically you will reuse the same Account entity in your CRM and in your ERP, so any integrations will not be necessary (or at least fairly easy to develop). This will be called Common Data Service for Apps (and has nothing to do with current CDS)
  3. PowerApps is now becoming a term that includes all customizable business apps  – the old PowerApps from now on called Canvas Apps and applications like Dynamics 365 Customer Engagement are called Model Apps. Both types of apps can use CDS for Apps and CDM. Basically this is our new UI layer over CDS and CDM
  4. The underlying technology for all these new toys is Dynamics 365 CE platform, also known as XRM platform. So if you know Dynamics CRM, you should already be familiar with everything that CDS for Apps, CDM and Model Apps in PowerApps have to offer.
  5. What we currently know as Organization, we should from now on refer to as an Instance. Once the whole new ecosystem is finished, it should be called Environment 

That’s the shortest summary which highlights everything  important from the perspective of technical guys like developers and solution architects. Looks pretty exciting right? At least for Dynamics CRM Developers, who are currently becoming the specialists of the development platform for all new technologies (as a comparison, look at Office 365 applications, where most of applications are SharePoint based).

Ok, so that’s still a theory, we can take that into our minds and wait until the machinery starts running, right? Wrong, we already can start playing with all of that!

I would like to show you how to get started with some new Model App based on CDM.

You will need a Dynamics 365 9.0 environment (can be a trial of course). Log into your Organization and go to the following link:

https://admin.powerapps.com/environments

You should see something like that

1

Now click on New environment button

2

Enter Environment name (for me it’s XRM), choose your region and Environment type (I would not go with Production just yet, but if you want, be my guest). Click Create environment.

3

Ok so we have a new Environment, so in the world that we already know – we have a CRM instance but without any Organization. That’s not very useful, so let’s create a database.

4

Looks like the Organization creator for Dynamics CRM installation, doesn’t it? So we have to put there our desired Currency and Language. I unchecked the sample data, as I would like this app to be as clean as possible. After clicking Create database we will have to wait few minutes:

5

After DB is provisioned we will see something like that:

6

If we will now switch to the old, well-known Dynamics 365 Administration Center, we will see that we have a new CRM organization:

7

Let’s open it! We will see quote “raw” CRM organization:

8

No Sales, Service, Marketing tiles, only Settings. Let’s see what we have in Customizations:

9

You can see only the entities from Common Data Model, so there are only System entities that are required to use all the features of the system (Users, Teams, Views, etc) and some really basic entities (like Account and Contact) – there are no Cases, Opportunities, Leads, Goals etc. Simply a clean XRM platform that we can use to create a totally custom application!

No let’s see the new customizations interface. Open the following link:

https://web.powerapps.com/

10

And on top right corner choose your newly created Environment. Now open your entities:

10.1

Now click on some entity, I chose Account entity:

12

On top pane, you can see all the well-known sections like Fields, Keys, Relationships, Views etc. Let’s create a new field called Short Name

13

After clicking on Save entity, open again Customizations in your empty Organization

14

The field is there!

Once you start playing with all the customizations, you will see that there are still many places where you are redirected to the “old-CRM2011-UI” because there is still no new UI for that, but you can already see that things are heading in the right direction.

For sure Microsoft will need some more time to finish all this new features. Let’s keep our fingers crossed that everything goes as expected, because future looks really exciting!

Dynamics 365 Server-side validation

 

Defining required and optional fields are always an important part of every Dynamics 365 project that I have ever worked on. Have you ever thought properly about the server-side validation on that matter? From my experience, Dynamics CRM Customizers/Developers usually just create fields with proper value in  “Field requirement” field. I hope that everyone is aware of the fact that this is only client-side validation, which can be easily changed from client-side code (JS) or Business Rules. This usually is enough, because CRM users were not aware of how this application works and would not run any malicious JavaScript on the form (wishful thinking…). Unfortunately, there are some great Chrome extensions, that allow to disable all the client-side validations in a single click and more and more users are aware of their existence, “hacking” the input for CRM for their convenience. Of course, more advanced CRM users were always aware of the fact that they can override the system behavior by using simple Import or some tools from XrmToolBox, so they even did not not have to know programming and CRM SDK. That’s why it’s sometimes really important to prepare some sort of Server-Side validation logic for really vital data, to avoid situation when a salesman does not fill Tax Number for a client, because he did not have time to search for the data before putting it inside CRM.

Maybe you already know that but there is a built-in functionality for Server-Side validation in CRM. These are simply Business Rules, which are set to “Entity” scope. Most of you know the Business Rules as something that allows CRM Power Users to perform some simplified tasks on the form without writing any JavaScript. Although I am still not a big fan of Business Rules as something that replaces JS, they come in very handy when it comes to this server-side validation. Just have a look at the following rule:

Configuration

If we register it for “Entity” scope we will not be able to save the record not only using standard forms:

Message

but also using SDK or Import:

Message2

What if we have so many entities and fields that creating and maintaining Business Rules for all of them is a nightmare? It’s quite easy to create a plugin that we can register on Pre-Validation and it will do all the dirty job for us. Have a look at this example:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
namespace Validation.Plugin.CoreLib
{
public class ValidationPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = serviceFactory.CreateOrganizationService(null);
var entity = GetEntityFromContext(context);
var allRequredAttributes = GetAllRequiredAttributes(service, entity);
foreach (var attribute in allRequredAttributes)
{
var errorMessage = $"Attribute {attribute.LogicalName} is required. Please specify value for this attribute before saving record.";
if (!entity.Contains(attribute.LogicalName))
{
throw new InvalidPluginExecutionException(errorMessage);
}
else
{
var value = entity[attribute.LogicalName];
if (value == null)
{
throw new InvalidPluginExecutionException(errorMessage);
}
else if (value is string && string.IsNullOrEmpty(value as string))
{
throw new InvalidPluginExecutionException(errorMessage);
}
}
}
}
private Entity GetEntityFromContext(IPluginExecutionContext localContext)
{
var target = localContext.InputParameters["Target"] as Entity;
var entity = new Entity(target.LogicalName);
if (localContext.PreEntityImages.ContainsKey("PreImage"))
{
var preImage = localContext.PreEntityImages["PreImage"];
foreach (var preImageAttribute in preImage.Attributes)
{
entity[preImageAttribute.Key] = target.Contains(preImageAttribute.Key) ? target[preImageAttribute.Key] : preImageAttribute;
}
}
foreach (var targetAttribute in target.Attributes)
{
entity[targetAttribute.Key] = targetAttribute.Value;
}
return entity;
}
private IEnumerable<AttributeMetadata> GetAllRequiredAttributes(IOrganizationService service, Entity entity)
{
var metadataRequest = new RetrieveEntityRequest()
{
LogicalName = entity.LogicalName,
EntityFilters = Microsoft.Xrm.Sdk.Metadata.EntityFilters.Attributes
};
var metadata = (RetrieveEntityResponse)service.Execute(metadataRequest);
var attributeMetadataCollection = metadata.EntityMetadata.Attributes;
var allRequredAttributes = attributeMetadataCollection.Where(am => am.RequiredLevel.Value == AttributeRequiredLevel.ApplicationRequired);
return allRequredAttributes;
}
}
}

You should register this plugin on Pre-Validation of Create/Update of any entity (for Update you should also register PreImage). It will retrieve the entity metadata and check all the required attributes. If any of this attributes is missing, it will fire an exception that will prevent saving of the bad data. This is just an example, the full code of the plugin can be found here:

https://github.com/pawelgradecki/Validation.Plugin

List of undocumented SDK messages

I believe that very often, searching for a clue if something can be done in Dynamics 365, you can stumble upon some Message that is not documented in Microsoft Dynamics 365 SDK, but works perfectly fine (like for example ExportToExcel message). I decided to prepare a list of all those messages, together with input and output parameters that they are using. Of course, I will not provide you the documentation – you must experiment with them by yourself. For most of them it’s really clear from their name, what functionality they provide. Remember that all these messages are not in official SDK – they are used internally by CRM, but I would think twice before I would decide to use them in some project (because of their internal nature, they can become obsolete in future releases of CRM without any notice from Microsoft, not like the messages from the offical SDK, but personally I think such scenario is highly unlikely), but some of them look like a great candidates for usage in tools or some helper code for developers. Also if you will find something really useful, please don’t hesitate to post crm idea to add such message into official SDK.

How you can call this messages? It’s simply executing OrganizationRequest with proper parameters from the below table. Look at the following example:


public void DoRequest(IOrganizationService service)
{
var request = new OrganizationRequest("ResolveEmailAddress");
request.Parameters["EmailAddresses"] = "test@test.com";
request.Parameters["ObjectTypeCodes"] = new int[] { 1, 2 };
var response = service.Execute(request);
if (response.Results.Contains("Entities"))
{
var result = (EntityCollection)response.Results["Entities"];
//go nuts with the result
}
}

view raw

DoRequest.cs

hosted with ❤ by GitHub

Basically Input Parameters should be inserted in Parameters collection of the OrganizationRequest, and Output Parameters can be obtained from Results collection of the OrganizationResponse.

Here is the promised list of all Messages. Remember that this is to be used only by experienced Dynamics 365 Developers. If you are not sure what are you doing – don’t do it. And don’t test this messages on any important environments – I would suggest to create some local instance for yourself as a playground.

Having said that – have fun!

Request name Parameters
AddDynamicProperty

Input:

  • RegardingObject: Microsoft.Xrm.Sdk.EntityReference
  • DynamicProperty: Microsoft.Xrm.Sdk.Entity

Output:

  • Id: System.Guid
  • DynamicPropertyId: System.Guid
AddEditAppModule

Input:

  • AppModuleComponents: Microsoft.Xrm.Sdk.EntityCollection
  • AppModule: Microsoft.Xrm.Sdk.Entity
  • IsNew: System.Boolean
  • RetainAppModuleComponents: System.Boolean

Output:

  • AppModuleComponentIds: System.Guid[]
AddMembersByFetchXmlList

Input:

  • ListId: System.Guid
  • FetchXml: System.String

Output:

AddOrEditLocation

Input:

  • LocationName: System.String
  • AbsUrl: System.String
  • RegardingObjectId: System.String
  • RelativePath: System.String
  • RegardingObjType: System.Int32
  • ParentType: System.Int32
  • ParentId: System.String
  • IsAddOrEditMode: System.Boolean
  • IsCreateFolder: System.Boolean
  • DocumentId: System.String

Output:

  • LocationId: System.String
ApplyProfileRule

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference

Output:

AssignAllRecordsTeam

Input:

  • OldOwnerId: System.Guid
  • OldOwnerType: System.Int32
  • NewOwnerId: System.Guid
  • NewOwnerType: System.Int32

Output:

AssociateKnowledgeArticle

Input:

  • RegardingObjectId: System.Guid
  • RegardingObjectTypeCode: System.Int32
  • AssociationRelationshipName: System.String
  • KnowledgeArticleId: System.Guid

Output:

AuthenticateAndFetchACIData

Input:

  • EntityName: System.String
  • EntityRecordId: System.Guid
  • ACIRequestURI: System.String

Output:

  • ACIResponse: System.String
BestTimeToEmail

Input:

  • EntityReferenceCollection: Microsoft.Xrm.Sdk.EntityReferenceCollection

Output:

  • PreferredTime: System.DateTime
BuildTopicModel

Input:

  • TopicModelId: System.Guid

Output:

BulkDeleteImportedRecords

Input:

  • TargetEntityName: System.String
  • ImportSequenceNumber: System.Int32
  • ImportId: System.Guid
  • DeleteImportHistory: System.Boolean
  • JobName: System.String
  • SendEmailNotification: System.Boolean
  • ToRecipients: System.String
  • CCRecipients: System.String
  • RecurrencePattern: System.String
  • SourceImportId: System.Guid

Output:

  • JobId: System.Guid
BulkOperationStatusClose

Input:

  • BulkOperationId: System.Guid
  • FailureCount: System.Int32
  • SuccessCount: System.Int32
  • StatusReason: System.Int32
  • ErrorCode: System.Int32

Output:

CalculateTriggerDateTime

Input:

  • CalendarId: System.Guid
  • StartTime: System.DateTime
  • TriggerDuration: System.Int32

Output:

  • result: System.DateTime
CanCloseOpportunity

Input:

  • OpportunityId: System.Guid
  • QuoteId: System.Guid
  • NewStatus: System.Int32

Output:

  • CanClose: System.Boolean
CanUserSendEmail

Input:

Output:

  • HasPrivileges: System.Boolean
CheckClientCompatibility

Input:

  • CrmClientVersion: System.String

Output:

  • Result: System.Int32
CheckInDocument

Input:

  • Entity: Microsoft.Xrm.Sdk.Entity
  • CheckInComments: System.String
  • RetainCheckout: System.Boolean

Output:

CheckInSharePointDocument

Input:

  • DocumentId: System.String
  • CheckInComments: System.String
  • RetainCheckout: System.Boolean
  • SiteUrl: System.String
  • DocumentLocation: System.String
  • ReferencedEntity: System.String

Output:

CheckNotifications

Input:

  • Events: System.Int32[]
  • LastChecked: System.DateTime

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
CheckoutDocument

Input:

  • Entity: Microsoft.Xrm.Sdk.Entity

Output:

CheckoutSharePointDocument

Input:

  • DocumentId: System.String
  • SiteUrl: System.String
  • DocumentLocation: System.String
  • ReferencedEntity: System.String

Output:

CheckRouterCompatibility

Input:

  • CrmRouterVersion: System.String

Output:

  • Result: System.Int32
CleanUpBulkOperation

Input:

  • BulkOperationId: System.Guid
  • BulkOperationSource: System.Int32

Output:

CloneProductAssociation

Input:

  • Source: Microsoft.Xrm.Sdk.EntityReference

Output:

CloseOpportunity

Input:

  • OpportunityId: Microsoft.Xrm.Sdk.EntityReference
  • Status: Microsoft.Xrm.Sdk.OptionSetValue
  • ActualRevenue: Microsoft.Xrm.Sdk.Money
  • CompetitorId: Microsoft.Xrm.Sdk.EntityReference
  • CloseDate: System.DateTime
  • Description: System.String

Output:

ConfigureReportingDataConnector

Input:

  • DataProviderType: System.Int32

Output:

ConvertActivity

Input:

  • ActivityId: System.Guid
  • ActivityEntityName: System.String
  • TargetEntity: Microsoft.Xrm.Sdk.Entity
  • TargetEntityName: System.String
  • CreateCampaignResponse: System.Boolean

Output:

  • RecordId: System.Guid
ConvertCampaignResponse

Input:

  • CampaignResponse: Microsoft.Xrm.Sdk.EntityReference
  • EntityName: System.String
  • CreateOpportunityForExistingCustomer: System.Boolean
  • Customer: Microsoft.Xrm.Sdk.EntityReference
  • Currency: Microsoft.Xrm.Sdk.EntityReference
  • Owner: Microsoft.Xrm.Sdk.EntityReference

Output:

  • EntityReference: Microsoft.Xrm.Sdk.EntityReference
CopySharePointDocuments

Input:

  • DestinationLocation: System.String
  • AbsoluteUrls: System.String[]
  • RelativeUrls: System.String[]
  • Source: System.String

Output:

  • Status: System.String
CreateAndAssociate

Input:

  • RegardingObjectId: System.Guid
  • RegardingObjectTypeCode: System.Int32
  • AssociationRelationshipName: System.String
  • Article: Microsoft.Xrm.Sdk.Entity

Output:

CreateDocumentLibraries

Input:

  • DocumentLibraryNames: System.String
  • Url: System.String

Output:

  • DocumentLibraryResult: System.String
CreateEmailReplyDraft

Input:

  • MessageId: System.String
  • ReplyText: System.String

Output:

  • MailWebLink: System.String
CreateFolderAndNewDocuments

Input:

  • FolderName: System.String
  • FileNameList: System.String[]
  • RegardingObjectType: System.Int32
  • RegardingObjectId: System.String
  • LocationType: System.Int32
  • ParentLocationId: System.String

Output:

  • LocationId: System.Guid
CreateFolder

Input:

  • FolderName: System.String
  • RegardingObjectType: System.Int32
  • RegardingObjectId: System.String
  • DocumentType: System.Int32
  • ParentLocationId: System.String
  • SiteType: System.Int32
  • ValidateFolder: System.Boolean

Output:

  • LocationId: System.Guid
CreateFromTemplate

Input:

  • WizardXml: System.String
  • IsOrgReport: System.Boolean

Output:

  • id: System.Guid
CreateOrUpdateImportMapFromApp

Input:

  • MapXml: System.String
  • ColMappingIdsToDelete: System.Guid[]

Output:

  • ImportMapId: System.Guid
CreateOutlookSubscriptionSubscriptionClients

Input:

  • Target: Microsoft.Xrm.Sdk.Entity

Output:

  • id: System.Guid
CreatePostRelationships

Input:

  • EntityLogicalName: System.String

Output:

CreateProducts

Input:

  • Entities: Microsoft.Xrm.Sdk.EntityCollection
  • ParentEntity: Microsoft.Xrm.Sdk.Entity

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
CreateRecommendationModelVersion

Input:

  • RecommendationModelId: System.Guid
  • Target: Microsoft.Xrm.Sdk.Entity

Output:

  • id: System.Guid
CreateSchedule

Input:

  • ReportId: System.Guid
  • ScheduleXml: System.String
  • ParameterXml: System.String
  • ScheduledReportName: System.String

Output:

  • id: System.Guid
CreateSharePointDocumentLibraries

Input:

  • DocumentLibraryNames: System.String[]
  • Url: System.String

Output:

  • Status: System.String[]
CreateSharePointFolderAndUploadDocuments

Input:

  • FolderName: System.String
  • Path: System.String
  • DocumentLibrary: System.String
  • SiteUrl: System.String
  • IsNoteBookFolder: System.Boolean
  • FileName: System.String[]
  • DocumentContents: System.String[]

Output:

  • FileEditUrls: System.String[]
CreateSharePointFolder

Input:

  • FolderName: System.String
  • Path: System.String
  • DocumentLibrary: System.String
  • SiteUrl: System.String
  • IsNoteBookFolder: System.Boolean

Output:

CreateSubscriptionSyncInfo

Input:

  • Target: Microsoft.Xrm.Sdk.Entity

Output:

CreateWithMappingImportMap

Input:

  • ImportMap: Microsoft.Xrm.Sdk.Entity
  • ColumnMappings: Microsoft.Xrm.Sdk.EntityCollection
  • PickListMappings: Microsoft.Xrm.Sdk.EntityCollection
  • LookUpMappings: Microsoft.Xrm.Sdk.EntityCollection
  • OwnerMappings: Microsoft.Xrm.Sdk.EntityCollection
  • TransformationMappings: Microsoft.Xrm.Sdk.EntityCollection
  • TransformationParameterMappings: Microsoft.Xrm.Sdk.EntityCollection

Output:

  • Id: System.Guid
DebugCacheGetContents

Input:

  • CacheName: System.String

Output:

  • CacheItems: Microsoft.Crm.Sdk.Messages.CacheItem[]
DebugCacheGetSize

Input:

  • CacheName: System.String
  • OutputFormat: System.String
  • IncludeLogs: System.Boolean

Output:

  • CacheSizeDetails: System.String
DebugFlushCache

Input:

  • CacheName: System.String

Output:

DebugGenerateFault

Input:

  • FaultType: Microsoft.Crm.Sdk.Messages.FaultType

Output:

DebugRetrievePipelinePerformanceResult

Input:

  • WebRequestId: System.Guid

Output:

  • PerformanceResult: System.String
DebugTraceBufferGetContents

Input:

Output:

  • TraceBuffer: System.String[]
  • TraceBufferSizeSetting: System.Int32
DeleteDocument

Input:

  • Entities: Microsoft.Xrm.Sdk.EntityCollection

Output:

DeleteRecommendationModel

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference

Output:

  • IsModelDeletedInAzure: System.Boolean
DeleteRecommendationModelVersion

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference

Output:

  • IsBuildIdDeletedInAzure: System.Boolean
DeleteSharePointDocument

Input:

  • DocumentIds: System.String[]
  • SiteUrl: System.String
  • DocumentLocation: System.String
  • ReferencedEntity: System.String

Output:

DisassociateKnowledgeArticle

Input:

  • RegardingObjectId: System.Guid
  • RegardingObjectTypeCode: System.Int32
  • AssociationRelationshipName: System.String
  • KnowledgeArticleId: System.Guid

Output:

DiscardDocumentCheckout

Input:

  • Entity: Microsoft.Xrm.Sdk.Entity

Output:

DiscardSharePointDocumentCheckout

Input:

  • DocumentId: System.String
  • SiteUrl: System.String
  • DocumentLocation: System.String
  • ReferencedEntity: System.String

Output:

EditDocumentProperties

Input:

  • Entity: Microsoft.Xrm.Sdk.Entity

Output:

EditSharePointDocumentProperties

Input:

  • DocumentId: System.String
  • FullName: System.String
  • Title: System.String
  • SiteUrl: System.String
  • DocumentLocation: System.String
  • ReferencedEntity: System.String

Output:

EnableNewFormsForAllUsers

Input:

Output:

ExecuteDataPerformanceAction

Input:

  • QueryingUnitId: System.Guid
  • ActionName: System.String

Output:

ExecuteQuickFind

Input:

  • SearchText: System.String
  • EntityGroupName: System.String
  • EntityNames: System.String[]

Output:

  • Result: Microsoft.Xrm.Sdk.QuickFindResultCollection
ExpandWorkflows

Input:

  • ExpansionTaskId: System.Guid

Output:

ExportDynamicToExcel

Input:

  • View: Microsoft.Xrm.Sdk.EntityReference
  • FetchXml: System.String
  • LayoutXml: System.String
  • ExportType: Microsoft.Crm.Sdk.Messages.ExportDynamicToExcelType
  • PostData: System.String
  • IsRefresh: System.Boolean
  • QueryApi: System.String
  • QueryParameters: Microsoft.Crm.Sdk.Messages.InputArgumentCollection

Output:

  • ExcelFile: System.Byte[]
ExportFullSolution

Input:

  • SolutionName: System.String

Output:

  • ExportSolutionFile: System.Byte[]
ExportLinearMappingsImportMap

Input:

  • ImportMapId: System.Guid

Output:

  • MappingsXml: System.String
ExportTemplateToExcelOnline

Input:

  • Template: Microsoft.Xrm.Sdk.EntityReference
  • FetchXml: System.String
  • QueryApi: System.String
  • QueryParameters: Microsoft.Crm.Sdk.Messages.InputArgumentCollection

Output:

  • EditLink: System.String
ExportTemplateToExcel

Input:

  • EntityLocalizedDisplayName: System.String
  • LayoutXml: System.String
  • FetchXml: System.String

Output:

  • ExcelFile: System.Byte[]
ExportTemplateToWord

Input:

  • EntityTypeCode: System.Int32
  • SelectedEntities: System.String

Output:

  • WordFile: System.Byte[]
ExportToExcelOnline

Input:

  • View: Microsoft.Xrm.Sdk.EntityReference
  • FetchXml: System.String
  • LayoutXml: System.String
  • QueryApi: System.String
  • QueryParameters: Microsoft.Crm.Sdk.Messages.InputArgumentCollection

Output:

  • EditLink: System.String
ExportToExcel

Input:

  • View: Microsoft.Xrm.Sdk.EntityReference
  • FetchXml: System.String
  • LayoutXml: System.String
  • QueryApi: System.String
  • QueryParameters: Microsoft.Crm.Sdk.Messages.InputArgumentCollection

Output:

  • ExcelFile: System.Byte[]
ExportWordDocument

Input:

  • EntityTypeCode: System.Int32
  • SelectedTemplate: Microsoft.Xrm.Sdk.EntityReference
  • SelectedRecords: System.String

Output:

  • WordFile: System.Byte[]
FetchSiteUrl

Input:

  • DocumentId: System.String
  • RegardingObjectId: System.String
  • RegardingObjType: System.Int32

Output:

  • SiteAndLocationUrl: System.String
FireNotificationEvent

Input:

  • EventType: System.Int32
  • EventData: System.String

Output:

FlushMetadataCache

Input:

Output:

FollowEmailAttachment

Input:

  • ActivityMimeAttachmentId: System.Guid

Output:

  • Response: System.String
FollowInYammer

Input:

  • Target: Microsoft.Xrm.Sdk.Entity

Output:

GenerateNumber

Input:

  • PrefixName: System.String
  • CurrentNumberName: System.String
  • AddSuffix: System.Boolean

Output:

  • GeneratedNumber: System.String
GenerateSnapshot

Input:

  • ReportId: System.Guid

Output:

GetActualDate

Input:

  • Date: System.String

Output:

  • Result: System.String
GetAssociatedDocuments

Input:

  • AssociatedDocumentsRequestParams: System.String

Output:

  • BusinessEntityCollection: System.Object
GetAzureServiceConnectionIdByType

Input:

  • ConnectionType: System.Int32

Output:

  • ConnectionId: System.Guid
GetCommitmentSubjects

Input:

  • ActivityIds: System.Guid[]

Output:

  • Subjects: System.String[]
GetComponents

Input:

  • CustomizationFile: System.Byte[]

Output:

  • GetComponents: System.String
GetCurrentServerDateTimeInUtc

Input:

  • EntityName: System.String
  • EntityId: System.String

Output:

  • ServerDateTime: System.String
GetDataForTopicWordCloud

Input:

  • Filter: System.String

Output:

  • Topics: System.String
GetDefaultDocumentLibrary

Input:

  • SiteUrl: System.String

Output:

  • DefaultDocumentLibrary: System.String
GetEmailLinkTrackingUrls

Input:

  • TrackingId: System.Guid
  • ConversationTrackingId: System.Guid
  • ClientType: System.String
  • EmailLinkUrls: System.String[]

Output:

  • EmailLinkTrackingUrls: System.String[]
GetEmailTrackingPixelUrl

Input:

  • TrackingId: System.Guid
  • ConversationTrackingId: System.Guid
  • ClientType: System.String

Output:

  • EmailTrackingPixelUrl: System.String
GetEntitiesForAzureML

Input:

  • Filter: System.String

Output:

  • Result: System.String
GetEntityWiseDuplicatesCount

Input:

  • Entity: Microsoft.Xrm.Sdk.Entity

Output:

  • DuplicatesCount: System.Int32[]
  • EntityLogicalNames: System.String[]
GetFieldListForAzureML

Input:

  • EntityName: System.String
  • Filter: System.String

Output:

  • Result: System.String
GetMailMergeTargetEntityType

Input:

  • CampaignActivityId: System.Guid

Output:

  • EntityType: System.Int32
GetOutlookSyncDataSubscriptionClients

Input:

  • ClientId: System.Guid
  • EntityName: System.String
  • SyncAction: Microsoft.Crm.Sdk.Messages.SyncAction
  • BatchSize: System.Int32
  • ColumnSetXml: System.String

Output:

  • SyncDataXml: System.String
GetRecommendationModelId

Input:

Output:

  • id: System.Guid
GetRecommendationModelVersionOverLimit

Input:

  • RecommendationModelId: System.Guid

Output:

  • BusinessEntity: System.Object
GetRecommendations

Input:

  • VersionId: System.Guid
  • ItemIds: System.Guid[]
  • NumberOfResults: System.Int32

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
GetRelationshipsForAzureML

Input:

  • EntityName: System.String
  • Filter: System.String

Output:

  • Result: System.String
GetReportParameters

Input:

  • ReportId: System.Guid

Output:

  • ParametersXml: System.String
GetRIProvisioningStatus

Input:

Output:

  • ProvisioningStatus: System.String
GetRITenantEndpointUrl

Input:

Output:

  • TenantEndpointUrl: System.String
GetSimilarRecords

Input:

  • Id: Microsoft.Xrm.Sdk.EntityReference

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
GetSyncDataSubscription

Input:

  • SubscriptionId: System.Guid
  • EntityName: System.String
  • SyncAction: Microsoft.Crm.Sdk.Messages.SyncAction
  • BatchSize: System.Int32
  • ColumnSetXml: System.String

Output:

  • SyncDataXml: System.String
GetTrackingServiceBaseUrl

Input:

Output:

  • TrackingServiceBaseUrl: System.String
GetUnprocessedRecords

Input:

  • EntityName: System.String
  • Columns: System.String[]
  • RuleId: System.Guid
  • PageSize: System.Int32

Output:

  • UnprocessedRecords: Microsoft.Xrm.Sdk.EntityCollection
  • VersionNumbers: System.String[]
GetValidStatusTransition

Input:

  • IncidentId: System.String
  • ToStateCode: System.Int32

Output:

  • Result: System.Int32
ImportLinearMappingsImportMap

Input:

  • MappingsXml: System.String
  • ReplaceIds: System.Boolean

Output:

  • ImportMapId: System.Guid
IntersectRecordsWithQueueAndAggregate

Input:

  • QueueId: System.Guid
  • ViewId: System.Guid
  • VisualizationId: System.Guid
  • InteractionCentricFilterXml: System.String

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
InviteUser

Input:

  • UserId: System.Guid

Output:

  • InvitationToken: System.String
IsPartnerSolutionInstalled

Input:

  • SolutionName: System.String

Output:

  • IsPartnerSolutionInstalled: System.Boolean
IsPrimaryClientSubscriptionClients

Input:

  • ClientId: System.Guid

Output:

  • isPrimaryClient: System.Boolean
IsReportingDataConnectorInstalled

Input:

  • DataProviderType: System.Int32

Output:

  • IsReportingDataConnectorInstalled: System.Boolean
IsServerWorkgroup

Input:

Output:

  • IsWorkgroup: System.Boolean
IsSupportUserRole

Input:

  • RoleId: System.Guid

Output:

  • SupportUserRole: System.Boolean
IsSystemAdministratorRole

Input:

  • RoleId: System.Guid

Output:

  • SystemAdministratorRole: System.Boolean
ListSnapshots

Input:

  • ReportId: System.Guid

Output:

  • HistoryIds: System.String[]
  • CreatedDates: System.DateTime[]
LogExternalResultsClicked

Input:

  • Source: System.String

Output:

LogFailureBulkOperation

Input:

  • BulkOperationId: System.Guid
  • RegardingObjectId: System.Guid
  • RegardingObjectTypeCode: System.Int32
  • ErrorCode: System.Int32
  • Message: System.String
  • AdditionalInfo: System.String

Output:

LogSuccessBulkOperation

Input:

  • BulkOperationId: System.Guid
  • RegardingObjectId: System.Guid
  • RegardingObjectTypeCode: System.Int32
  • CreatedObjectId: System.Guid
  • CreatedObjectTypeCode: System.Int32
  • AdditionalInfo: System.String

Output:

MigrateToS2S

Input:

  • SiteUrl: System.String
  • EnableOneDrive: System.Boolean

Output:

MobileOfflineDeprovision

Input:

Output:

MobileOfflineProvision

Input:

Output:

MyExecuteCampaignActivity

Input:

  • CampaignActivityId: System.Guid
  • Propagate: System.Boolean
  • ActivityXml: System.String
  • TemplateId: System.Guid
  • OwnershipOptions: Microsoft.Crm.Sdk.Messages.PropagationOwnershipOptions
  • PostWorkflowEvent: System.Boolean
  • Owner: Microsoft.Xrm.Sdk.EntityReference
  • SendEmail: System.Boolean
  • QueueId: System.Guid

Output:

  • BulkOperationId: System.Guid
NavigateToNextEntity

Input:

  • CurrentEntityId: System.Guid
  • CurrentEntityLogicalName: System.String
  • NextEntityId: System.Guid
  • NextEntityLogicalName: System.String
  • NewActiveStageId: System.Guid
  • NewTraversedPath: System.String
  • ProcessId: System.Guid
  • ProcessInstanceId: System.Guid

Output:

NewDocument

Input:

  • FileName: System.String
  • RegardingObjectId: System.String
  • RegardingObjectTypeCode: System.String
  • LocationId: System.String

Output:

  • EditLink: System.String
OverrideDynamicProperties

Input:

  • RegardingObject: Microsoft.Xrm.Sdk.EntityReference
  • DynamicPropertyCollection: Microsoft.Xrm.Sdk.EntityCollection

Output:

OverrideDynamicProperty

Input:

  • RegardingObject: Microsoft.Xrm.Sdk.EntityReference
  • DynamicProperty: Microsoft.Xrm.Sdk.EntityReference

Output:

  • Id: System.Guid
  • DynamicPropertyId: System.Guid
OverwriteDynamicProperty

Input:

  • RegardingObject: Microsoft.Xrm.Sdk.EntityReference
  • DynamicProperty: Microsoft.Xrm.Sdk.EntityReference

Output:

  • Id: System.Guid
  • DynamicPropertyId: System.Guid
PopulateCard

Input:

  • UserId: System.Guid

Output:

  • Data: System.String
PopulateRecommendationCacheForRecord

Input:

  • ParentRecord: Microsoft.Xrm.Sdk.EntityReference

Output:

  • ShowAzureRecommendations: System.Boolean
PopulateRecommendationCache

Input:

  • EntityName: System.String
  • ItemId: System.Guid

Output:

  • ShowAzureRecommendations: System.Boolean
PostOutlookSyncSubscriptionClients

Input:

  • ClientId: System.Guid
  • EntityName: System.String
  • SyncAction: Microsoft.Crm.Sdk.Messages.SyncAction
  • BatchSize: System.Int32

Output:

PostSyncSubscription

Input:

  • SubscriptionId: System.Guid
  • EntityName: System.String
  • SyncAction: Microsoft.Crm.Sdk.Messages.SyncAction
  • BatchSize: System.Int32

Output:

PrepareOutlookSyncSubscriptionClients

Input:

  • ClientId: System.Guid
  • Type: System.Int32

Output:

  • SyncInfoXml: System.String
PrepareSyncSubscription

Input:

  • SubscriptionId: System.Guid
  • Type: System.Int32

Output:

  • SyncInfoXml: System.String
ProcessOneMemberBulkOperation

Input:

  • BulkOperationId: System.Guid
  • Entity: Microsoft.Xrm.Sdk.Entity
  • BulkOperationSource: System.Int32

Output:

  • ProcessResult: System.Int32
ProcessReplicationBacklog

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference

Output:

PromoteToAdmin

Input:

  • UserId: System.Guid

Output:

PublishAppModule

Input:

  • AppModuleId: System.Guid

Output:

PublishExternal

Input:

  • ReportId: System.Guid

Output:

PublishKnowledgeArticle

Input:

  • Entity: Microsoft.Xrm.Sdk.Entity
  • CopyRelatedProductFromAssociatedPrimary: System.Boolean
  • CopyRelatedCategoryFromAssociatedPrimary: System.Boolean
  • PublishApprovedRelatedTranslations: System.Boolean

Output:

  • IsPublish: System.Boolean
PublishResourceGroups

Input:

Output:

RegisterSolution

Input:

  • PluginAssembly: Microsoft.Xrm.Sdk.Entity
  • Steps: Microsoft.Crm.Sdk.Messages.SdkMessageProcessingStepRegistration[]

Output:

  • PluginAssemblyId: System.Guid
RemoveClientFromSubscriptionSubscriptionClients

Input:

  • ClientId: System.Guid

Output:

RemoveDynamicProperty

Input:

  • RegardingObject: Microsoft.Xrm.Sdk.EntityReference
  • DynamicProperty: Microsoft.Xrm.Sdk.EntityReference

Output:

RemoveMembersByFetchXmlList

Input:

  • ListId: System.Guid
  • FetchXml: System.String
  • KeepReturned: System.Boolean

Output:

RenameFolderNameForOneDrive

Input:

  • FolderName: System.String

Output:

RenameFolderName

Input:

  • SiteUrl: System.String
  • FolderPath: System.String
  • NewFolderName: System.String

Output:

RenderTemplateFromView

Input:

  • Template: Microsoft.Xrm.Sdk.EntityReference
  • View: Microsoft.Xrm.Sdk.EntityReference

Output:

  • ExcelFile: System.Byte[]
RenderTemplate

Input:

  • Template: Microsoft.Xrm.Sdk.EntityReference
  • FetchXml: System.String
  • QueryApi: System.String
  • QueryParameters: Microsoft.Crm.Sdk.Messages.InputArgumentCollection

Output:

  • ExcelFile: System.Byte[]
ResetPerformanceReport

Input:

Output:

ResetSyncStateSubscription

Input:

  • SubscriptionId: System.Guid
  • ResetSyncInfo: Microsoft.Crm.Sdk.Messages.ResetSyncStateInfo[]

Output:

ResolveEmailAddress

Input:

  • EmailAddresses: System.String
  • ObjectTypeCodes: System.Int32[]

Output:

  • Entities: Microsoft.Xrm.Sdk.EntityCollection
ResolveIncident

Input:

  • IncidentId: Microsoft.Xrm.Sdk.EntityReference
  • Status: Microsoft.Xrm.Sdk.OptionSetValue
  • BillableTime: System.Int32
  • Resolution: System.String
  • Remarks: System.String

Output:

ResolveQuote

Input:

  • Subject: System.String
  • Status: Microsoft.Xrm.Sdk.OptionSetValue
  • QuoteId: Microsoft.Xrm.Sdk.EntityReference
  • Description: System.String
  • CloseDate: System.DateTime

Output:

RetrieveAADAccessToken

Input:

  • ClientId: System.String
  • ServerId: System.String
  • Tenant: System.String

Output:

  • AccessToken: System.String
RetrieveAttributeList

Input:

  • RegardingObjectTypeCode: System.Int32

Output:

  • Result: System.String
RetrieveBusinessRulesForForm

Input:

  • EntityTypeCode: System.Int32
  • FormId: System.Guid
  • BusinessProcessId: System.Guid
  • IncludeDraftRules: System.Boolean
  • DetailGridTypeCollectionString: System.String
  • IsHomePage: System.Boolean
  • IsAssociatedGrid: System.Boolean

Output:

  • BusinessRulesScript: System.String
RetrieveCardData

Input:

  • CardTypeId: System.Guid
  • AdditionalParameter: System.String

Output:

  • Data: System.String
RetrieveClientSubscriptionSubscriptionClients

Input:

  • ClientId: System.Guid
  • ColumnSet: Microsoft.Xrm.Sdk.Query.ColumnSet

Output:

  • Entity: Microsoft.Xrm.Sdk.Entity
RetrieveCollation

Input:

Output:

  • Result: System.String
RetrieveCustomersNotPlacedOrders

Input:

  • Query: Microsoft.Xrm.Sdk.Query.QueryBase

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveDashboardForms

Input:

  • SystemUserId: System.Guid
  • AppModuleId: System.Guid

Output:

  • SystemForms: Microsoft.Xrm.Sdk.EntityReferenceCollection
RetrieveDefaultStatusForState

Input:

  • EntityName: System.String
  • State: Microsoft.Xrm.Sdk.OptionSetValue

Output:

  • Status: Microsoft.Xrm.Sdk.OptionSetValue
RetrieveDocumentsFromAllLocationsAndShared

Input:

  • Query: Microsoft.Xrm.Sdk.Query.QueryBase
  • AllSitesAndLocations: System.String
  • ReferencedEntity: System.String
  • PageToken: System.String
  • SharedDocuments: System.Boolean

Output:

  • BusinessEntityCollection: System.Object
RetrieveDocumentTemplates

Input:

  • EntityTypeCode: System.Int32
  • DocumentType: System.Int32

Output:

  • DocumentTemplateEntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveEntitiesForAggregateQuery

Input:

  • OuterQuery: Microsoft.Xrm.Sdk.Query.QueryBase
  • SubQueries: Microsoft.Crm.Sdk.Messages.QueryByEntityNameDictionary

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveEntitiesToFilter

Input:

  • CampaignActivityId: System.Guid
  • EntityName: System.String

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveEntitiesToMailMerge

Input:

  • CampaignActivityId: System.Guid
  • Query: Microsoft.Xrm.Sdk.Query.QueryBase
  • EntityName: System.String

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveEntitiesToSync

Input:

  • ClientId: System.Guid
  • EntityName: System.String
  • SyncAction: Microsoft.Crm.Sdk.Messages.SyncAction
  • BatchSize: System.Int32
  • ColumnSetXml: System.String

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveEntitlementsForCase

Input:

  • CustomerId: System.String
  • PrimaryContactId: System.String
  • ProductId: System.String
  • Query: Microsoft.Xrm.Sdk.Query.QueryBase

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveEntityDynamicPropertyDefinitions

Input:

  • RegardingObject: Microsoft.Xrm.Sdk.EntityReference
  • ForDraftRegarding: System.Boolean

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveEntityGroupConfiguration

Input:

  • EntityGroupName: System.String

Output:

  • EntityGroupConfiguration: Microsoft.Xrm.Sdk.QuickFindConfigurationCollection
RetrieveEntityXml

Input:

  • EntityName: System.String

Output:

  • EntityXml: System.String
RetrieveExternalRequiredComponents

Input:

  • ComponentId: System.Guid
  • ComponentType: System.Int32
  • IncludeSubcomponents: System.Boolean
  • SolutionUniqueName: System.String

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveExternalRoots

Input:

  • ComponentId: System.Guid
  • ComponentType: System.Int32

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveFilteredProcesses

Input:

  • EntityLogicalName: System.String

Output:

  • Processes: Microsoft.Xrm.Sdk.EntityCollection
RetrieveImportJobProgress

Input:

  • ImportJobId: System.Guid

Output:

  • Progress: System.String
RetrieveItemIdsForRecord

Input:

  • ParentRecord: Microsoft.Xrm.Sdk.EntityReference

Output:

  • ItemIds: System.Guid[]
RetrieveKeyPhrasesForKnowledgeSearch

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference

Output:

  • KeyPhrases: System.String[]
RetrieveKeyPhrasesForSimilaritySearch

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference

Output:

  • KeyPhrases: System.String[]
RetrieveLanguagesAvailableForProvisioning

Input:

Output:

  • LanguagesAvailableForProvisioning: System.Int32[]
RetrieveMetadataChangesForRichClient

Input:

  • LastTimestamp: System.Int64
  • LastSyncTime: System.DateTime
  • LastUserLanguage: System.Int32
  • MetadataVersion: System.String

Output:

  • SyncType: System.Byte
  • NewTimestamp: System.Int64
  • NewSyncTime: System.DateTime
  • NewUserLanguage: System.Int32
  • NewCalculatedTimestamp: System.String
  • AddedOrUpdatedLocalizedLabelMetadata: System.Byte[]
  • AddedOrUpdatedOtherMetadata: System.Byte[]
  • DeletedMetadata: System.Byte[]
RetrieveMetadataForRichClient

Input:

Output:

  • Metadata: System.Byte[]
RetrieveMultipleSystemFormsWithAllLabels

Input:

  • Query: Microsoft.Xrm.Sdk.Query.QueryBase

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RetrieveOfficeGroupsSetting

Input:

  • SettingType: System.Int32

Output:

  • Setting: System.String
RetrieveOptionalFeatureStatus

Input:

  • Name: System.String

Output:

  • IsEnabled: System.Boolean
  • InstallationStatus: System.Int32
RetrievePerformanceReport

Input:

  • DumpStrategy: System.Int32
  • FilterByOrganization: System.Boolean
  • FilterByTestName: System.String

Output:

  • ReportXml: System.String
RetrievePersonalSite

Input:

  • SiteUrl: System.String

Output:

  • PersonalSiteUrl: System.String
  • MainSiteUrl: System.String
RetrievePersonalSiteUrl

Input:

Output:

  • PersonalSiteUrl: System.String
RetrievePrivilegeMaxDepthFromTeamRoles

Input:

  • UserId: System.Guid

Output:

  • RolePrivileges: Microsoft.Crm.Sdk.Messages.RolePrivilege[]
RetrieveProcessActiveStage

Input:

  • EntityId: System.Guid
  • EntityLogicalName: System.String
  • ProcessId: System.Guid
  • ProcessInstanceId: Microsoft.Xrm.Sdk.EntityReference

Output:

  • Entity: Microsoft.Xrm.Sdk.Entity
RetrieveProcessControlData

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference
  • ProcessId: Microsoft.Xrm.Sdk.EntityReference
  • ProcessInstanceId: Microsoft.Xrm.Sdk.EntityReference

Output:

  • Entity: Microsoft.Xrm.Sdk.Entity
  • GlobalNavigationData: Microsoft.Xrm.Sdk.Entity
RetrieveProcessWithFallback

Input:

  • EntityLogicalName: System.String
  • Process: Microsoft.Xrm.Sdk.EntityReference
  • AppModuleId: System.Guid

Output:

  • Entity: Microsoft.Xrm.Sdk.Entity
RetrievePublishedAppModuleWithLocale

Input:

  • LcId: System.Int32

Output:

  • AppModuleCollection: Microsoft.Crm.Sdk.Messages.AppModuleCollection
RetrieveRecommendationLineItemMetadata

Input:

  • ParentEntityName: System.String

Output:

  • RecommendationLineItemMetadata: System.String
RetrieveRecommendationLineItemProducts

Input:

  • ParentEntityName: System.String
  • ParentEntityId: System.Guid

Output:

  • RecommendationLineItemProducts: System.String
RetrieveRecommendationsCount

Input:

  • ParentRecord: Microsoft.Xrm.Sdk.EntityReference
  • PriceLevelId: System.Guid

Output:

  • RecommendationsCount: System.Int32
RetrieveReferenceSiteMap

Input:

Output:

  • Entity: Microsoft.Xrm.Sdk.Entity
RetrieveReportSqlFromQuery

Input:

  • FetchXml: System.String
  • UseStoredProcedures: System.Boolean
  • RetrieveAllColumns: System.Boolean

Output:

  • ReportSql: System.String
RetrieveSharePointData

Input:

  • Query: Microsoft.Xrm.Sdk.Query.QueryBase
  • SiteUrl: System.String
  • DocumentLocation: System.String
  • ReferencedEntity: System.String
  • PageToken: System.String

Output:

  • BusinessEntityCollection: System.Object
RetrieveSharePointGlobalSettings

Input:

Output:

  • SharePointGlobalSetting: System.String
RetrieveTenantInfo

Input:

Output:

  • TenantInfo: System.String
RetrieveTrendingDocuments

Input:

  • Query: Microsoft.Xrm.Sdk.Query.QueryBase
  • SharePointSiteUrl: System.String

Output:

  • BusinessEntityCollection: System.Object
RetrieveUserDefaultCurrency

Input:

Output:

  • Currency: Microsoft.Xrm.Sdk.EntityReference
RetrieveValidSLA

Input:

  • RecordTypeName: System.String
  • RecordId: System.Guid

Output:

  • BusinessEntity: System.Object
RetrieveWallByView

Input:

  • View: Microsoft.Xrm.Sdk.EntityReference
  • PagingCookie: System.String
  • PageNumber: System.Int32
  • PageSize: System.Int32
  • CommentsPerPost: System.Int32
  • Source: Microsoft.Xrm.Sdk.OptionSetValue

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
RollupForActivityWall

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference
  • Query: Microsoft.Xrm.Sdk.Query.QueryBase
  • RollupType: Microsoft.Crm.Sdk.Messages.RollupType

Output:

  • EntityCollection: Microsoft.Xrm.Sdk.EntityCollection
SaveEntityGroupConfiguration

Input:

  • EntityGroupName: System.String
  • EntityGroupConfiguration: Microsoft.Xrm.Sdk.QuickFindConfigurationCollection

Output:

ScheduleBuildTopicModel

Input:

  • TopicModelId: System.Guid
  • ScheduleBuildStartTime: System.DateTime
  • ScheduleBuildRecurrence: System.String

Output:

SearchDocument

Input:

  • RegardingObjectType: System.Int32
  • RegardingObjectId: System.String
  • DocumentId: System.String

Output:

  • SearchLocation: System.String
  • DocumentLocation: System.String
SearchSharePointDocument

Input:

  • SiteUrl: System.String
  • DocumentLocation: System.String
  • ReferencedEntity: System.String
  • GetSharedDocumentsKeyword: System.Boolean

Output:

  • SearchLocation: System.String
SetActionCardState

Input:

  • ActionCardId: System.Guid
  • ActionState: Microsoft.Xrm.Sdk.OptionSetValue
  • MessageId: System.String

Output:

SetDevErrors

Input:

  • UserId: System.Guid
  • OrganizationId: System.Guid
  • Value: System.Boolean

Output:

SetOptionalFeatureStatus

Input:

  • Name: System.String
  • NewStatus: System.Boolean

Output:

SetPrimaryClientSubscriptionClients

Input:

  • ClientId: System.Guid

Output:

SetStateAzureServiceConnection

Input:

  • EntityMoniker: Microsoft.Xrm.Sdk.EntityReference
  • State: System.Int32
  • Status: System.Int32
  • CascadeModelState: System.Boolean

Output:

SetWordTemplate

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference
  • SelectedTemplate: Microsoft.Xrm.Sdk.EntityReference

Output:

ShouldDisplaySLALimitNotification

Input:

  • RegardingObjectTypeCode: System.Int32

Output:

  • Result: System.Boolean
SnoozeActionCard

Input:

  • ActionCardId: System.Guid

Output:

StartRIProvisioning

Input:

  • HubName: System.String
  • PrimaryKey: System.String

Output:

StatusUpdateBulkOperation

Input:

  • BulkOperationId: System.Guid
  • FailureCount: System.Int32
  • SuccessCount: System.Int32

Output:

TestAzureServiceConnection

Input:

  • AzureServiceConnectionId: System.Guid

Output:

TestTopicModel

Input:

  • TopicModelId: System.Guid
  • ConfigurationUsedId: System.Guid
  • MaxTopics: System.Int32

Output:

ToggleGuidedHelp

Input:

  • Value: System.Boolean

Output:

ToggleGuidedHelp

Input:

  • Value: System.Boolean

Output:

TrackEmail

Input:

  • ExchangeItemId: System.String
  • Regarding: Microsoft.Xrm.Sdk.EntityReference

Output:

UnfollowEmailAttachment

Input:

  • ActivityMimeAttachmentId: System.Guid

Output:

UnregisterSolution

Input:

  • PluginAssemblyId: System.Guid

Output:

UpdateDelveActionStatus

Input:

  • MessageId: System.String
  • ActionState: System.Int32
  • RecordId: System.String

Output:

UpdateDocumentManagementSettings

Input:

  • SiteCollection: System.String
  • FolderStructureEntity: System.Int32
  • EntityDocMgmtXml: System.String
  • ValidateStatus: System.Int32
  • ValidateStatusReason: System.Int32

Output:

UpdateFromTemplate

Input:

  • ReportId: System.Guid
  • WizardXml: System.String
  • IsOrgReport: System.Boolean

Output:

UpdateGlobalSharePointSettings

Input:

  • SharePointRealm: System.String
  • IsSharePointOnline: System.Boolean
  • UseAuthorizationServer: System.Boolean

Output:

UpdateRITenantInfo

Input:

  • HubName: System.String
  • PrimaryKey: System.String

Output:

UpdateSchedule

Input:

  • ReportId: System.Guid
  • ScheduleXml: System.String
  • ParameterXml: System.String
  • ScheduledReportName: System.String

Output:

UpdateYammerProperties

Input:

  • BusinessEntity: Microsoft.Xrm.Sdk.Entity

Output:

UpgradeToS2S

Input:

Output:

UploadDocument

Input:

  • Content: System.Byte[]
  • Entity: Microsoft.Xrm.Sdk.Entity
  • OverwriteExisting: System.Boolean

Output:

UploadSharePointDocument

Input:

  • DocumentContent: System.String
  • FileName: System.String
  • OverwriteExisting: System.Boolean
  • SiteUrl: System.String
  • DocumentLocation: System.String
  • ReferencedEntity: System.String

Output:

  • EditLink: System.String
ValidateAppModule

Input:

  • AppModuleId: System.Guid

Output:

  • AppModuleValidation: Microsoft.Crm.Sdk.Messages.AppModuleValidationResponse
ValidateOfficeGraphAuthentication

Input:

Output:

  • ValidationResult: System.String
ValidateSharePointFolder

Input:

  • FolderUrls: System.String[]
  • SiteUrls: System.String[]

Output:

  • ValidationStatus: System.Boolean[]
ValidateSharePointSite

Input:

  • SiteUrls: System.String[]

Output:

  • ValidationStatus: System.Boolean[]
  • ValidationLog: System.String
ValidateUrl

Input:

  • SharePointUrls: System.String

Output:

  • UrlValidationResult: System.String
VerifyProcessStateData

Input:

  • Target: Microsoft.Xrm.Sdk.EntityReference
  • ProcessState: System.String

Output:

  • IsValid: System.Boolean

Making use of transactions in Dynamics 365 Customer Engagement

I believe that if you have ever written a CRM plugin, you are aware of the event execution pipeline (https://msdn.microsoft.com/en-us/library/gg327941.aspx) and that the operation is run in transaction which means that if there will be any exception, the whole transaction will be rolled back. Pre-operation plugins and Post-Operation plugins are also run in transaction, so if there will be an exception raised by the plugin, changes will not be reflected in CRM. This includes not only chaning attributes for the entity that the plugin is running on – create/update/delete changes on any other entities are also rolled back. This is really convenient – we can perform a bunch of operations and we are sure that even if anything goes wrong during Post-Operation, everything will be changed back to the original state.

The usage of transactions can be embraced even more by using custom Actions and ExecuteTransactionRequest. Custom  Actions can also be run with a rollback option (which can be enabled or disabled on Action configuration page). ExecuteTransactionRequest allows to group different OrganizationRequests and send them in bulk to the server – they will be run in single transaction.

Ok, but how this transactional behaviour works in real world? To make it easier to understand for you I created a custom entity “new_lock” and prepared a very simple CRM plugin:

public void Execute(IServiceProvider serviceProvider)
{
    var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    var service = factory.CreateOrganizationService(null);
    var @lock = new Entity("new_lock");
    @lock.Id = new Guid("590424F1-611F-E711-80D2-00155D010402");
    @lock["new_name"] = "lockme";
    service.Update(@lock);
    Thread.Sleep(30 * 1000);
}

I registered this plugin on Create message of the Contact entity. As you can see it simply retrieves the “new_lock” record and updates its name to some different value and then waits 30 seconds. Let’s open this record in a browser:

Now, let’s create a Contact (using a different browser window). After clicking “Save” you will notice that the loader starts but the data is not getting saved. That’s ok because our plugin is running in the background and it takes about 30 seconds to finish. If you try to save the opened “new_lock” record with some different name, you will notice that… you can’t do this:

This is the principal idea of transactions – by creating a Contact we opened a transaction and “locked” the “new_lock” record by updating it. Before the transaction is finished nothing can write to this record. After 30 seconds, our contact is created and the “new_lock” is updated with the new value. Is this a value from the plugin or from what we have entered on the form? Well it’s the second option, because we updated the value after the transaction has done it (it does not matter that it waited 30 seconds after that).

How can we make use of that transactional behaviour of CRM? I believe that a very cool feature that we can develop is proper auto-numbering. It’s pretty well known problem which is usually handled by some custom counter entity. The algorithm usually works like that:

  1. Run plugin on create of numbered entity
  2. Retrieve Counter
  3. Assign current value of counter to entity record
  4. Update counter with the next value
  5. Plugin ends

If you don’t have many users or some custom apps creating entities, you will probably never run into any concurrency problems. But imagine what will happen if you have two concurrent calls and both retrieved value of the counter before point 3 was fulfiled. This means that both records will have the same number and counter will be increased only by 1. Some may say – ok let’s use standard locking, we can have a static object to lock on and before plugin starts it’s core logic, the running thread must obtain the lock. That would work flawlessly on single-frontend-server environments. But what happens if there are more application servers for CRM? You cannot synchronize threads between different servers…

Knowing what we already know about transactions, we can easily modify this scenario to:

  1. Run plugin on create of numbered entity
  2. Retrieve Counter
  3. Update some dummy counter property to some dummy value (here we will lock the counter for usage only for this transaction)
  4. Retrieve Counter (now we are sure nobody will modify its value)
  5. Assign current value of counter to entity record
  6. Update counter with the next value
  7. Plugin ends (and any concurrent operations start from point 3)

This should work even on multi-server environments, because the transactions are locked on database level. Very simplified code (so pardon the style) can look like this:

public class TransactionPlugin : IPlugin
{
    public void Execute(IServiceProvider serviceProvider)
    {
        var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
        var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
        var service = factory.CreateOrganizationService(null);

        var counter = GetCounter(service);
        counter["new_name"] = "lockme";
        service.Update(counter);

        //now counter is locked

        //we have to get it again to prevent race conditions
        counter = GetCounter(service);

        var target = (Entity)context.InputParameters["Target"];
        target["new_number"] = counter["new_nextnumber"];

        counter["new_nextnumber"] = (int)counter["new_nextnumber"] + 1;
        service.Update(counter);
        //after finishing the transaction, concurrent executes can go on from the lock
    }

    private Entity GetCounter(IOrganizationService service)
    {
        //return some counter
    }
}

 

UPDATE 21.07.2017

I prepared more detailed code for autonumbering plugin, which I have successfully used in my projects. The plugin works on Pre-Create of Account.


namespace TransactionPlugin
{
public class Autonumber : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var pluginContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = factory.CreateOrganizationService(null);
var target = pluginContext.InputParameters["Target"] as Entity;
var qExpression = new QueryExpression("new_lock");
qExpression.ColumnSet = new ColumnSet("new_name");
qExpression.Criteria.AddCondition("new_name", ConditionOperator.Equal, "Counter");
var results = service.RetrieveMultiple(qExpression);
var counter = results.Entities.First();
var blocker = new Entity("new_lock");
blocker.Id = counter.Id;
blocker["new_name"] = "Counter";
service.Update(blocker); //NOW WE LOCK ALL TRANSACTIONS
var lockedCounter = service.Retrieve("new_lock", blocker.Id, new ColumnSet("new_lastnumber"));
var currentNumber = lockedCounter.GetAttributeValue<int>("new_lastnumber");
target["accountnumber"] = $"{++currentNumber}";
var counterUpdater = new Entity("new_lock");
counterUpdater.Id = counter.Id;
counterUpdater["new_lastnumber"] = currentNumber;
service.Update(counterUpdater);
}
}
}

view raw

Autonumber.cs

hosted with ❤ by GitHub

As you can see I’m retrieving my “new_lock” entity by its name using QueryExpression and after locking it with Update, I’m retrieving it again using Retrieve message. It is very important, because the following code will not work correctly:


namespace TransactionPlugin
{
public class Autonumber : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var pluginContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = factory.CreateOrganizationService(null);
var target = pluginContext.InputParameters["Target"] as Entity;
var qExpression = new QueryExpression("new_lock");
qExpression.ColumnSet = new ColumnSet("new_name", "new_lastnumber");
qExpression.Criteria.AddCondition("new_name", ConditionOperator.Equal, "Counter");
var results = service.RetrieveMultiple(qExpression);
var counter = results.Entities.First();
var blocker = new Entity("new_lock");
blocker.Id = counter.Id;
blocker["new_name"] = "Counter";
service.Update(blocker);
var qExpression2 = new QueryExpression("new_lock");
qExpression2.ColumnSet = new ColumnSet("new_lastnumber");
qExpression2.Criteria.AddCondition("new_name", ConditionOperator.Equal, "Counter");
var lockedResults = service.RetrieveMultiple(qExpression2);
var lockedCounter = results.Entities.First();
var currentNumber = lockedCounter.GetAttributeValue<int>("new_lastnumber");
target["accountnumber"] = $"{++currentNumber}";
var counterUpdater = new Entity("new_lock");
counterUpdater.Id = counter.Id;
counterUpdater["new_lastnumber"] = currentNumber;
service.Update(counterUpdater);
}
}
}

If you try to run that, you will discover that you have a lot of duplicates. The reason is that the second RetrieveMultiple gets exactly the same value as the first one, so if we already obtained counter and some other thread will modify its value – this change will be not reflected. This is very important, so make sure you are not falling for this trap – for me this feature is quite surprising, QueryExpression results seem to be cached somehow in plugin context. What is even more interesting the following version will work flawlessly:


namespace TransactionPlugin
{
public class Autonumber : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var pluginContext = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = factory.CreateOrganizationService(null);
var target = pluginContext.InputParameters["Target"] as Entity;
var qExpression = new QueryExpression("new_lock");
qExpression.ColumnSet = new ColumnSet("new_name", "new_lastnumber");
qExpression.Criteria.AddCondition("new_name", ConditionOperator.Equal, "Counter");
var results = service.RetrieveMultiple(qExpression);
var counter = results.Entities.First();
var blocker = new Entity("new_lock");
blocker.Id = counter.Id;
blocker["new_name"] = "Counter";
service.Update(blocker);
var qExpression2 = new QueryExpression("new_lock");
qExpression2.ColumnSet = new ColumnSet("new_lastnumber");
qExpression2.Criteria.AddCondition("new_name", ConditionOperator.Equal, "Counter");
var rmRequest = new RetrieveMultipleRequest();
rmRequest.Query = qExpression2;
var lockedResults = (RetrieveMultipleResponse)service.Execute(rmRequest);
var lockedCounter = lockedResults.EntityCollection.Entities.First();
var currentNumber = lockedCounter.GetAttributeValue<int>("new_lastnumber");
target["accountnumber"] = $"{++currentNumber}";
var counterUpdater = new Entity("new_lock");
counterUpdater.Id = counter.Id;
counterUpdater["new_lastnumber"] = currentNumber;
service.Update(counterUpdater);
}
}
}

So simply replacing service.RetrieveMultiple with RetrieveMultipleRequest causes the results to be retrieved from the database, not from some kind of cache. That is something you should probably be aware of when implementing your own solution.

Using early bounds in plugin for multiple entities

Recently I was caught up in the discussion about early bound entities and late bound entities. One of the argument of the “late bound supporters” was the fact, that using late-bound allows reusing the same plugin for multiple entities (so for example create a plugin that runs for all activities or a common plugin for leads/opportunities/accounts). This argument is not valid, I will show you how to write a clean code, that uses modern approach for coding and is perfectly testable.

Let’s start with the late bound version of the plugin to give you an idea what are we trying to achieve here:


public class CalculateTotalAmountLateBoundPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService(typeof(IPluginExecutionContext)) as IPluginExecutionContext;
var serviceFactory = serviceProvider.GetService(typeof(IOrganizationServiceFactory)) as IOrganizationServiceFactory;
var service = serviceFactory.CreateOrganizationService(context.UserId);
var lateBoundEntity = context.InputParameters["Target"] as Entity;
lateBoundEntity["new_totalsum"] = lateBoundEntity.GetAttributeValue<decimal>("new_netamount") + lateBoundEntity.GetAttributeValue<decimal>("new_margin");
}
}

The plugin simply adds values of “new_netamount” and “new_margin” fields on the entity and sets field “new_totalsum” to a resulting value. Of course this can be simply achieved by using calculated field, but I just want to show the basic idea here, real world scenario is usually much more complicated.

If we register this field on pre-update of any entity containing field “new_totalsum”, “new_netamount” and “new_margin” it would basically do its job. If you are not an experienced CRM Developer, who remembers CRM 3.0 or 4.0, just an ordinary .NET Developer, you would probably say, that this code looks ugly. It’s almost like it was taken from some JavaScript library – where are all the types, what is the type of “new_totalsum”, which year is that, are we still in 2017? And yes, you’ll be right – this code simply looks awful, does not allow to use any of great refactoring tools, because it uses some “magic” dictionaries and keys and without access to CRM you would not even have a clue, what are the proper types of the properties. It’s exactly the same story as with ORM – who of us still uses plain ADO .NET and not, for example, Entity Framework? Such approach is much less error prone, testable and also much more friendly for CRM newbies. Code using early bounds may look like that:


public class CalculateTotalAmountAccountPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService(typeof(IPluginExecutionContext)) as IPluginExecutionContext;
var serviceFactory = serviceProvider.GetService(typeof(IOrganizationServiceFactory)) as IOrganizationServiceFactory;
var service = serviceFactory.CreateOrganizationService(context.UserId);
var account = (context.InputParameters["Target"] as Entity).ToEntity<Account>();
account.new_totalsum = account.new_netamount + account.new_margin;
}
}

Now it’s much better, but there is a significant problem with this code. We assumed that our plugin runs on Account entity. What if we will register it on Contact entity? Well, it will simply crash when we will try to convert our target!

So the basic approach would be to create a switch statement and simply check the entity type:


public class CalculateTotalAmountEarlyBoundBADPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService(typeof(IPluginExecutionContext)) as IPluginExecutionContext;
var serviceFactory = serviceProvider.GetService(typeof(IOrganizationServiceFactory)) as IOrganizationServiceFactory;
var service = serviceFactory.CreateOrganizationService(context.UserId);
var lateBoundEntity = context.InputParameters["Target"] as Entity;
switch (lateBoundEntity.LogicalName)
{
case Account.EntityLogicalName:
var account = lateBoundEntity.ToEntity<Account>();
account.new_totalsum = account.new_netamount + account.new_margin;
break;
case Opportunity.EntityLogicalName:
var opportunity = lateBoundEntity.ToEntity<Opportunity>();
opportunity.new_totalsum = opportunity.new_netamount + opportunity.new_margin;
break;
default:
break;
}
}
}

Ok so now we can register the plugin on multiple entities, but our code looks even worse than the first version, because we have a lot of code that is copy-pasted. Imagine that the operation would be much more complex and would change frequently in time – we would have to change all the switch statements every time.

To overcome this problem we will take advantage of the fact that all early-bound classes are partial, so they can implement any interface we want. Let’s create such interface and some helper partial classes:


interface IEntityWithTotalSum
{
decimal new_totalsum { get; set; }
decimal new_netamount { get; set; }
decimal new_margin { get; set; }
}


public partial class Account : IEntityWithTotalSum {}
public partial class Opportunity : IEntityWithTotalSum {}

It would be great if we could use this like that:


public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService(typeof(IPluginExecutionContext)) as IPluginExecutionContext;
var serviceFactory = serviceProvider.GetService(typeof(IOrganizationServiceFactory)) as IOrganizationServiceFactory;
var service = serviceFactory.CreateOrganizationService(context.UserId);
var entityWithTotalSum = context.InputParameters["Target"] as IEntityWithTotalSum;
entityWithTotalSum.new_totalsum = entityWithTotalSum.new_netamount + entityWithTotalSum.new_margin;
}

But unfortunately, our “Target” is an entity and we have to somehow convert it to an early bound. I developed a simple utility class that is capable of that:


public static class Extensions
{
public static T Mock<T>(this Entity entity)
{
return InterfaceMocker.Mock<T>(entity);
}
}

view raw

Extensions.cs

hosted with ❤ by GitHub


static class InterfaceMocker
{
public static T Mock<T>(Entity entity)
{
var type = ProxyTypesCache.GetProxyType(entity.LogicalName);
var copy = Activator.CreateInstance(type);
(copy as Entity).Attributes = entity.Attributes;
(copy as Entity).Id = entity.Id;
if (typeof(T).IsAssignableFrom(type))
{
return (T)copy;
}
else
{
throw new InvalidPluginExecutionException("Cannot mock interface. Target type does not implement interface " + typeof(T).Name);
}
}
}


static class ProxyTypesCache
{
private static Dictionary<string, Type> cachedTypes = new Dictionary<string, Type>();
static ProxyTypesCache()
{
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var allTypes = assembly.GetExportedTypes();
foreach (var type in allTypes)
{
if (typeof(Entity).IsAssignableFrom(type))
{
cachedTypes.Add(type.GetCustomAttribute<EntityLogicalNameAttribute>().LogicalName, type);
}
}
}
public static Type GetProxyType(string logicalName)
{
return cachedTypes[logicalName];
}
}

Simple explanation of the code – during first execution of the plugin, all the early-bound types are cached in a dictionary, that can be later fast accessed from the code (in this case it’s InterfaceMocker). It simply instantiates the early-bound and copies all attributes inside. There is also an extension method that simplifies the usage for Entity. Now our plugin can simply look like that:


public void Execute(IServiceProvider serviceProvider)
{
var context = serviceProvider.GetService(typeof(IPluginExecutionContext)) as IPluginExecutionContext;
var serviceFactory = serviceProvider.GetService(typeof(IOrganizationServiceFactory)) as IOrganizationServiceFactory;
var service = serviceFactory.CreateOrganizationService(context.UserId);
var earlyBound = (context.InputParameters["Target"] as Entity).Mock<IEntityWithTotalSum>();
earlyBound.new_totalsum = earlyBound.new_netamount + earlyBound.new_margin;
}

And that’s it! This code looks much better then late bound version, provides type-checking for entity attributes and also is very nice when we implement unit tests, as we have an interface to test, not a full early-bound. If our logic is more complex we can create some additional classes that will be dependent upon IEntityWithTotalSum interface, not Account or Opportunity entity.