Tuesday, May 22, 2012

CRM 2011 Developer Extensions

Overview


CRM Developer Extensions provide an additional abstracted method of accessing the underlying CRM Organization Service providing:

  • A simpler method in which a developer can connect to the Organization Service through a CrmConnection object.
  • Custom extensions and context configurability through the CrmConfigurationManager.
  • The ability to cache service results using the CachedOrganizationService to increase performance.

At it's most primative, for an application to connect to MS CRM 2011 using the developer extensions all that is required is a connection string, a CrmConnection instance and an OrganizationServiceContext which can be either generated using CrmSvcUtil.exe or programatically using the CrmConfigurationManager.  


All related Crm Developer Extension objects live in the Microsoft.Xrm.Client namespace.



CrmSvcUtil.exe for Developer Extensions

CrmSvcUtil.exe for Developer Extensions offers the following main benefits:

  • Generates statically typed entity classes for ease of use in your code.
  • Generates many-to-many relationship classes to allow you to manage more complex relationships in your CRM Organization.
  • Generates class names and property names based on Microsoft Dynamics CRM schema names.
  • Generates a WCF Services compatible Data Context supporting OData

The data context class is generated by CrmSvcUtil.exe using the /codecustomization and /servicecontextname parameters:

CrmSvcUtil.exe /codeCustomization:"Microsoft.Xrm.Client.CodeGeneration.CodeCustomization,Microsoft.Xrm.Client.CodeGeneration"
/url:https://crm-org-name.crm.dynamics.com/org-id /username:user-wlid-email /password:user-crm-pwd
/deviceid:user-defined-deviceid /devicepassword:user-defined-devicepwd" /out:"Xrm.cs" /namespace:Xrm

/serviceontextname: crmOrgServiceContext


Note: If no servicecontextname parameter is specified then a Data Context is not generated by CrmSvcUtil.exe


CrmConnection


At it's simplest the CrmConnection class is a wrapper that provides all of  the items necessary for accessing and authenticating against the OrganizationService. To access the OrganizationService, CrmConnection provides two public constructor overloads allowing us to specify the connection settings for our CRM Organization as either a string which represents the name of the configuration stored in the web or app.config file, or as a CrmConnectionStringSettings object (System.Configuration namespace).


Bill Ryan's blog provides an excellent dive under the covers of the CrmConnection class.

In addition, CrmConnection provides a static Parse method for parsing a string URL and instantiating a CrmConnection instance:


CrmConnection Constructors

public CrmConnection(string connectionStringName);
public CrmConnection(string CrmConnectionStringSettings connectionString);
public static CrmConnection Parse (string connectionString)


Sample Connection String defined in a configuration file.

<connectionStrings>
<add name="crm" connectionString="Url=http://my.server.com/MyOrganizationName;"/>
</connectionStrings>



Use the connection string from the configuration file named "crm"

var connection = new CrmConnection("crm");


Use the ConnectionStringSettings object

ConnectionStringSettings connectionSetting = ConfigurationManager.ConnectionStrings["crm"];
var connection = new CrmConnection(connectionSetting );


Use the CrmConnection.Parse method

var connection = CrmConnection.Parse("Url=http://my.server.com/MyOrganizationName; Domain=MyDomain; Username=crm; Password=monkey;");



OrganizationServiceContext for Developer Extensions


OrganizationServiceContext can be configured and instantiated in several ways. Crm Developer Extensions provide the ability to quickly define a CrmConnection, use the CrmConnection to create an OrganizationService instance from which we can generate an OrganizationServiceContext.

In addition, Crm Developer Extensions allows you to define and configure the context in a configuration file and surface this configuration to your application using the CrmConfigurationManager class.  This section will briefly look at both of these implmentations.  Additional details about the OrganizationServiceContext class can be found in my previous post here.


Instantiate OrganizationServiceContext with a CrmConnection

var service = new OrganizationService(connection);
var context = new CrmOrgServiceContext(service);



Note: CrmOrganizationServiceContext is the developer specified name for the OrganizationServiceContext which was specified as part of the /serviceContextName parameter when CrmSvcUtil.exe was used to generate the Data Context.


Configure OrganizationServiceContext in a Configuration File

The CrmConfigurationManager wraps the underlying .Net ConfigurationManager class to provide additional functionality for defining connection settings in a configuration file and using them for creating an OrganizationServiceContext instance in your applicaiton. At a high level the CreateContext method takes the name of the context configuration element that defines the settings for the data context as shown below:


var contextName = "Xrm";
using (var context = CrmConfigurationManager.CreateContext(contextName) as CrmOrgServiceContext)
{
}




Microsoft has a great link here which demonstrates how to fully configure a Data Context via the app / web.config file.

Monday, May 21, 2012

OrganizationServiceContext Overview

Overview




The OrganizationServiceContext class provides an alternative method of connecting to the underlying MS CRM Organization Service, providng the ability to easily track CRM objects and their related object, perform actions on those objects and commit changes back into the CRM database. The following link provides a list of all supported methods of OrganizationServiceContext



OrganizationServiceContext can be generated using the CrmSvcutil.exe code generation tool located in the <deployed location>SDK\bin folder of the CRM SDK. The syntax for generating the OrganizationServiceContext is outlined below:



CrmSvcUtil.exe /url:http:////XRMServices/2011/Organization.svc /out:.cs /username: /password: /domain:/namespace: /serviceContextName


Under the covers OrganizationServiceContext wraps the OrganizationServiceProxy class and uses an IOrgnaizationService as it's underlying connection. In the sample below we have generated an OrganizationServiceContext called OrgServiceContext which is ready for use.

Note: The name of your OrgnanizationSerivceContext object may be different based on what name you specified in the /serviceContextName parameter during the creation using CrmSvcutil.exe:



//Generate the Serivce Proxt _serivceProxy
...

//Generate IOrgnaizationService from the _serviceProxy
IOrganizationService _service = (IOrganizationService)_serviceProxy;

//Generate OrganizationServiceContext from the IOrganizationService
OrgServiceContext _orgContext = new OrgServiceContext(_service);



You can use both Early and Late Bound Types with the OrganizationServiceContext. To use Early Bound Types you have to enable Proxy Types on the IOrganizationServiceProxy prior to the OrganizationServiceContext being created:



// Enable Proxy Types for Early Bound classes
_serviceProxy.EnableProxyTypes();

// Create a new contact and commit to CRM using _orgContext.AddObject.
var contact = new Contact()
{
    FirstName = "Joe",
    LastName = "Bloggs",
    Address1_Line1 = "116 Joe Bloggs St.",
    Address1_City = "Leeds",
    Address1_StateOrProvince = "North Yorkshire",
    Address1_PostalCode = "99999",
    Telephone1 = "0111222332",
    EMailAddress1 = "joe.bloggs@joebloggs.com",
    Id = Guid.NewGuid()
};

_contactId = contact.Id;
_orgContext.AddObject(contact);



The following provides an example of how to use Late Bound Types with the generated OrganizationServiceContext:



// Create an organization service context object
OrgServiceContext _orgContext = new OrgServiceContext(_serviceProxy);

// Instantiate an account object using the Entity class.
Entity sampleAccount= new Entity("account");

sampleAccount["name"] = "Sample account 1";
testaccount["emailaddress1"] = sampleAccount@samplecompany.com;

// Save the entity using the organization service context object.
_orgContext.AddToAccountSet(sampleAccount);
context.SaveChanges();


LINQ Queries


OrganizationServiceContext implementes the IQueryable interface allowing .Net LINQ queries to be written and executed against CRM data via the OrganizationServiceContext. The code sample below assumes an OrganizationServiceContext object has been instantiated and retrieves a list of all accounts with a name beggining with the word "Sample"


      
var accounts = (from a in _orgContext.CreateQuery("account")
where ((string)a["name"]).StartsWith("Sample")
select new
{
   Id = (Guid)a["accountid"],
   Name = (string)a["name"]
}).ToList();




The CreateQuery method takes either an Entity type name or a String representing the entity logical name and returns a collection of IQueryable. The following link provides a good overview of how to construct LINQ queries using the OrganizationServiceContext.

Tuesday, May 15, 2012

IOrganizationService Overview

Overview


The IOrganizationService is the main Web Service that provides methods for accessing and manipulating data in your Organization.

There are two main ways to consume the IOrganizationService:

  • Via the Microsoft.Xrm.Sdk and Microsoft.Xrm.Sdk.Proxy dll's 
  • Referencing the Organization WCF Service directly through it's Service Reference.

Microsoft.Xrm.Sdk dll's


The Microsoft.Xrm.Sdk dll's are the main and recommended method for interacting with the IOrganizationService. To do this  Visual Studio add references to the following assemblies located in the CRM SDK <installdir>/SDK/bin folder:

In addition you will need to add the following .Net assemblies:
  • System.Runtime.Serialization.dll
  • System.ServiceModel.dll

Once added to your Visual Studio project you're ready to start developing code to communicate with CRM via the IOrganizationService.


Instantiating IOrganizationService


Via Microsft.Crm.SDK

When invoking the IOrganizationService via the Microsoft.Xrm.Sdk dll's the IOrganizationService is generated by an OrganizationServiceProxy instance.  The OrganizationProxy is responsible for abstracting the communication with the underlying SOAP WCF service as shown below:


using( OrganizationServiceProxy _Service = new OrganizationServiceProxy(OrganizationUri
                                                                       , null
                                                                       , credentials
                                                                       , null))
{   
   IOrganizationService _iOrganizationService = (IOrganizationService)_Service;
}


In the code sample above an OrganizationServiceProxy is wrapped in a using statement to ensure that it is properly disposed of as it implements IDisposable(). The OrganizationServiceProxy constructor has 5 overloads accepting parameters for defining Federation Server URI's and Device Id's for Windows Live Single Sign On. These will be dicussed in detail in a different post and are set to NULL for the sample code above.  


Integrated Windows Authentication

To authenticate against the OrganizationService using Integrated Authentication a new instance of ClientCredentials should be instantiated and assigned the DefaultNetworkCredentials from the CredentialCache which resides in the System.Net assembly.

ClientCredentials credentials = new ClientCredentials();
credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
OrganizationServiceProxy _Service = new OrganizationServiceProxy(OrganizationUri, null, credentials, null);


Active Directory Authentication

To authenticate using Active Directory Authentication add a reference to the Microsoft.Xrm.Sdk.Client assembly and declare a new instance of AuthenticationCredentials and instantiate the ClientCredentials.Windows.ClientCredential property as follows:

AuthenticationCredentials creds = new AuthenticationCredentials();
creds.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(_userName 
                                                                                    , _password
                                                                                    , domain);
OrganizationServiceProxy _Service = new OrganizationServiceProxy(OrganizationUri
                                                                 ,null
                                                                 ,creds.ClientCredentials.ClientCredentials
                                                                 ,null);



Windows Live Id (Online)

To authenticate using Windows Live Id you need to register your machine using DeviceRegistration.exe to create a Device Id and Password. DeviceRegistration.exe is located in the SDK\tools\deviceregistration folder of your deployed CRM 2011 SDK.

The following code snippet shows how to assign the device id, username and password to an AuthenticationCredentials instance for instantiating the IOrganizationServiceProxy class with:

authCredentials.ClientCredentials.UserName.UserName = _userName;
authCredentials.ClientCredentials.UserName.Password = _password;
authCredentials.SupportingCredentials = new AuthenticationCredentials();
authCredentials.SupportingCredentials.ClientCredentials = Microsoft.Crm.Services.Utility.DeviceIdManager.LoadOrRegisterDevice();

OrganizationServiceProxy _Service = new OrganizationServiceProxy(OrganizationUri
                                                                 ,null

                                                                 ,creds.ClientCredentials.ClientCredentials
                                                                 , null);


Executing Service Requests

The following demonstrates how to execute a simple service request to retrieve a list of Accounts using a QueryExpression:

private static void ExecuteGetAccounts_QueryExpression(IOrganizationService service)
{


     QueryExpression query = new QueryExpression("account");
     query.ColumnSet = new ColumnSet();
     query.ColumnSet.AllColumns = true;

     EntityCollection accounts = service.RetrieveMultiple(query);
     
     foreach (Entity entity in accounts.Entities)
     {
        Console.WriteLine("Name: {0}", entity["name"]);
     }
}



The following demonstrates how to execute a simple service request to retrieve a list of Accounts using FetchXML:

private static void ExecuteGetAccounts_FetchXML(IOrganizationService service)
{
    Console.WriteLine("Executing Fetch XML Query");

    string fetchAccount = @"
                          <fetch mapping='logical'>
                             <entity name='account'>
                                <attribute name='accountid'/>
                                   <attribute name='name'/>
                                </entity>
                             </fetch> ";


    EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetchAccount));
    foreach (var entity in result.Entities)
    {
       Console.WriteLine("Name: {0}", entity["name"]);
    }
}



Via SOAP

When invoking the IOgranizationService directly via the WCF Service endpoint, an OrganizationServiceClient object is used. The OrganizationServiceClient constructor requires details about the WCF binding which can either be specified programatically or within the Applicaiton or Web.Configuration file of your solution. Daniel Cai's blog details how to programatically configure the CRM WCF Service bindings without a configuration file.


Configuring SOAP Access

The following demonstrates how to instantiate OrganizationServiceClient and return IOrganizationService via the WCF SOAP endpoint.
  • Add the CRM WCF SOAP endpoint to your solution as a Service Reference:
Visual Studio - Add Organization Service Reference


Adding the Service Reference will automatically generate the configuration bindings in your solution app / web.config file:

<system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="CustomBinding_IOrganizationService">
         
             <security defaultAlgorithmSuite="Default" authenticationMode="SspiNegotiated" requireDerivedKeys="true" securityHeaderLayout="Strict" 
            includeTimestamp="true" keyEntropyMode="CombinedEntropy" messageProtectionOrder="SignBeforeEncryptAndEncryptSignature"
            messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
            requireSecurityContextCancellation="true" requireSignatureConfirmation="false">
            <localClientSettings cacheCookies="true" detectReplays="true"
              replayCacheSize="900000" maxClockSkew="00:05:00" maxCookieCachingTime="Infinite"
              replayWindow="00:05:00" sessionKeyRenewalInterval="10:00:00"
              sessionKeyRolloverInterval="00:05:00" reconnectTransportOnFailure="true"
              timestampValidityDuration="00:05:00" cookieRenewalThresholdPercentage="60" />
            <localServiceSettings detectReplays="true" issuedCookieLifetime="10:00:00"
              maxStatefulNegotiations="128" replayCacheSize="900000" maxClockSkew="00:05:00"
              negotiationTimeout="00:01:00" replayWindow="00:05:00" inactivityTimeout="00:02:00"
              sessionKeyRenewalInterval="15:00:00" sessionKeyRolloverInterval="00:05:00"
              reconnectTransportOnFailure="true" maxPendingSessions="128"
              maxCachedCookies="1000" timestampValidityDuration="00:05:00" />
            <secureConversationBootstrap />
          </security>
          <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
            messageVersion="Default" writeEncoding="utf-8">
            <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
              maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          </textMessageEncoding>
          <httpTransport manualAddressing="false" maxBufferPoolSize="524288"
            maxReceivedMessageSize="999999" allowCookies="false" authenticationScheme="Anonymous"
            bypassProxyOnLocal="false" decompressionEnabled="true" hostNameComparisonMode="StrongWildcard"
            keepAliveEnabled="true" maxBufferSize="999999" proxyAuthenticationScheme="Anonymous"
            realm="" transferMode="Buffered" unsafeConnectionNtlmAuthentication="false"
            useDefaultWebProxy="true" />
        </binding>
      </customBinding>
    </bindings>
    <client>
      <endpoint address="
http://<SERVER>/<ORGANIZATION>/XRMServices/2011/Organization.svc"
        binding="customBinding" bindingConfiguration="CustomBinding_IOrganizationService"
        contract="CRMOrgService.IOrganizationService" name="CustomBinding_IOrganizationService">
      </endpoint>
    </client>

</system.serviceModel>

The default authentication scheme is Anonymous as highlighted above. To implement Integrated Authentication change the authenticaitonScheme and proxyAuthenticationScheme nodes to "Negotiate"

Note: The Integrated Authentication attribute setting for authenticationScheme and proxyAuthenticationScheme is not a valid setting for http and https transport bindings. The following defines the permitted HttpTransport and HttpsTransport attributes and elements for a WCF binding.


Executing SOAP Service Requests

One of the main advantages of using the SOAP endpoint over the Microsoft.Xrm.SDK dlls is that it exposes the following additional methods for Asynchronous execution:

  • Begin / End Associate
  • Begin / End Create
  • Begin / End Update
  • Begin / End Delete
  • Begin / End Disassociate
  • Begin / End Execute
  • Begin / End RetrieveMultiple


  • The following demonstrates how to instantiate an OrganizationServiceClient with the binding configuration specified above and execute an asynchronous service request.

    OrganizationServiceClient client = new OrganizationServiceClient("CustomBinding_IOrganizationService"); 

    private static void Begin_AsyncExection(ClientCredentials credentials)
    {
        QueryExpression query = new
    QueryExpression();
        query.EntityName = "account";
        query.ColumnSet = new ColumnSet();
        query.ColumnSet.Columns = new string[] { "name" };
        service.BeginRetrieveMultiple(query, Async_Callback, service);

    }

    private static void Async_Callback(IAsyncResult result)
    {
        EntityCollection response = ((IOrganizationService)result.AsyncState).EndRetrieveMultiple(result);

        foreach (Entity entity in response.Entities)
        {
            for (int i = 0; i < entity.Attributes.Count; i++)
            {
                KeyValuePairOfstringanyType attribute = entity.Attributes[i];

                Console.WriteLine(attribute.value);
            }
        }

    }


    Additional Reading


    The Microsoft Patterns & Practices WCF Security guide goes into great detail the different WCF binding configurations and can be found here.

    Monday, May 14, 2012

    Finding Org Service URL's

    For an on-premise implementation of Dynamics CRM 2011 the following WCF Service endpoints are available:

    Discovery Service Endpoint
    http://{server}/XRMServices/2011/Discovery.svc

    Organization Service Endpoint
    http://{server}/{OrgName}/XRMServices/2011/Organization.svc (SOAP)

    OData Service Endpoint
    http://{server}/{OrgName}/XRMServices/2011/OrganizationData.svc (REST)

    Deployment Service Endpoint
    http://{server}/XRMDeployment/2011/Deployment.svc

    The absolute URI for your on-premise services can be downloaded from the Developer Resources section in MS CRM: Click on Settings -> Customizations -> Developer Resources.

    Developer Resources

    From here you can download the Service URL's for your organization.