Category Archives: CRM 2011

Developing applications for multiple versions of Dynamics CRM

As most of you know, I have a several add-on products for Dynamics CRM developers, administrators, and consultants.

They all share common user interface and application frameworks that I developed several years ago.

When I first started writing these tools all I had to worry about was Dynamics CRM 4.0, which was pretty easy. Then Dynamics CRM 2011 shipped, which made things a bit more difficult, but not impossible.

I just had to separate my internal components so that one section worked with CRM 4.0, and the other with CRM 2011.  Once I got the unified connection user interface component working correctly, everything else just fell into place and things were all warm and cozy.

CRM 4.0 and CRM 2011 communicate over very different means so even though you ask for a single set of information from the user, you must appropriately handle the connection specific to the platform on the back-end.  That took a little bit of work to make happen flawlessly every time.

Then Microsoft shipped Dynamics 2013.  Still not a big deal as I could use the CRM 2011 SDK assemblies to communicate without any issue.

Then Microsoft shipped Dynamics 2015 and I started thinking that maybe I need to re-examine my methodology.  And I am quite happy with my solution.

 

Requirements

I need to communicate with Dynamics CRM 2011, 2013, and 2015 while changing the smallest amount of code possible.

I also had to stay with .NET 4.0, since my user interface framework does not support .NET 4.5 and I was not in a position to write the entire UI for a handful of products.

 

Solution

The solution was to upgrade my SDK .NET assemblies to the Dynamics CRM 2013 level. 

That gave me access to all three platforms as long as I kept in mind that I could never ask for something that did not exist on a specific platform. 

Like SLAs or Entitlements from CRM 2011. Since those do not exist until 2013, it would throw an error.

 

Further Thoughts

For some reason, I had never tried connecting to a lower-version environment using the newest SDK assemblies.  Turns out that works just fine. (silly me for never asking the question till a month ago).

If my UI framework was .NET 4.5 compatible, I would have jumped up to the CRM 2015 assemblies because that is what they are written with.

The only side-effect to this is that I am stuck using Visual Studio 2010 because that same UI framework doesn’t work with Visual Studio 2013, my normal environment.

 

Conclusion

This process was quite eye-opening for me and just about as painless as it could be.  The Microsoft SDK team has done a huge amount of really great work that makes our jobs as developers, much less work than it could be, or even once was.

Let me know if you have had similar or even different experiences.

Troubleshooting Dynamics CRM: The Mother of all call stacks

So one of my customers ran into this issue while opening an Opportunity.  As you can see, a custom plugin was throwing an exception because it could not find a Account with a specified ID.

SNAGHTML5c035ae

That part is 100% correct.  The Account with that ID indeed does not exist in the database.

The problem is, this plugin should never have been fired. As I mentioned, this was happening on a Retrieve operation and there are no plugins registered against the Retrieve Message.

So I waited until tonight so I could turn on Tracing (on-premise, of course), and this is what I found:

[2014-10-16 20:12:57.600] Process: w3wp |Organization:d98bbf24-8eed-4cb4-a379-6aa848499cb0 |
    Thread:   25 |Category: Exception |User: fb9a439c-b578-4a4b-83e0-39ea0059ad2a |
    Level: Error |ReqId: eb0bc9e4-e6e6-4552-b322-1c2fa0bdbc10 | 
    CrmException..ctor  ilOffset = 0x7

    at CrmException..ctor(String message, Exception innerException, Int32 errorCode,
       Boolean isFlowControlException)  ilOffset = 0x7

    at CrmException..ctor(String message, Exception innerException, Int32 errorCode)  ilOffset = 0x5
    at RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig,
       Boolean constructor)  ilOffset = 0xFFFFFFFF

    at RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder,
       Object[] parameters, CultureInfo culture)  ilOffset = 0xF7

    at RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args,
       CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)  ilOffset = 0x1E8

    at Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args,
       CultureInfo culture, Object[] activationAttributes)  ilOffset = 0xBB

    at Activator.CreateInstance(Type type, Object[] args)  ilOffset = 0xA
    at VersionedPluginProxyStepBase.WrapExceptionToThrow(CrmException exception)  ilOffset = 0xD3
    at VersionedPluginProxyStepBase.Execute(PipelineExecutionContext context)  ilOffset = 0x65
    at Pipeline.Execute(PipelineExecutionContext context)  ilOffset = 0x65
    at MessageProcessor.Execute(PipelineExecutionContext context)  ilOffset = 0x1C5
    at InternalMessageDispatcher.Execute(PipelineExecutionContext context)  ilOffset = 0xE4
    at ExternalMessageDispatcher.ExecuteInternal(IInProcessOrganizationServiceFactory serviceFactory,
       IPlatformMessageDispatcherFactory dispatcherFactory, String messageName, String requestName, 
       Int32 primaryObjectTypeCode, Int32 secondaryObjectTypeCode, ParameterCollection fields, 
       CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId,
       Guid transactionContextId, Int32 invocationSource, Nullable`1 requestId, Version endpointVersion)  ilOffset = 0x156

    at OrganizationSdkServiceInternal.ExecuteRequest(OrganizationRequest request, CorrelationToken correlationToken,
       CallerOriginToken callerOriginToken, WebServiceType serviceType, UserAuth userAuth, Guid targetUserId, 
       Boolean traceRequest, OrganizationContext context, Boolean returnResponse)  ilOffset = 0x145

    at OrganizationSdkServiceInternal.ExecuteRequest(OrganizationRequest request, CorrelationToken correlationToken,
       CallerOriginToken callerOriginToken, WebServiceType serviceType)  ilOffset = 0x3D

    at OrganizationSdkServiceInternal.Retrieve(String entityName, Guid id, ColumnSet columnSet,
       CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType)  ilOffset = 0x66

    at InprocessServiceProxy.RetrieveCore(String entityName, Guid id, ColumnSet columnSet)  ilOffset = 0x28
    at OrganizationServiceProxy.Retrieve(String entityName, Guid id, ColumnSet columnSet)  ilOffset = 0x4
    at Utility.UpdateAccountKeyword(IOrganizationService service, Guid accountId)  ilOffset = 0xE8 
        C:\Plugins\SupportingClasses\Utility.cs(205)

    at InvoiceCancelOpportunityCloseKeywords.Execute(IServiceProvider serviceProvider)  ilOffset = 0x164 
        C:\Plugins\InvoiceCancelOpportunityCloseKeywords.cs(70)

    at V5PluginProxyStep.ExecuteInternal(PipelineExecutionContext context)  ilOffset = 0xD0
    at VersionedPluginProxyStepBase.Execute(PipelineExecutionContext context)  ilOffset = 0x65
    at Pipeline.Execute(PipelineExecutionContext context)  ilOffset = 0x65
    at MessageProcessor.Execute(PipelineExecutionContext context)  ilOffset = 0x1FB
    at InternalMessageDispatcher.Execute(PipelineExecutionContext context)  ilOffset = 0xE4
    at ExtensiblePlatformMessageDispatcher.Execute(PipelineExecutionContext pluginContext)  ilOffset = 0x0
    at ExtensiblePlatformMessageDispatcher.UpdateWithInvocationSource(BusinessEntity entity, FilterExpression filter,
       Int32 invocationSource, ExecutionContext context)  ilOffset = 0xCE

    at ExtensiblePlatformMessageDispatcher.Update(BusinessEntity entity, FilterExpression filter, ExecutionContext context) 
       ilOffset = 0x5

    at BusinessProcessObject.UpdateWithPipelineAndExtensions(IBusinessEntity entity, ExecutionContext context)  ilOffset = 0x78
    at QOIPriceService.UpdateEntity(BusinessEntity newQoi, BusinessEntity oldQoi, ExecutionContext context)  ilOffset = 0x87
    at PriceService.CalculatePrice(BusinessEntity entity, Guid lineItemId, Boolean skipQOIDetailPricing,
       Boolean overridePricePerUnitLock, Boolean overrideDiscountLock, ExecutionContext context)  ilOffset = 0x6E1

    at OpportunityPriceService.CalculatePrice(BusinessEntity entity, Guid lineItemId, Boolean skipQOIDetailPricing,
       Boolean overridePricePerUnitLock, Boolean overrideDiscountLock, ExecutionContext context)  ilOffset = 0x70

    at QOIPriceService.CalculatePrice(Guid qoiId, Guid lineItemId, Boolean skipLineItemPricing, Boolean overridePricePerUnitLock,
       Boolean overrideDiscountLock, Boolean isModifiedBySystem, ExecutionContext context)  ilOffset = 0x2E

    at OpportunityService.CalculatePrice(Guid opportunityId, Guid opportunityProductId, Boolean skipOpportunityProductPricing,
       Boolean overridePricePerUnitLock, Boolean overrideDiscountLock, Boolean isModifiedBySystem, ExecutionContext context) 
       ilOffset = 0x12

    at QOIService.Retrieve(BusinessEntityMoniker moniker, EntityExpression entityExpression, ExecutionContext context,
       Boolean isModifiedBySystem, Boolean calculatePrice, Int32 state)  ilOffset = 0x2D

    at OpportunityService.Retrieve(BusinessEntityMoniker moniker, EntityExpression entityExpression,
       ExecutionContext context)  ilOffset = 0x14

    at RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig,
       Boolean constructor)  ilOffset = 0xFFFFFFFF

    at RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) 
       ilOffset = 0x25

    at RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder,
       Object[] parameters, CultureInfo culture)  ilOffset = 0x89

    at LogicalMethodInfo.Invoke(Object target, Object[] values)  ilOffset = 0x4F
    at InternalOperationPlugin.Execute(IServiceProvider serviceProvider)  ilOffset = 0x57
    at V5PluginProxyStep.ExecuteInternal(PipelineExecutionContext context)  ilOffset = 0x58
    at VersionedPluginProxyStepBase.Execute(PipelineExecutionContext context)  ilOffset = 0x65
    at Pipeline.Execute(PipelineExecutionContext context)  ilOffset = 0x65
    at MessageProcessor.Execute(PipelineExecutionContext context)  ilOffset = 0x1C5
    at InternalMessageDispatcher.Execute(PipelineExecutionContext context)  ilOffset = 0xE4
    at ExternalMessageDispatcher.ExecuteInternal(IInProcessOrganizationServiceFactory serviceFactory,
        IPlatformMessageDispatcherFactory dispatcherFactory, String messageName, String requestName, 
        Int32 primaryObjectTypeCode, Int32 secondaryObjectTypeCode, ParameterCollection fields, 
        CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId, 
        Guid transactionContextId, Int32 invocationSource, Nullable`1 requestId, Version endpointVersion)  ilOffset = 0x156

    at OrganizationSdkServiceInternal.ExecuteRequest(OrganizationRequest request, CorrelationToken correlationToken,
        CallerOriginToken callerOriginToken, WebServiceType serviceType, UserAuth userAuth, Guid targetUserId, 
        Boolean traceRequest, OrganizationContext context, Boolean returnResponse)  ilOffset = 0x145

    at OrganizationSdkServiceInternal.ExecuteRequest(OrganizationRequest request, CorrelationToken correlationToken,
       CallerOriginToken callerOriginToken, WebServiceType serviceType)  ilOffset = 0x3D

    at OrganizationSdkServiceInternal.Execute(OrganizationRequest request, CorrelationToken correlationToken,
       CallerOriginToken callerOriginToken, WebServiceType serviceType)  ilOffset = 0x24

    at InprocessServiceProxy.ExecuteCore(OrganizationRequest request)  ilOffset = 0x34
    at PlatformCommand.XrmExecuteInternal()  ilOffset = 0xF6
    at RetrieveCommand.Execute()  ilOffset = 0x2
    at EntityProxy.Retrieve(String[] columns, Guid auditingTransactionId, Boolean addRequiredColumns)  ilOffset = 0x69
    at EntityProxy.Retrieve(String[] columns, Guid auditingTransactionId)  ilOffset = 0x4
    at EntityProxy.Retrieve(String columnSet, Guid auditingTransactionId)  ilOffset = 0xB
    at EntityProxy.Retrieve(String columnSet)  ilOffset = 0x7
    at AppForm.FormLoadEvent()  ilOffset = 0x11
    at AppForm.RaiseDataEvent(FormEventId eventId)  ilOffset = 0xC7
    at EndUserForm.Initialize(Entity entity)  ilOffset = 0x1F
    at CustomizableForm.Execute(Entity entity, FormDescriptor fd)  ilOffset = 0x62
    at RecordPageHandler.ConfigureFormWrapper()  ilOffset = 0xC
    at GenericEventProcessor.RaiseEvent(String eventName)  ilOffset = 0x2D
    at PageManager.OnPreRender(EventArgs e)  ilOffset = 0x47
    at Control.PreRenderRecursiveInternal()  ilOffset = 0x54
    at Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)  ilOffset = 0x6D3
    at Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)  ilOffset = 0x3C
    at Page.ProcessRequest()  ilOffset = 0x14
    at Page.ProcessRequest(HttpContext context)  ilOffset = 0x33
    at CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()  ilOffset = 0x18D
    at HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)  ilOffset = 0x15
    at ApplicationStepManager.ResumeSteps(Exception error)  ilOffset = 0x10A
    at HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)  ilOffset = 0x5C
    at HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)  ilOffset = 0x16A
    at ISAPIRuntime.ProcessRequest(IntPtr ecb, Int32 iWRType)  ilOffset = 0x4B

That is 75 calls, if you didn’t feel like counting, and in the middle of all of that was the failing call to my plugin.

The nearest thing I can figure is that this Opportunity is set to be System Calculated and when it was opened, a update to the revenue numbers. This ended up firing my plugin which failed because data that was passed to it turned out to be invalid.

I temporarily disabled the plugin and was able to save the Opportunity and correct the invalid data issue.

I could not have done this troubleshooting without the following tools:

If you do not have these tools in your toolbox, you need to fix that today.

Interesting Dynamics CRM 2011 Bug: Attachments do not get deleted

I verified a strange bug related to attachments and the deleting of the parent record chain.

  • If you delete an Email with an attachment it deletes the attachment.
  • If you delete a Contact with emails that have attachments the email is deleted but not the attachment.

For on-premise CRM administrators, you can use this script to find the orphans:

SELECT t1.*
FROM Attachment t1
LEFT JOIN ActivityMimeAttachment t2 ON t2.AttachmentId = t1.AttachmentId
WHERE t2.AttachmentId IS NULL

My assumption is that you would need to clean these up manually.

If you are using CRM Online, then you will have a much different story and one for which I do not have an answer.

In a nutshell, you need to use the CRM SDK write a small application that will compare the IDs in the Attachment table to those in the ActivityMimeAttachment table and produce a list of Attachments that are not connected to ActivityMimeAttachments, then delete those records.

CRM SDK Nugget: Metadata Browser

Way back when, like in version 3.0 and 4.0, Dynamics CRM had a hidden page that would display a list of entities and attributes in a concise list.

That went away in version 2011 but the good folks on the CRM SDK team (Jim specifically), created a managed solution that was included in the SDK.

The main purpose of the tool is to show you information about CRM entities that does not show up on the normal CRM user interface through the dialogs associated with solutions.  This information is available, but only available via programmatic access.

My utility SnapShot! for Dynamics CRM uses exactly the same techniques to produce reports showing information not normally seen through the CRM UI.

 

Installation

After you have downloaded and installed the SDK, you can find the Metadata Browser here:

sdk\Tools\MetadataBrowser

Install the Metadata Browser as you would any other managed solution. When finished, open the solution and go to the Configuration page, where you can launch the different Metadata Browser components.

 

Metadata Browser

The first tool is the main Metadata Browser itself, which you activate by clicking the Open Metadata Browser button:

image

The interface looks like this:

image

As you click on an entity on the left-hand side, it’s properties will be displayed on the right.

At this point you can either view or edit the entity and its details.

 

Entity Metadata Browser

The second function will display a dialog that will allow you to edit an entity. This is the same dialog that is shown when you click the Edit Entity button on the Metadata Browser.

Here is what that interface looks like:

image

As you can see, you can edit or view any of the properties of the entity.

 

In conclusion, this is a simple yet powerful tool and one that is often overlooked but one which is great to have in your toolbox, should you need it.

New Upgrade Tool Released: Kill Plugin

There are occasions which some of us may experience during or shortly after the upgrade of a Dynamics CRM 4.0 system to CRM 2011 where we discover we have plugins that cannot be removed by the Plugin Registration Tool.

Most of the time this is caused by the plugin being registered as "on disk" and it not being available to the CRM 2011 server, in some way.

Regardless, there are cases where the Plugin Registration Tool simply will not unregister the assembly because it "can't find it."

Kill Plugin uses the SDK to delete the plugin and its associated steps.

Visit the Codeplex site here:

https://killcrmplugin.codeplex.com/

You may download a zip file containing everything required to run the tool (via the command-line) or you can download the source and build your own copy.

I have tested it in a limited set of environments so if you have any issues, please let me know.

Error: Proxy type with the name account has been defined by another assembly

I am a big fan of the Simplified Connection technology added by the Xrm.Client assembly and use it almost exclusively.

Recently, I ran into the same issue at two separate customers. Both had just converted from CRM 4.0 and both had plugins and custom ASP.NET web pages in use.

Occasionally, the following exception would be encountered in either the custom ASP.NET pages or within the plugins:

A proxy type with the name account has been defined by another assembly.

Current type: Account, MyAssembly, Version=1.0.0.4, Culture=neutral, PublicKeyToken=be9afbacb707a086,

Existing type: Account, CustomPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Parameter name: account”

The solution was quite simple:

When creating your connection:

var connection = CrmConnection.Parse(connectionString);

set the ProxyTypesAssembly property:

connection.ProxyTypesAssembly = Assembly.GetExecutingAssembly();

That will instruct .NET to use the executing application as the source for Dynamics CRM proxy types.

I hope that saves you some time.

Date.js and Dynamics CRM 2011

In a word: NO.

I recently added the JavaScript date library Date.js to an installation to get some date calculation functionality.

It worked great but totally broke the CRM date controls.

After much troubleshooting and some helpful advice from my friend Tanguy, I realized Date.js was incompatible with CRM and I had to remove it as a web resource in order to return CRM's native date control functionality.

Upgrades: Contracts and Contract Detail Records

Contracts within Dynamics CRM can sometimes be a little peculiar to work with.  Case in point: You must have at least one Contract Detail record associated with the contract in order to make it function properly.

The issue there is that some companies don't actually care about detail lines, but they have to have them anyway.

On a recent upgrade, the previous consultant had created a plugin to automatically create a dummy, or placeholder, Contract Detail record.

We ran into an issue later when we found that CRM was very upset when attempting operations such a Renew Contract, when it found start and stop dates on the Contract Detail that were outside of the start and stop dates of the Contract itself.

It turns out that for some reason, the internal CRM process that checks the Contract Detail start and stop dates does not occur when you create the record via the SDK.  No idea why that is, but it did cause a bit of a problem until I tracked it down and modified the custom plugin to use different start/end dates on the Contract Detail.

You can use this SQL query to locate Contract Detail lines that are outside of the bounds of the parent Contract:

SELECT     
    'Status' = CASE
        WHEN ContractDetail.ActiveOn < Contract.ActiveOn THEN 'Before Contract Start'
        WHEN ContractDetail.ExpiresOn > Contract.ExpiresOn THEN 'After Contract End'
        END,
    CONVERT(VARCHAR(10), dbo.fn_UTCToLocalTime(Contract.ActiveOn), 101)
       AS "Contract ActiveOn", 
    CONVERT(VARCHAR(10), dbo.fn_UTCToLocalTime(ContractDetail.ActiveOn), 101)
       AS "Contract Detail ActiveOn", 
    CONVERT(VARCHAR(10), dbo.fn_UTCToLocalTime(Contract.ExpiresOn), 101)
       AS "Contract ExpiresOn",
    CONVERT(VARCHAR(10), dbo.fn_UTCToLocalTime(ContractDetail.ExpiresOn), 101)
       AS "Contract Detail ExpiresOn",
    Contract.Title, 
    ContractDetail.Title AS "Contract Detail Title",
    'http://[crm server]/[organization]/userdefined/edit.aspx?etc=1010&id=%7b' + 
     convert(nvarchar(50), Contract.ContractId) + '%7d'

FROM         
    Contract INNER JOIN
        ContractDetail ON Contract.ContractId = ContractDetail.ContractId
WHERE
    (ContractDetail.ActiveOn < Contract.ActiveOn
    OR
    ContractDetail.ExpiresOn > Contract.ExpiresOn)
    AND
    Contract.StateCode in (1,2,3)

     --1= Draft
     --2= Invoiced
     --3= Active
     --4= On Hold
     --5= Canceled
     --6= Expired
 

I have included a column containing a URL that will open the parent contract. If you need to use this line, just replace the [crm server] and [organization] placeholders with the proper values.

Also, I am filtering the Contracts based on their StateCode.  The comment block at the end of the SQL script lists the available Contract states should you wish to modify the query.

 

You have three courses of action once you have this data:

1. Change the contract start and stop dates using direct SQL (unsupported, but fast).

2. Write a small application that uses the CRM SDK to correct the dates.

3. Manually delete the offending Contract Detail line and add another.

Retrieving the OptionSet Label using JavaScript

I was adding some custom JavaScript to a customer's Opportunity Entity when I, again, learned the importance of not over-thinking things.

I was concatenating three field values and using that combined value to set the value of another field. Unfortunately, one of those fields was an OptionSet.

The problem with OptionSets is that when you retrieve the value of the field, using the following command:

var fieldValue = Xrm.Page.getAttribute("field").getValue()

returns the integer value of the field, not the textual label displayed on the form – and it was the latter that I needed.

So I'm thinking to myself, "Man, now I have to go and get the metadata for that OptionSet and pull out the label."  That is not a lot of fun and not exactly performant either, so I would need to optimize the way I would retrieve and store this data.  Again, not a lot of fun.

Then it occurred to me: I didn't need to get the Label for just any value in the OptionSet, just the one that was selected.

And it was the Xrm.Page object model to my rescue.

This command will retrieve the currently selected Label from an OptionSet on the form:

var fieldValue = Xrm.Page.getAttribute("field").getText()

This worked just fine for my purposes, and it might for yours as well.

 

References:

Sample: Retrieve Entity Metadata Using JavaScript

Contracts, Renewals, and Plugins

I ran into an interesting issue today during a CRM 4.0 upgrade to 2011.

We have a plugin that automatically generates a Contract Detail record when a Contract is created. This is because Contracts need at least one detail record but the customer is not using the actual details.

The issue arose when the Contract was renewed. The Contract Detail creation plugin was fired and it threw an error because it didn't like something about the Contract Detail record it was creating.

I investigated the Renewal Process and found that we should not be firing the Contract Detail creation plugin during this process because CRM takes care of everything.

Once I made that determination, I had to discover a way to differentiate between Create of a new Contract and Renew of an existing Contract.

Here is my solution:

if (context.ParentContext != null &&
    context.ParentContext.MessageName == MessageName.Renew)
{
    return;
}

In the case of a Renewal, we will have a sub-step that is actually the Create.  The main, or parent step is actually the Renew.

By checking to see if our Create step has a parent step, AND if that parent step is a Renew message, then I will exit the plugin and not perform the creation of the Contact Detail record.