Two major functionalities going to be deprecated from future releases of Dynamics CRM..

As a part of important changes coming in the future releases of Dynamics CRM, two major existing functionalities which had been in place from CRM 2015 version would be deprecated and find their details as below.

  1. Removal of legacy form rendering option
     With Microsoft Dynamics CRM Online 2015 Update 1, introduced a new form rendering engine (sometimes called “turbo forms” ) that provides improved performance. This new rendering engine became available for customers with on-premises deployments with Microsoft Dynamics CRM 2016.

Because such a change can introduce different behaviors in form scripts, CRM 2016  currently provide a Use legacy form rendering option in System Settings so that an organization can opt out if they detect problems. This is intended to be a temporary solution. We hope these kind of issues would be addressed soon.

The Use legacy form rendering option will be removed in the next major release. Legacy form rendering (sometimes called “refresh forms”) is deprecated and will no longer be available. Only the rendering option will be removed; the form definition is still supported. Organizations who have not yet resolved issues with their customizations by using the new form rendering engine should take action immediately so that they will be able to upgrade to the next major version.

If you have turned on legacy form rendering, please turn it off and test your scripts. During testing, if you see breaking changes in forms that use:

  • The supported client API objects and methods documented in MSDN: Client-side programming reference, report this to CRM technical support.
  • Unsupported form objects or methods, remove these customizations or find a different way to achieve the results you want.

So Organizations be aware of this..

2.Microsoft Dynamics Email Router is deprecated

 The Microsoft Dynamics CRM Email Router will become deprecated in the next major release of Microsoft Dynamics CRM. To prepare for this change, we strongly recommend that you migrate all email routing functionality to use the server-side synchronization feature. More information: Migrate settings from the Email Router to server-side synchronization
Cheers,
PMDY

About XMLHTTPRequest

We all know with CRM 2016 we should use Web API instead of oData in JavaScript. So before actually implementing that let us understand XMLHTTPRequest which would be a major part of this code.

Lets take a code example and then analyze each and every component

The following script demonstrates how to create and use the XMLHttpRequest object. For best client-side performance, the XMLHTTP request is asynchronous and uses an onreadystatechange event handler to process the data returned by the call. The script uses the getXMLHttpRequest()function defined above to create the request object.

 X : Integer denoting readystate changes

The full list of readyState values is:

State  Description
0      The request is not initialized
1      The request has been set up
2      The request has been sent
3      The request is in process
4      The request is complete
function handler()
{
    if (oReq.readyState == X /* complete */) {
        if (oReq.status == 200) {/*Click here to check all statuses possible*/
            console.log(oReq.responseText);/*Responsetext property*/
        }
    }
}
var oReq = getXMLHttpRequest();
if (oReq != null) {
    oReq.open("GET", "http://localhost/test.xml", true);
    oReq.onreadystatechange = handler;
    oReq.send();
}
else {
    window.console.log("AJAX (XMLHTTP) not supported.");
}

Sample Workout


Further process to be followed in my next post..

 

Cheers,

PMDY

Calling Workflows and Dialogs from Javascript

Today we would see calling workflows and dialogs from javascript

Call Workflow

Runs the specified workflow for a particular record asynchronously. Optionally, you can add callback functions which will fire when the workflow has been executed successfully or if it fails.

Parameters: Workflow ID/Guid, Record ID/Guid, Success Callback (function), Error Callback (function), CRM Base URL (not required on forms/views)

Usage:

Process.callWorkflow("4AB26754-3F2F-4B1D-9EC7-F8932331567A", 
    Xrm.Page.data.entity.getId(),
    function () {
        alert("Workflow executed successfully");
    },
    function () {
        alert("Error executing workflow");
    });

 

Call Dialog

Note: This function has been deprecated. It will remain in the solution, however no future enhancements or fixes will be done. Please check out Alert.js for a better way of showing dialogs.

Opens the specified dialog for a particular record in a CRM light-box, or Modal Dialog if run from Outlook. Once the dialog is closed, a custom callback function can be executed, e.g. to refresh the form with new data set by the dialog.

Parameters: Dialog ID/Guid, Entity Name, Record ID/Guid, Callback function, CRM Base URL (not required on forms/views)

Usage:

Process.callDialog("C50B3473-F346-429F-8AC7-17CCB1CA45BC", "contact", 
    Xrm.Page.data.entity.getId(),         
    function () { 
        Xrm.Page.data.refresh(); 
    });

Cheers,
PMDY

Building SQL Query using Query Expression programmatically

Today we will see how to query the data in ms crm similar to a SQL Query using Query Expression.

// Build the following SQL query using QueryExpression:
// SQL Query:
//		SELECT contact.fullname, contact.address1_telephone1
//		FROM contact
//			LEFT OUTER JOIN account
//				ON contact.parentcustomerid = account.accountid
//				AND
//				account.name = 'Litware, Inc.'
//		WHERE (contact.address1_stateorprovince = 'WA'
//		AND
//			contact.address1_city in ('Redmond', 'Bellevue', 'Kirkland', 'Seattle')
//		AND 
//			contact.address1_telephone1 like '(206)%'
//			OR
//			contact.address1_telephone1 like '(425)%'
//		AND
//			DATEDIFF(DAY, contact.createdon, GETDATE()) > 0
//		AND
//			DATEDIFF(DAY, contact.createdon, GETDATE()) < 30
//		AND
//			contact.emailaddress1 Not NULL
//			   )


Similar Query using QueryExpression:
QueryExpression query = new QueryExpression()
{
    Distinct = false,
    EntityName = Contact.EntityLogicalName,
    ColumnSet = new ColumnSet("fullname", "address1_telephone1"),
    LinkEntities = 
    {
        new LinkEntity 
        {
            JoinOperator = JoinOperator.LeftOuter,
            LinkFromAttributeName = "parentcustomerid",
            LinkFromEntityName = Contact.EntityLogicalName,
            LinkToAttributeName = "accountid",
            LinkToEntityName = Account.EntityLogicalName,
            LinkCriteria = 
            {
                Conditions = 
                {
                    new ConditionExpression("name", ConditionOperator.Equal, "Litware, Inc.")
                }
            }
        }
    },
    Criteria =
    {
        Filters = 
        {
            new FilterExpression
            {
                FilterOperator = LogicalOperator.And,
                Conditions = 
                {
                    new ConditionExpression("address1_stateorprovince", ConditionOperator.Equal, "WA"),
                    new ConditionExpression("address1_city", ConditionOperator.In, new String[] {"Redmond", "Bellevue" , "Kirkland", "Seattle"}),
                    new ConditionExpression("createdon", ConditionOperator.LastXDays, 30),
                    new ConditionExpression("emailaddress1", ConditionOperator.NotNull)
                },
            },
            new FilterExpression
            {
                FilterOperator = LogicalOperator.Or,
                Conditions =
                {
                    new ConditionExpression("address1_telephone1", ConditionOperator.Like, "(206)%"),
                    new ConditionExpression("address1_telephone1", ConditionOperator.Like, "(425)%")
                }
            }
        }
    }
};

DataCollection<Entity> entityCollection = _service.RetrieveMultiple(query).Entities;

// Display the results.
Console.WriteLine("List all contacts matching specified parameters");
Console.WriteLine("===============================================");
foreach (Contact contact in entityCollection)
{
    Console.WriteLine("Contact ID: {0}", contact.Id);
    Console.WriteLine("Contact Name: {0}", contact.FullName);
    Console.WriteLine("Contact Phone: {0}", contact.Address1_Telephone1);
}
Console.WriteLine("<End of Listing>");
Console.WriteLine();
Cheers,
PMDY

Using Pre & Post Entity images in MS CRM

Options we have to access fields that are not included in the context.

  1. Obtain a reference to Organisation Service within your plugin, and then use a Retrieve request to return the fields you need from the plugin. As the context will always contain the GUID for the record that has changed, this is generally the most common way you will see this done.
  2. Utilise either a Pre or Post Entity Image of the record, which contains the attributes that you need to access.

Previously we would be using the First option for any kind of operations.Recently though, I have been evaluating Entity Images more and more, and have begun to actively use them as part of my plugins. Entity Images are, essentially, snapshots of the CRM record that are stored on the platform either before the database operation has completed or straight after – you can decide which one you want to use, depending on what your plugin is doing. They are configured as part of deploying your plugin to your target CRM instance, and the steps involved are actually quite simple – I think it’s generally the case that they are not well-understood or utilised by developers who are learning about CRM for the first time.

So why should you go to the extra effort to use them within your plugins?

As alluded to above, using Pre Entity Images means you can get a snapshot of your CRM data before the data was changed. This may prove to be particularly invaluable in cases where you need to perform a before/after comparison on a particular field, and then execute a specific action either way. The only non-entity image way of doing this within CRM would be via hidden field that stores the current value of your field in question, which is referenced and updated once your business logic has completed. A slightly inelegant solution, and one that is questionable, given that we can utilise Entity Images instead. Having ready access to the attributes which may not necessarily be exposed to the plugin when it is executed is particularly invaluable, given that this avoids a scenario where you would have to go down option 1). Disadvantages of this approach is the potential for unnecessary delays in your plugin completing and problems with your CRM instance as a whole, if your CRM’s web services are already accessed to breaking point.

In these cases, we go with plugin images. That’s all for today.. would be sharing code related stuff about using plugin images in my next blog post..

Cheers,

PMDY

 

addPreSearch and addCustomFilter to your lookups in Dynamics 365

Using these methods we can now easily filter the lookup in Dynamics 365.

Using addPreSearch we can specify a handler to PreSearch Event. Inside the handler we can specify our fetch xml query that can be used for filtering. The filter applied in the fetch xml will be combined with the any previously added filter as an ‘AND’ condition.

To remove the filter we can use removePreSearch method.

formContext.getControl(arg).addPreSearch(myFunction)

  1. Pass execution context
  2. Specify the argument which is nothing but the lookup field you want to addPresearch functionality.

Example:

formContext.getControl(“csz_consumedproduct”).addPreSearch(filterConsumedProductLookup);

function filterConsumedProductLookup(executionContext) {
debugger;
var entityLogicalName = “product”;
var filter = ” <filter>” +
” <condition attribute=’name’ operator=’not-like’ value=’%Accompanied%’ />” +
” </filter>”;
executionContext.getFormContext().getControl(“csz_consumedproduct”).addCustomFilter(filter, entityLogicalName)
}

Cheers,

PMDY

Cheers,

PMDY

How to show Javascript alerts with appropriate alert messages

So often in CRM, users need to be alerted of their actions. For example, a typical alert might be a message that warns you when a user attempts to enter a text string into a numeric field.

Using the alert() method, a message is usually displayed in a pop-up window. This type of alert method does not require any in-depth response from users, who will simply acknowledge it by clicking the OK button.

Microsoft CRM Pop-Up Message 1

With Microsoft Dynamics CRM 2013 and 2015, Microsoft Dynamics CRM has continued to improve the user experience by introducing a flattened interface so to speak.

This approach focuses on removing excessive clicks and flattening out pop-up windowsfound in the previous version of Dynamics CRM.

With that in mind, using an alert method that requires a response from users seems to contradict the flattening approach.

At the same time, Dynamics CRM 2013 and 2015 introduced new alert methods:setFormNotification and setNotification.

These two methods display messages on the form instead of on a pop-up window, and require no clicking from users.

The Classic CRM Alert Method Versus the New Improved CRM Alert

In this article, I am going to demonstrate how the new alert methods are different from the classic one, and how they can be used.

The example here is a credit card form where credit card numbers and a CVV codes were to be validated. In the previous version of Dynamics CRM, using the classic alert() method, users are typically notified of errors one by one and users have to acknowledge them by clicking OK.

Microsoft CRM Pop-Up Message 2

Microsoft CRM Pop-Up Message 3

Of course this alert method can be slightly improved by displaying the error messages in one single alert, thus reducing the number of pop-ups and clicks to one. But still users have to make the effort clicking the OK button.

Microsoft CRM Pop-Up Message 4

Eliminating the Mouse Clicks on the Notifications in Dynamics CRM

In Dynamics CRM 2013 or 2015, mouse click(s) can be completely eliminated by using setFormNotification that displays message on the form header area. And depending on the nature of messages, they can be categorized as information, error and warning.

  • Xrm.Page.ui.setFormNotification(‘Please fix the following error(s):’, ‘INFORMATION’,);
  • Xrm.Page.ui.setFormNotification(‘Invalid Credit Card Number.’, ‘ERROR’);
  • Xrm.Page.ui.setFormNotification(‘Invalid CVV Code.’, ‘WARNING’);

Microsoft CRM Pop-Up Message 5

There is an extra line of code to clear the message from the form, simply use the following method:

  • Xrm.Page.ui.clearFormNotification()

When there are many fields on one form, users may find it difficult to locate the fields that require correction (of course, additional methods can be developed to help locate such fields, for example, setFocus or setRequiredLevel).

In such a case, I would tend to use another alert, setNotification, that displays messages next to the fields. This way, fields can be visibly located and corrected.

  • Xrm.Page.getControl(“new_creditcardnumber”).setNotification(‘Invalid Credit Card Number.’);
  • Xrm.Page.getControl(“new_cvvcode”).setNotification(‘Invalid CVV Code.’);

Microsoft CRM Pop-Up Message 6

Deprecation of DynamicEntity and inclusion of early, late bound entity classes

In Microsoft Dynamics CRM 2011, you can choose from several programming scenarios to find the model that best suits your needs. The programming scenario in Microsoft Dynamics CRM 4.0 used the Web Services Description Language (WSDL) with early bound classes for each entity, and the DynamicEntity class for late-bound programming. You were also required to use late-bound programming for plug-in and custom workflow development. All of this has changed with the new programming models.

 

The main development scenario for Microsoft Dynamics CRM 2011 no longer uses the WSDL. Instead, you now reference two assemblies that allow you to connect to any Microsoft Dynamics CRM system for both early and late bound types. This scenario can be described as late binding or loosely typed. To use late bound types, use the Entity class. This class defines a collection of attributes that can be used to get and set the values of attributes. To use this model, the exact logical name must be known (and specified) as a string.

 

Alternatively, you can use early bound classes generated directly from the metadata, which include all customizations. The generated classes provide early binding and IntelliSense to aid you as you write custom code.

 

The DynamicEntity class has been replaced by the base class Entity. This means that all types are discoverable at both build time and runtime, making all strongly-typed entities now loosely-typed entities. You can use both programming scenarios in the same code as shown in the following example:

C#:

Account entity = new Account();
entity[“name”] = “My Account”; //loosely typed, late binding
entity.AccountNumber = “1234”; //strongly typed, early binding

The Microsoft Dynamics CRM SDK documentation includes samples that use both programming scenarios. The early bound samples use a file of strongly-typed classes that are generated with the code generation utility from a new, uncustomized installation of Microsoft Dynamics CRM. To run the samples, you must generate a file of strongly-typed classes from your installation. You can decide whether to create a proxy assembly from the generated code file or to include the file in your project directly.
@Information above is based on official Microsoft communication on the same.

getIsDirty Property

Returns a Boolean value indicating if there are unsaved changes to the attribute value.

Attribute Types: All

Xrm.Page.getAttribute(arg).getIsDirty()
Return Value
Type: Boolean. True if there are unsaved changes, otherwise false.In this way you can search for all unsaved changes on the form.

Auto save picks up the fields which were dirty(Not saved yet).

Enhancements to solution patches in 2016

Introduction

Now moving forward we will look at a common scenario of sending out Patches for your solution. Often it happens that you may need to update a few components of your managed solution and ideally would like to have a way to only have those shipped and then have a way to compile all of these patches in the next release of the solution. This helps with better versioning control and management of the solution assets.

Patches

Taking it forward from our previous article, here is what our solution looked like.

Keep your eye on the Version number of the solution as this is one of the critical thing which will come to notice as we move ahead.

Now suppose due to some requirement changes I have to increase the maximum length for “First Name” field of Contact from 155 to 175.

Since this field was a part of the earlier solution that we had and this is the only change we need to make to that solution, we will go ahead and create a patch for the original solution

Use the “Clone a Patch” button added to the solutions views

Notice the other buttons as well “Clone Solution” and “Apply Solution Upgrade”, this completes the entire solution management architechture.

It is a good practice to increment the version number for a proper version management of the solution. Since this is a patch it only allows you to modify the last 2 digits in the version.

This creates the following new solution.

Add only the Contact “First Name” field to the solution and update the length as shown below.

When you import this patch on the client environment this solution will be applied on top of the base solution and only the “firstname” field will be updated.

You can create multiple patches for a solution. To create another patch you would select the original solution and click “Clone a Patch”.

The original solution can no longer be modified.

In the Target system, you can delete the managed solution of a patch without affecting the base solution installed there.

The patches don’t have a dependency on each other, so in the target system I deleted the first patch that was imported and then imported the second patch and it works just fine. So that’s huge plus, since one of the problems with earlier solution management architecture was the dependencies that would be created between managed solutions. So here there is no dependency of the patches amongst them. But of course they do need the base solution present since they are patch for the base solution.

Uninstalling the base solution on the target also automatically uninstalls all the patches that were installed for the base solution.

When you want to plan your next version of the solution, you usually want to rollup all of the patches that were released for the base solution into the new solution, before you can add new changes in there. This can be done using the “Clone Solution” button.

Now this time, you are only allowed to change the major and minor parts of the version.

Once the operation completes, you will find the patches deleted and the base solution version updated.

This solution is now available for editing as well.

When the Upgrade solution is imported on the target system, it auto detects this to be an upgrade to an existing installed package and notifies you of the same.

On the next step of the import wizard, you get the option to choose if you want this imported as “Holding Solution”. This option will show up and checked by default if the target organization has base solution and patches for base solution installed. You can learn more about the “Holding Solution” concept here

When the solution import completes, you see a new option “Apply Solution Upgrade” since you chose to import this as a holding solution.

If you do not choose to apply the solution upgrade, you will see three solutions in the target system.

Now choose the original Base solution on the target system and click on the “Apply Solution Upgrade” button to keep a single solution for the package instead of the three that appear now

If there were no patches installed in the target system and the “Stage for Upgrade” option is unchecked, you have the option to check it if you want. If you leave it unchecked it simply updates the solution and you do not need to “Apply Solution Upgrade”.

Conclusion

Though I have only explored this using basic customizations to entities, the whole solution management framework from creating a solution to patching to moving it to the next release and similarly “upgrading solution” on the target system appears that, it would reduce if not end a lot of “solution import” woes of the ISV community.