Thursday, March 22, 2012

Extending CrmSvcUtil (Filtering the generated classes)

Most people use the CrmSvcUtil  in its native form. But CrmSvcUtil exposes some interfaces to extend its functionality. Here is the list of all the extension interfaces. One of my personal favourite is ICodeWriterFilterService. This interface can control which entity/attribute/relationship etc. would be created by CrmSvcUtil. By default, CrmSvcUtil generates all the entities, attributes and relationships. If you run the CrmSvcUtil for an out of box CRM online organization, it will generate a file of around 5 mb with all the entities and attributes. In reality we need to generate very few entities for our solutions. We do not need all the extra code generated by the utility. The size of the file is even more important when we are writing code for CRM online. In this blog, we are going to learn how to filter the entities generated by the code generation utility.
Here are the steps:
  1. Open up the solution we created in our last blog.
  2. Add a new class file to the project. I named my class file BasicFilteringService.cs. I copied this file from the sdk samples. Here is the code. The code will restrict the code generation utility to generate just the custom entities.
    using System;
    using Microsoft.Crm.Services.Utility;
    using Microsoft.Xrm.Sdk.Metadata;
    namespace CRMExtensions
    {
        /// 
        /// Sample extension for the CrmSvcUtil.exe tool that generates early-bound
        /// classes for custom entities.
        /// 
        public sealed class BasicFilteringService : ICodeWriterFilterService
        {
            public BasicFilteringService(ICodeWriterFilterService defaultService)
            {
                this.DefaultService = defaultService;
            }
    
            private ICodeWriterFilterService DefaultService { get; set; }
    
            bool ICodeWriterFilterService.GenerateAttribute(AttributeMetadata attributeMetadata, IServiceProvider services)
            {
                return this.DefaultService.GenerateAttribute(attributeMetadata, services);
            }
    
            bool ICodeWriterFilterService.GenerateEntity(EntityMetadata entityMetadata, IServiceProvider services)
            {
                if (!entityMetadata.IsCustomEntity.GetValueOrDefault()) { return false; }
                
                return this.DefaultService.GenerateEntity(entityMetadata, services);
            }
    
            bool ICodeWriterFilterService.GenerateOption(OptionMetadata optionMetadata, IServiceProvider services)
            {
                return this.DefaultService.GenerateOption(optionMetadata, services);
            }
    
            bool ICodeWriterFilterService.GenerateOptionSet(OptionSetMetadataBase optionSetMetadata, IServiceProvider services)
            {
                return this.DefaultService.GenerateOptionSet(optionSetMetadata, services);
            }
    
            bool ICodeWriterFilterService.GenerateRelationship(RelationshipMetadataBase relationshipMetadata, EntityMetadata otherEntityMetadata,
            IServiceProvider services)
            {
                return this.DefaultService.GenerateRelationship(relationshipMetadata, otherEntityMetadata, services);
            }
    
            bool ICodeWriterFilterService.GenerateServiceContext(IServiceProvider services)
            {
                return this.DefaultService.GenerateServiceContext(services);
            }
        }
        //
    }
    
  3. Add codewriterfilter parameter to CrmSvcUtil.exe.config file. <add key="codewriterfilter" value="CRMExtensions.BasicFilteringService, CRMExtensions"/>
  4. Press F5 to debug the solution. It will generate a file  with the name specified in “o” or output parameter.
  5. Check the size of the file. It will be very very small as compare to the file generated without codewriterfilter. The size of the file created for default CRM online  organization with codewriterfilter is just 44 kb as compared to 4.7 mb without codewriterfilter.
Note if we replace the following line in BasicFilteringService.cs
if (!entityMetadata.IsCustomEntity.GetValueOrDefault()) { return false; }
with
if (entityMetadata.LogicalName!="account") { return false; }
The utility will generate just account entity. Happy programming.

Monday, March 12, 2012

How to use CrmSvcUtil (Improved Version) in Visual Studio Part 2

Few days ago, I posted a blog on how to use CrmSvcUtil in Visual Studio. Here is link to that blog. Last few days, I spent some time on working out how to extend the CrmSvcUtil. I read this article on MSDN and I figured out that my last blog was not the best practice solution to use the CrmSvcUtil in Visual Studio. Here is new improved version of “How to use CrmSvcUtil in Visual Studio”
  1. In Visual Studio, create a new “Class Library” project as shown in the following screen shot csunew_thumb[4]
  2. Delete the class file “class1.cs” created by the Visual Studio.
  3. Add the references to following files to the project. These files can be find in SDK\Bin folder of CRM SDK. You can add a reference by right-clicking the name of your project and then selecting Add Reference.
    • CrmSvcUtil.exe
    • Microsoft.Crm.Sdk.Proxy.dll (Only needed for the on-premise and IFD installation)
    • Microsoft.Xrm.Sdk.dll
  4. Click on “Show All Files” in solution explorer as shown in the following screen shot.csu8_thumb[6]
  5. The solution explorer will look like the following screen shot.csu9_thumb[6]
  6. Click on “bin” folder highlighted in yellow in the above screen shot. It will open open up an another folder “debug”.Click on the debug folder.Now the solution explorer will look like the following screen shot.csu10_thumb[7]
  7. Now we need to add CrmSvcUtil.exe.config to the debug folder. There are following two ways to achieve this.
    • Add the file to debug folder by using window explorer outside the visual studio.
    • or Right Click on the bin folder in the solution explorer and select “Include in project”. Now Select the debug folder and add an application configuration file by selecting “Project” menu----“Add New Item”---“Application Configuration File”.
  8. CrmSvcUtil.exe.config will contain “CrmSvcUtil.exe parameters”. The solution explorer will look like a following screen shot.csu11_thumb[3]

  9. Here is a list of all the parameters we can use with CrmSvcUtil.
  10. Add the following keys to CrmSvcUtil.exe.config file.
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <appSettings>
        <add key="url" value="https://cdc.crm5.dynamics.com/XRMServices/2011/Organization.svc"/>
        <add key="o" value="CrmProxy.cs"/>
        <add key="u" value="username@live.com"/>
        <add key="p" value="password"/>
        <add key="servicecontextname" value="XrmContext"/>   
      </appSettings>
    </configuration>
  11. Now the interesting part, right click on project node and press properties.csu5_thumb3
  12. It will pop up a following dialog box. Click on the “Build Events” tab.csu12 
  13. Type "$(TargetDir)CrmSvcUtil.exe" in “Post-build event command line” and choose “On Successful build” on  “Run the post-build event” as shown in the above screen shot.
  14. Now Build the solution.
  15. You can check the “Output Window” of Visual Studio for the result. If everything goes smoothly, it will create “CrmProxy.cs” file in debug folder.
  16. Click on “Refresh” button in solution explorer and you can see the file in the debug directory.
  17. Check the CrmProxy.cs, it will have all the crm entities classes and "XrmContext".

Tips


  • You can add, remove and edit keys in CrmSvcUtil.exe.config to pass parameters to code generator tool.
  • Try accessing the CRM through the browser before debugging, if you are working with CRM Online.
  • You can add this project to any of your crm solution. Change the
  • <add key="o" value="CrmProxy.cs"/> entry in the config file to generate the output file in the desired folder. for e.g <add key="o" value="C:\Users\amreek\Desktop\CrmProxy.cs"/>

Note

    I have chosen to run the CrmSvcUtil.exe in Post-build event. I will explain the reason in my next blog.

Thursday, March 8, 2012

How to use CrmSvcUtil (Code generation tool) in Visual Studio Part 1

We use CrmSvcUtil to generate early bind classes in CRM2011. Also, we have an option to generate Organisation service context. The only problem is that it is a command line tool. We have to go to command prompt, type the command with required parameters and then move the generated file to our project. It is a bit annoying.
In this blog we will learn, how to use CrmSvcUtil tool in Visual Studio. Here are the steps.
  1. In Visual Studio, create a new “Class Library” project as shown in the following screen shot csu1
  2. Delete the class file “class1.cs” created by the Visual Studio.
  3. Add the following files to the project from SDK\Bin folder of CRM SDK.
    • CrmSvcUtil.exe
    • Microsoft.Crm.Sdk.Proxy.dll
    • Microsoft.Xrm.Sdk.dll
  4. Add an  application configuration file to the project and name it “CrmSvcUtil.exe.config”. This file will contain all the parameters we can pass to “CrmSvcUtil.exe”. The solution explorer will look like a following screen.csu4
  5. Here is a list of all the parameters we can use with CrmSvcUtil.
  6. Add the following keys to CrmSvcUtil.exe.config file.
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <appSettings>
        <add key="url" value="https://cdc.crm5.dynamics.com/XRMServices/2011/Organization.svc"/>
        <add key="o" value="CrmProxy.cs"/>
        <add key="u" value="username@live.com"/>
        <add key="p" value="password"/>
        <add key="servicecontextname" value="XrmContext"/>   
      </appSettings>
    </configuration>
    
  7. Now the interesting part, right click on project node and press properties.csu5
  8. It will pop up a following dialog box. Click on the “Debug” tab csu6
  9. Select “Start external program” and choose the CrmSvcUtil.exe, we added in step 3.
  10. Now choose the “Working directory” where you want the output file to go.
  11. Debug the code and it will come up with following screen.csu7
  12. You can check the “Output Window” of  Visual Studio for the result. If everything goes smoothly, it will create “CrmProxy.cs” file in the folder selected in “Working directory” in step 14.
  13. Include the “CrmProxy.cs” file into the project.
  14. Check the CrmProxy.cs, it will have all the crm entities classes and "XrmContext".

Tips

  • You can add, remove and edit keys in CrmSvcUtil.exe.config to pass parameters to code generator tool.
  • Try accessing the CRM through the browser before debugging, if you are working with CRM Online.
  • You can add this project to any of your crm solution. Change the "Working directory" of the project to generate the CrmProxy file in a desired folder.

Wednesday, February 29, 2012

Step by step tutorial to add a CRM report on an entity form

Today we are going to add a CRM2011 report on an entity form. The report will run on a current record.
The solution is broken down into 4 steps
  1. Get the ReportViewer URL and the reportId
  2. Add an Iframe on an entity form
  3. Create a JavaScript webresource to set the src(URL) for Iframe.
  4. Call the webresource method on form load event.

How to get a ReportViewer URL and reportId

  • To get the reportviewer url and reportId, run the report from Workplace –Reports section as shown in the following screen shot.Report5
  • The report will look like the following screen shot.report1
  • The only thing we need from the above screen shot is URL at the top of the form. If you can’t see the URL press F11. Copy that URL. It will look like following
  • https://inventive.crm5.dynamics.com/crmreports/viewer/viewer.aspx?action=filter&helpID=Account%20Overview.rdl&id=%7bB13C5C38-BB4B-E111-9453-D8D3855B355E%7d
  • We are going to remove the parts of the URL with yellow background.
  • We don’t need the server and organization path as we will use relative path.
  • We don’t need the helpID parameter as report can run without it.
  • We will change the action = Filter to Run.
  • We will add an another parameter to the URL named “records” to run it on the current record.
  • The modified URL will look like
  • /crmreports/viewer/viewer.aspx?action=run&id=%7bB13C5C38-BB4B-E111-9453-D8D3855B355E%7d&records="

Add an Iframe on an entity form

  • Open the form in the customization mode and add a new tab as shown belowReport2
  • Change the formatting of the section to “One Column” and insert an Iframe. You can set the properties of the Iframe as shown in the following screen shot. Just remember the name of the Iframe is “IFRAME_OverviewReport” not “OverviewReport”.Report3
  • Save the customizations.

Create a JavaScript webresource

  • Create a JavaScript webresource and add the following function to it.
    function showReport() 
    {    
    
        //Get iframe control 
        var iframeObject = Xrm.Page.getControl("IFRAME_OverviewReport");
    
        if (iframeObject != null)
        {
            //URL we created in first step + id of the current record (Xrm.Page.data.entity.getId())
            var strURL = "/crmreports/viewer/viewer.aspx?action=run&id=%7bB13C5C38-BB4B-E111-9453-D8D3855B355E%7d&records=" + Xrm.Page.data.entity.getId();
            
            //Set iframe URL
            iframeObject.setSrc(strURL);
    
        }
    }
  • Check the highlighted line it is the same URL we created earlier in step (How to  a get ReportViewer URL and reportId).
  • Save the changes and publish the webresource.

Call the webresource method on form load event

  • Open the form in the customization mode again and click on form properties.
  • Add the webresource to the form and call the showReport function as shown in the screen shot below.report7
  • Save the form and publish the changes.
  • Test the solution.

Thursday, February 23, 2012

Use of “Filtering Attributes” in plugin registration

Recently one of my clients asked me to make some changes to one of their existing plugin. The plugin was sending an email to a user when “Estimated Revenue” field is updated. The plugin was comparing  the value of “Estimated Revenue” from the  PreEntityImage with value from PostEntityImage. It made me think that why you would use PreEntityImage and PostEntityImage values to do that when you can achieve that by setting  “Filtering Attributes” during plugin registration.
parentcustomer  
This is very handy parameter of step registration process. It is equivalent to “IsDirty” function in JavaScript. If you use “Filtering Attributes” on post update events, you don’t need to compare the value of fields using PreEntityImage and PostEntityImage. The other biggest advantage is that your plugin does not execute on every update event.
Here is some code. This code creates a note for a contact when parentcustomerid field is updated.
protected void ExecutePostContactUpdate(LocalPluginContext localContext)
        {
            if (localContext == null)
            {
                throw new ArgumentNullException("localContext");
            }             // TODO: Implement your custom Plug-in business logic.
            IPluginExecutionContext context = localContext.PluginExecutionContext;
            IOrganizationService service = localContext.OrganizationService;
            
            // The InputParameters collection contains all the data passed in the message request.
            if (context.InputParameters.Contains("Target") &&
            context.InputParameters["Target"] is Entity)
            {
                // Obtain the target entity from the input parmameters.
                Entity entity = (Entity)context.InputParameters["Target"];
                
                try
                {
                    Entity task = new Entity("task");
                    task["subject"] = "Account number is missing";
                    task["regardingobjectid"] = new EntityReference("contact", entity.Id);
                    task["description"]= "Parent Customer field has been updated".;

                     // Create the task in Microsoft Dynamics CRM.
                    service.Create(task);
                                        
                }

                 catch (FaultException ex)
                {
                    throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
                }

             }
        }
To run this code create a plugin using developer's toolkit on contact entity. Use the values shown in the screen shot to register the update step. Then overwrite the  ExecutePostContactUpdate() method with this code. Deploy the plugin and test it.
Here is link to a step by step tutorial to create a plugin using developers toolkit.

Friday, February 17, 2012

Plugin to update children records when parent record is updated

I love the developer's toolkit. It makes it so easy to deploy plugins. In this blog we are going to create a plugin that will update the Main Phone (“telephone1”) of the contacts when the Main Phone of their parent account is updated. We will use the OrganizationContext to update the contacts.
  1. Create a new solution in CRM2011. I named my solution “CRM Plugin Solution”.
  2. Open Visual Studio 2010. Select File—New –Project. It will display new project templates dialog as shown in the screen sheet belowp1
  3. Select “Dynamics CRM 2011 Package” project. This is also optional. You can go ahead and select “Dynamics CRM 2011 Plugin Library”, but then you cannot deploy the plugin straight from the Visual Studio. You have to use Plugin Registration tool to register the plugin. Enter the name of project/solution.
  4. VS studio will display following dialog. Enter you CRM 2011 server details. Select the solution name we created in step 1 and click okp2
  5. Now right click on the solution and add “Dynamics CRM 2011 Plugin Library” project to the solution. The plugin project will already have a plugin.cs file.
  6. Now sign the plugin assembly. Right click on Plugin Project and select properties. Select Signing tab from left navigation of project property page. On the Signing tab, select the Sign the assembly check box and set the strong name key file of your choice. At a minimum, you must specify a new key file name. Do not protect your key file by using a password.
  7. If you cannot see the “CRM Explorer” window on the upper left side of the VS studio, click on View menu and select “CRM Explorer”. p3
  8. Now expand “Entities” Node. Right Click on “Account” entity and select “Create Plugin”.p4
  9. It will display a following screen. It is equivalent to “Update Step” screen in plugin registration tool. Notice the  “Pre Image Alias” and  “Post Image Alias”. I have passed the “telephone1” attribute to the pre image and post image. You can change of the Class attribute. Press Ok.image_thumb[7]
  10. It will create a PostAccountUpdateContacts.cs file with name mentioned in “Class” attribute in screen shot above. Double click on the class file (PostAccountUpdateContacts.cs).
  11. Add the following references at the top
  12.  using Microsoft.Xrm.Sdk.Client; // to get the OrganizationContext
     using System.Linq; // to use linq queries with OrganizationContext
    
  13. Scroll down the PostAccountUpdateContacts.cs file and look for ExecutePostAccountUpdateContacts method. Overwrite the method with the following code. Look at the comments to understand the code
  14.  protected void ExecutePostAccountUpdateContacts(LocalPluginContext localContext)
    {
        if (localContext == null)
        {
            throw new ArgumentNullException("localContext");
        }
        string oldPhone = ""; // to store the old Main Phone no:
        string newPhone = ""; //// to store the new Main Phone no:
    
        // get the plugin context 
        IPluginExecutionContext context = localContext.PluginExecutionContext;
    
        //Get the IOrganizationService
        IOrganizationService service = localContext.OrganizationService;
              
        //create the service context
        var ServiceContext = new OrganizationServiceContext(service);
        ITracingService tracingService = localContext.TracingService;
    
        // The InputParameters collection contains all the data passed in the message request.
        if (context.InputParameters.Contains("Target") &&
        context.InputParameters["Target"] is Entity)
        {
            // Obtain the target entity from the input parmameters.
            Entity entity = (Entity)context.InputParameters["Target"];
    
            // get the pre entity image
            Entity preImageEntity = (context.PreEntityImages != null && context.PreEntityImages.Contains(this.preImageAlias)) ? context.PreEntityImages[this.preImageAlias] : null;
            
            // get the post entity image
            Entity postImageEntity = (context.PostEntityImages != null && context.PostEntityImages.Contains(this.postImageAlias)) ? context.PostEntityImages[this.postImageAlias] : null;
    
            // get the preimage and postimage telephone1 value
            if (preImageEntity.Attributes.Contains("telephone1"))
            {
                oldPhone = (string)preImageEntity.Attributes["telephone1"];
            }
    
            if (postImageEntity.Attributes.Contains("telephone1"))
            {
                newPhone = (string)postImageEntity.Attributes["telephone1"];
            }
    
            if (newPhone != oldPhone)
            {
                try
                {
                    //Create query to get the related contacts
                    var res = from c in ServiceContext.CreateQuery("contact")
                                where c["parentcustomerid"].Equals(entity.Id)
                                select c;
    
                    foreach (var c in res)
                    {
                        Entity e = (Entity)c;
                        e["telephone1"] = newPhone;
    
                        //ServiceContext.Attach(e);
                        ServiceContext.UpdateObject(e);
                    }
    
                    ServiceContext.SaveChanges();
    
    
                }
                catch (FaultException ex)
                {
                    throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
                }
            }
    
        }
    }
    
  15. Now right click on CRM Package project we created in step 2 and select deploy. It will register the plugin assembly as well as step, preEntityImage and postEntityImage for the plugin. 
  16. Update the “Main Phone” on account entity and test the plugin.

Tuesday, January 31, 2012

Step by step plugin tutorial using Developer's Toolkit Part 3

This blog is an extension of my last blog Step by step plugin tutorial using Developer's Toolkit Part 2. We used “Create Wrapper” method to generate strongly typed classes. In this blog, we will use OrganizationServiceContext to create “Task” entity instead of using service.Create() method. To use OrganizationServiceContext in the plugin, we need to add reference to Microsoft.Xrm.Sdk.Client at the top of the class.
using Microsoft.Xrm.Sdk.Client;
Now create an instance of OrganizationServiceContext by passing the OrganizationService as shown below
// TODO: Implement your custom Plug-in business logic.
IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;
//ITracingService tracingService = localContext.TracingService;
//create a service context
var ServiceContext = new OrganizationServiceContext(service);
Now replace the
service.Create(task);
with
ServiceContext.AddObject(task);
ServiceContext.SaveChanges();
Deploy the plugin and test it.

Advantages of using OrganizationServiceContext

The biggest advantage of using OrganizationServiceContext is that we can track multiple entities/operations and save all the changes with ServiceContext.SaveChanges(); statement. Have a look at this article on MSDN.