Showing posts with label SDK. Show all posts
Showing posts with label SDK. Show all posts

Friday, October 30, 2009

Close Opportunity via SDK

Posted by Darren Liu at 11:19 AM

To close a CRM opportunity is different comparing to other CRM entities. If you want to close an opportunity, you need to use the WinOpportunityRequest and LostOpportunityRequest in the CRM SDK instead of the SetState requests. The code snippet below simply shows you how to set an opportunity to Win status via the CRM SDK.

opportunityclose close = new opportunityclose();
close.opportunityid = new Lookup();
close.opportunityid.Value = targetOppId;

WinOpportunityRequest request = new WinOpportunityRequest();
request.OpportunityClose = close;
// Update the status code according to your environment
request.Status = 1;

crmService.Execute(request);

Friday, February 27, 2009

Creating a Birthday Contact List

Today we welcome our guest blogger CRM MVP Darren Liu from the Crowe Horwath company.

Have you ever been asked by someone to get a list of contacts having birthdays during a certain time period from CRM? If so what have you done to perform this task? Within the application, birthdays are tracked on Contact records as a single date (including year). This causes problems when searching for birthdays in a certain time period as the birth date is evaluated including the year. To illustrate, consider the following example:

· John Dole, 10/1/1980

· Adam Smith, 9/1/1970

· Mark Francis, 10/10/1960

Within CRM, searching for date is done by range. There is no easy way to identify from the above contacts all those having birthday in October as any range you choose will include the year. Wildcard functions on date fields are not a workable solution.

There are several solutions to this problem including JavaScript to parse birthday on the onChange event, a custom report or a plug-in. The desired functionality is to be able to search by birth month, birth day, and/or birth year, allowing the user to quickly identify all birthdays in a certain time period.

In this blog, I will show you how to use a pre plug-in to parse the birthday field into day, month and year. This way, the users will able to perform searches using Advanced Find. I have chosen the plug-in approach because it will help me parse the birthday field not only when the users update the birthday on the contact form but also when updating the birthday through the CRM web service for data imports and data integration.

Implement the pre plug-in

1. Create New Attributes

Create three new attribute on the Contact entity form in CRM. After creating the new attributes, publish the Contact customization.

Display Name

Schema Name

Type

Searchable

Values

Birth Month

new_birthmonth

Picklist

Yes

Jan = 1, Feb = 2, Mar = 3, Apr = 4, May = 5, Jun = 6, Jul = 7, Aug = 8, Sept = 9, Oct = 10, Nov = 11, Dec = 12

Birth Day

new_birthday

Int

Yes

Min Value = 1

Max Value = 31

Birth Year

new_birthyear

Int

Yes

Min Value = 1900

Max Value = 9999

clip_image002

2. Create pre plug-in using Visual Studio

Create a plug-in project name Crm.Plugin, copy and paste the following code to your Plug-in project.

using System;


using System.Collections.Generic;


using System.Text;


using Microsoft.Crm.Sdk;


using Microsoft.Crm.SdkTypeProxy;


 


namespace Crm.Plugin


{


  public class MonthDayYearContactPlugin : IPlugin


  {


    public void Execute(IPluginExecutionContext context)


    {


    DynamicEntity entity = null;


 


    if (context.InputParameters.Properties.Contains(ParameterName.Target) &&


        context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)


    {


        entity = (DynamicEntity)context.InputParameters[ParameterName.Target];


        if (entity.Name != EntityName.contact.ToString()) { return; }


    }


    else


    {


        return;


    }


 


    try


    {


        if (entity.Properties.Contains("birthdate"))


        {


            CrmDateTime _birthdate = (CrmDateTime)entity["birthdate"];


            if (_birthdate.IsNull)


            {


                entity["new_birthday"] = CrmNumber.Null;


                entity["new_birthmonth"] = Picklist.Null;


                entity["new_birthyear"] = CrmNumber.Null;


            }


            else


            {


                DateTime birthdayValue = _birthdate.UserTime; 


                entity["new_birthday"] = new CrmNumber(birthdayValue.Day);


                entity["new_birthmonth"] = new Picklist(birthdayValue.Month);


                entity["new_birthyear"] = new CrmNumber(birthdayValue.Year);


            }


        }


    }


    catch (Exception ex)


    {


        throw new InvalidPluginExecutionException("An error occurred in the Month, Day, Year Plug-in for Contact.", ex);


    }


    }


  }


}


 




3. Register the plug-in The last step is to register the plug-in. To register the plug-in, you may use the Plug-in Registration tool from the MSDN Code Gallery. After the assembly is uploaded, you need to associate the following steps to the plug-in:





 
























Message: Create


Primary Entity: contact


Filtering Attributes: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Parent Pipeline



Message: Update


Primary Entity: contact


Filtering Attribute: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Parent Pipeline



Message: Create


Primary Entity: contact


Filtering Attributes: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Child Pipeline



Message: Update


Primary Entity: contact


Filtering Attribute: birthdate


Eventing Pipeline Stage of Execution: Pre Stage


Execution Mode: Synchronous



Triggering Pipeline: Child Pipeline





Summary








That’s all there is to it! The users will now be able to use Advanced Find to quickly identify their contacts birthday in a certain time period from now on. For the existing contacts previously stored in CRM, you will need to write a one-time SQL script to update the birthday fields in the MSCRM database in order for CRM to return the correct data back to the users. Hopefully this will help you on your next CRM project.



clip_image004



clip_image006



Cheers,



Darren Liu



Published Friday, February 27, 2009 9:26 AM by crmblog

Thursday, February 12, 2009

Example of Dynamic Entity Retrieval

by Danny Varghese 01.31.09


There have been numerous requests on other blogs about sample code to on how to retrieve entities in CRM. One way is to use the CRM web service to retrieve business entities, however by doing so, you're only limited to out-of-the-box entities with system attributes. To retrieve anything more "dynamic," you'll have to employ other methods.  Please remember that in order to retrieve any record, you must have the proper permissions on that entity.

Below is a code example of how to retrieve a record with an id using dynamic entity retrieve:

public DynamicEntity RetrieveEntity()


{


            //variable initialization     


            TargetRetrieveDynamic target = new TargetRetrieveDynamic();         


            RetrieveRequest retrieveRequest = new RetrieveRequest();            


            RetrieveResponse retrieveResponse = null;                                       


            DynamicEntity entity = null;                                      


 


            target.EntityName = <name of entity here>


            target.EntityId = <id of entity here>


 


            //initialize request parameters


            retrieveRequest.ColumnSet = new AllColumns();


            retrieveRequest.ReturnDynamicEntities = true;


            retrieveRequest.Target = target;


 


            //build the response object


            retrieveResponse = (RetrieveResponse)GetCrmService().Execute(retrieveRequest);


 


            //retrieve the service order item from the response


            entity = (DynamicEntity)retrieveResponse.BusinessEntity;


 


            return entity;


}


 




 



The above example is a simple one, but the example below retrieves all contacts that have an account id = some id, and also retrieve the records with only a certain attributes. This is probably a more robust example encompassing many retrieval options:



 





private ArrayList RetrieveMultipleContacts(ICrmService crmService, Guid parentAccountId)


{


    //variable initialization


    ConditionExpression condition = new ConditionBLOCKED EXPRESSION;


    FilterExpression filter = new FilterBLOCKED EXPRESSION;


    QueryExpression query = new QueryBLOCKED EXPRESSION;


    RetrieveMultipleRequest request = new RetrieveMultipleRequest();


    ColumnSet cols = new ColumnSet();


    RetrieveMultipleResponse response = null;


    ArrayList contacts = new ArrayList();


 


    //Set the condition for retrieval


    condition.AttributeName = "parentcustomerid";


    condition.Operator = ConditionOperator.Equal;


    condition.Values = new string[] { parentAccountId.ToString() };


 


     //Set the properties of the filter.


    filter.FilterOperator = LogicalOperator.And;


    filter.AddCondition(condition);


 


    //Set the attributes needed to be returned.  NOTE: The CRM Sdk has an erroneous example


    //of how to set the attributes for retrieval. 


    cols.Attributes.Add("address1_line1");


    cols.Attributes.Add("address1_line2");


    cols.Attributes.Add("address1_line3");


    cols.Attributes.Add("address1_city");


    cols.Attributes.Add("address1_stateorprovince");


    cols.Attributes.Add("address1_postalcode");


    cols.Attributes.Add("address1_country");


    cols.Attributes.Add("telephone1");


    cols.Attributes.Add("fax");


 


    //Set the properties of the QueryExpression object.


    query.EntityName = EntityName.contact.ToString();


    query.ColumnSet = cols;


    query.Criteria = filter;


 


    //Set the query for the request and set the flag to return


    //dynamic entities


    request.Query = query;


 


    //retrieve the contacts


    response = (RetrieveMultipleResponse)crmService.Execute(request);


    foreach (BusinessEntity cont in response.BusinessEntityCollection.BusinessEntities)


    {


        contacts.Add(cont);


    }


 


    return contacts;


}



I hope these examples help someone, happy coding!

Wednesday, October 15, 2008

Microsoft CRM 4.0.7 SDK Update Released

Another great update to the Microsoft CRM 4.0 SDK. This update was actually released on October 1st, 2008:

http://www.microsoft.com/downloads/details.aspx?FamilyID=82E632A7-FAF9-41E0-8EC1-A2662AAE9DFB&displaylang=en

Tuesday, October 7, 2008

CRM SDK Update - 4.0.6

There's been a new release of the CRM 4.0 SDK - available here http://www.microsoft.com/downloads/details.aspx?FamilyId=82E632A7-FAF9-41E0-8EC1-A2662AAE9DFB&displaylang=en

There are no major changes in this release, but there are a few aspects of note:
  1. The Plug-in example code now uses the DynamicEntity class as recommended
  2. The code for use of ImportXml, ExportXml and PublishXml looks like it now passes all required XML nodes
  3. There are now instructions for setting up a web reference in Visual Studio 2008
  4. The PrependOrgName client-side function is now documented (and hence supported)

Plug-ins - differences between Target and Image Entity

Posted by David Jennaway


In a plug-in there are potentially several ways to access entity data relevant to the plug-in action. For example, on the create message you can access the data on the new entity instance in one of the following ways:
  1. Via the Target InputParameter
  2. Via an Image Entity registered on the step
  3. Via a Retrieve request in the plug-in code

These do not always work in the same way, as follows:

Availability of the data by stage

The general rules are:

  1. InputParameter is available in all stages. It can be modified in the pre-stage, but changing it in the post-stage will have no effect
  2. A PostImage Entity is available in the post-stage, and a PreImage Entity in the pre-stage only
  3. If using a Retrieve in the plug-in, then the data returned depends on the stage. In the pre-stage, you will see the data before the modification, whereas in the post-stage you see the data after the modification
  4. Some Image Entities are not relevant for some messages - e.g. there is no PreImage for a Create message, and no PostImage for a Delete message

Data in the Name attribute

If the message is updating CRM (e.g. a Create or Update message) then the InputParameter only contains the minimum information that needs to be saved to CRM. A consequence of this is that the name attribute of any of the following data types is null:

  • Lookup
  • Owner
  • Customer
  • Picklist
  • Boolean

So, if your code needs to access the name, then you cannot rely on the InputParameter, and have to use either the Image Entity or a Retrieve to get the data.

My preference is to use an Image Entity, mostly as this reduces the code I have to write. The CRM SDK also suggests that this is more efficient, though I've not done any thorough performance testing on this to determine if this is relevant.

Blog Move: Speed Racer - Call CRM at speeds that would impress even Trixie

posted at: 9:37 AM by Aaron Elder


It's been a year since Invoke Systems and Ascentium merged and we finally took down the old clunk server that was hosting the old Invoke Systems blog.  This blog is of course still getting lots of hits and due to popular demand, I am going to migrate a few choice posts on our current blog.  Note these posts are all related to Microsoft CRM 3.0.  Here is the first.

The other day I was doing a bit of testing to find the fastest way to make rapid calls to CRM Web Services.  The truth of the matter is that when you are calling CRM Web Services you are pretty much down to the metal; so the only places I found to make optimizations were at the .NET Web Service Request level and at the server level.  The good news is that with a few very easy tweaks you can improve rapid CRM calls by almost 50%!

The results are based on a test that involved performing 250 Account create operations in a single-threaded clean CRM 3.0 RTM environment.  Please note that this article is about improving performance for operations such as data imports and bulk operations.  The same user and CRM Service are re-used for all 250 calls.  Be warned that not all settings are "safe" to use in all scenarios... please read up on the suggestions prior to implementing them in a production system.

The results of this study are as follows.

Test

Results

Raw Dog

15402.1472

PreAuthenticate

14450.7792

PreAuthenticate & Unsafe

12638.1728

Just Unsafe

9633.8528

Unsafe + IIS Tweaks

8862.744

[IMAGE MISSING]

So what does all this mean?  The answers are below.  For simplicity of the code samples, I assume you have already declared and setup a CrmService, the credentials are set and the URL is configured.  I also assume there is a CRM Account called "acc" that is ready to go.  Something like this:

CrmService crm = new CrmService();
crm.Credentials = System.Net.CredentialCache.DefaultCredentials;
crm.Url = "
http://localhost/MSCRMServices/2006/CrmService.asmx";

account acc = new account();
acc.name = "Test";

"Raw Dog" - This is the most straightforward and basic way of calling the CRM service, the code looks something like this:

crm.Create(acc);

PreAuthenticate - This is the first optimization that people seem to use and it does indeed provide a small benefit (~7%) over the default settings.  The code looks like this:

crm.PreAuthenticate = true;
crm.Create(acc);

So why does this work?  Reading the documentation suggests that this simply saves a round-trip required by NTLM's challenge-response authentication system.  MSDN: "With the exception of the first request, the PreAuthenticate property indicates whether to send authentication information with subsequent requests without waiting to be challenged by the server. When PreAuthenticate is false, the WebRequest waits for an authentication challenge before sending authentication information." - Of course since most systems have "Keep Alives" enabled and my scenario is using the same connection over and over, the savings are minimal.

PreAuthenticate & Unsafe - This attempt adds the "UnsafeAuthenticatedConnectionSharing" option to the mix and we get yet another boost in performance (~12% over our last test and ~18% for our first test).  The code looks like this:

crm.PreAuthenticate = true;
crm.UnsafeAuthenticatedConnectionSharing = true;
crm.Create(acc);

So why does this help?  When used in conjunction with "Keep Alives" this option keeps an authenticated connection open to the server.  MSDN: "The default value for this property is false, which causes the current connection to be closed after a request is completed. Your application must go through the authentication sequence every time it issues a new request.  If this property is set to true, the connection used to retrieve the response remains open after the authentication has been performed. In this case, other requests that have this property set to true may use the connection without re-authenticating. In other words, if a connection has been authenticated for user A, user B may reuse A's connection; user B's request is fulfilled based on the credentials of user A."

This option is very powerful and before using it be sure to read up on it here.  Since the connection is authenticated and shared, you need to make sure that two different users don't come in on the same connection.  If they do, the server will thing the user is the first user that opened the connection and allow the 2nd user to do whatever the 1st user could and to do it as if they were the same person.  There is a way around this using the property "ConnectionGroupName" property.  For my scenario of a bulk import, the only user that will be using this connection is the migration user and it will be the same user from start to end, so we are ok.

Unsafe - Now something curious happens when we keep PreAuthenticate off, but leave UnsafeAuthenticatedConnectionSharing on.  This is where things get a bit odd, this is faster than have both options on; a full 38% faster than the original test as a matter of fact!  The code looks like this:

crm.UnsafeAuthenticatedConnectionSharing = true;
crm.Create(acc);

Why does this work?  Well the answer is I don't know and I have talked to people on the CRM team and the .NET team and nobody has a really good answer.  The good new is that it does, perhaps it is best to leave it at that.

Tweaks - Finally, if you follow all the recommended steps for configuring a high-performance ASP.NET application (disable logging, enable ISAPI caching, make sure ASP.Net is tweaked) you can get a final little nip of performance.  Basically do what this article says and we get another 9% bump in performance.

 

Disclaimer:
This posting is provided "AS IS" with no warranties, and confers no rights.

Saturday, May 31, 2008

Microsoft Dynamics CRM SDK version 4.0.5!

Check out the latest SDK update, version 4.0.5! If you’ve been waiting for sample code in VB .NET, then download the SDK. We’ve converted over 100 samples to VB .NET.

Here’s a list of changes:

  • Added 64-bit versions of the DLLs needed for plug-in and custom workflow activity development.
  • New sample code for accessing the CRM Web services from JScript including:
    • How to use the GenerateAuthenticationHeader function to simplify your JScript code.
    • Samples showing how to call each CrmService Web service method using JScript.
    • How to retrieve data from related records
  • New walkthrough that shows how you can use a Web Debugging proxy to capture SOAP packets for Microsoft Dynamics CRM Web service calls from a console application.
  • New sample showing how to add a new Web page to the navigation pane.
  • New info in the Report Writers Guide:
    • Linking related reports
    • Managing reports in offline mode
    • Formatting values in reports
    • Categorizing and displaying reports in different languages
  • New message for Microsoft Dynamics CRM Online: RetrieveOrganizationResources. This message retrieves statistics about the resources used for an organization.
  • New custom workflow activity samples:
    • Return a calculated value
    • Create a task in a custom workflow activity
    • Return the next birthday for an account or contact
  • New sample code for these messages:
  • New topic on how to export customized entity and attribute text for translation.
  • New topic and sample code that contains information about using the Microsoft Dynamics CRM for Outlook SDK.
  • Plug-ins:
    • Added information and sample code showing how to instantiate the Web services in code executing within a child pipeline.
    • Updated upgrade information for Microsoft Dynamics CRM 3.0 callouts.

Availability:

The download will be posted mid-day today. The dev center online version should be live within a week.

 

Amy Langlois

Microsoft Dynamics CRM Developer Center