Thursday, January 31, 2013

Add a link to an external website on CRM 2011 form

There is a navigation tree on the left side of a CRM 2011 form. This can be modified to allow the current form to be replaced with an existing web resource or external web page.
  • Start customizing the form
  • Click the Navigation button under the select section on the ribbon:



  • Click the Insert tab
  • Now Naviagation Link is enabled. Click it, enter the Name and External URL. (Even though External URL  is disabled, click it and it will become enabled)
  • Click OK.
  • Save and Publish, then open the form. Now, on the left navigation panel you will see your text; clicking it will cause the website to open in the place of your form.

Tuesday, January 29, 2013

Raw WCF ODATA examples for CRM 2011

This returns the WSDL:

http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet(guid'4fadffb7-4a45-e211-9621-005056862bb8')

This returns the entire set
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet

This returns a specific record
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet(guid'4fadffb7-4a45-e211-9621-005056862bb8')

This returns a single field
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet(guid'4fadffb7-4a45-e211-9621-005056862bb8')/fcb_name

This returns a single field value only without metadata:
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet(guid'4fadffb7-4a45-e211-9621-005056862bb8')/fcb_name/$value

This returns an ordered set:
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet?$orderby=fcb_name desc

This gets an ordered set, the top 1 after skipping 2:
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet?$orderby=fcb_name desc&$top=1&$skip=2

This filters by a specific field"
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet?$filter=fcb_name eq 'Category 3'

Compound filter
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet?$filter=fcb_name eq 'Category 3' or fcb_name eq 'Category 2'

This gets specific fields from the table instead of every one:
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/fcb_costbillingservicecategorySet(guid'4eadffb7-4a45-e211-9621-005056862bb8')?$select=fcb_name,ModifiedOn

This gets basic Metadata about the service: (list of attributes, types etc)
http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/OrganizationData.svc/$metadata



Monday, January 28, 2013

Test the Mobile version of forms

You can access  the mobile forms using this syntax:

http://crmdevmt.develop.fcbt:5555/JamesDev/m/ef.aspx?etn=account

server
Org
Mobile
Page
Entity

Wednesday, January 23, 2013

Adding multiple records to CRM2011 using Linq provider

LINQ stands to Language Integrated Query. It provides a consistent interface for querying early bound or late bound queries, all using  standard CLR types.

Review Using early binding (typed WCF classes) in CRM 2011 to review the use of the crmsvcutil.exe code -generator. Note that you have to use the  /serviceContextName:CrmDataContext switch to generate the Service Context, which is the gateway to the Linq provider.

The generated context, in this case CrmDataContext has public queryable entitysets that can be queried.
The following example shows adding multiple records to Account, with one call to CRM:

Note that even though in code only ONE call is made, multiple calls are made to the CRM service for this but it is still more efficient by about 60%.
NOTE: YOU HAVE TO USE THE .NET FRAMEWORK 4 AS THE TARGET FRAMEWORK, AND NOT THE .NET FRAMEWORK 4 CLIENT PROFILE

private void buttonExecuteLinqQuery_Click(object sender, EventArgs e)
{
    //SET UP CREDENTIALS
    Uri OrganizationUri = new Uri(textBoxServerUrl.Text + "/" + textBoxOrg.Text + "/XRMServices/2011/Organization.svc");
    ClientCredentials Credentials = new ClientCredentials();
    Credentials.Windows.ClientCredential = new System.Net.NetworkCredential(textBoxName.Text, textBoxPassword.Text, textBoxDomain.Text);

    //INITIALIZE PIPELINE SERVICE
    OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(OrganizationUri, null, Credentials, null);
    serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
    IOrganizationService service = (IOrganizationService)serviceProxy;

    fcbt.crm.baseline.CrmDataContext context = new fcbt.crm.baseline.CrmDataContext(service);
    context.AddObject(new fcbt.crm.baseline.Account { Name = "joe" });
    context.AddObject(new fcbt.crm.baseline.Account{Name = "pete"});
    context.AddObject(new fcbt.crm.baseline.Account{Name = "mike"});
    context.AddObject(new fcbt.crm.baseline.Account{Name = "harry"});
    context.SaveChanges();
}

Friday, January 18, 2013

Adding/updating records to CRM using dynamic entities

This example shows how to use WCF to add records to an existing CRM entity.
  1. Create a windows application project, and add the following references:

    Review MSDN:
    In order to work with the classes you also need to add references to the following Microsoft Dynamics CRM and .NET assemblies:


    C:\CRM\sdk\bin\microsoft.crm.sdk.proxy.dll
    C:\CRM\sdk\bin\microsoft.xrm.client.dll
    C:\CRM\sdk\bin\microsoft.xrm.portal.dll
    C:\CRM\sdk\bin\microsoft.xrm.portal.files.dll
    C:\CRM\sdk\bin\microsoft.xrm.sdk.dll


    NOTE: YOU HAVE TO USE THE .NET FRAMEWORK 4 AS THE TARGET FRAMEWORK, AND NOT THE .NET FRAMEWORK 4 CLIENT PROFILE

  2. Create a windows form and add the following Usings:

    using System;
    using System.ServiceModel.Description;
    using System.Windows.Forms;
    using Microsoft.Crm.Sdk.Messages;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Client;
    using Microsoft.Xrm.Sdk.Query;
     
  3. Add the following labels and textboxes to the form:
    • Server URL: textBoxServerUrl
    • Org: textBoxOrg
    • Domain: textBoxDomain
    • UserName: textBoxName
    • Password: textBoxPassword
  4. Add another label and textbox, with an Add button:

    • Account name: textboxAccountName
    • buttonAdd: Add account
  5. You should have something that looks like this: 
  6. Embed this code in the click event of the AddNewRecord button:
    //SET UP CREDENTIALS
    Uri OrganizationUri = new Uri(textBoxServerUrl.Text + "/" + textBoxOrg.Text + "/XRMServices/2011/Organization.svc");
    ClientCredentials Credentials = new ClientCredentials();
    Credentials.Windows.ClientCredential = new System.Net.NetworkCredential(textBoxName.Text, textBoxPassword.Text, textBoxDomain.Text);

    //INITIALIZE PIPELINE SERVICE
    OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(OrganizationUri, null, Credentials, null);
    serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
    IOrganizationService service = (IOrganizationService)serviceProxy;

    Entity a = new Entity("account");
    a["name"] = textBoxAccountName.Text;
    service.Create(a); 
  7.  Run the code, and fill in a value in the account name, then click the Add new record button. Go to CRM and refresh the account list, there will be your new account.

The following code UPDATES records on CRM:

using System;
using System.ServiceModel.Description;
using Applications.LoanAccounting.BillsAndStatements.Properties;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace Applications.LoanAccounting.BillsAndStatements
{
    public class XRMWriter
    {
 
 
        #region Private members
 
        private string _server;
        private string _login;
        private string _password;
        private string _domain;
        private ClientCredentials _credentials;
 
        #endregion
 
 
        #region Constructor
        public XRMWriter()
        {
            try
            {
                _login = (string)Settings.Default["CRMUserLoginId"];
                _password = (string)Settings.Default["CRMUserPassword"];
                _domain = (string)Settings.Default["CRMUserDomainName"];
                _server = (string)Settings.Default["CRMServerUrl"];
 
                _credentials = new ClientCredentials();
                _credentials.Windows.ClientCredential = new System.Net.NetworkCredential(_login, _password, _domain);
 
            }
            catch (Exception ex)
            {
               
                throw;
            }
        }
        #endregion
 
 
        #region General private methods
 
        /// <summary>
        /// Gets a CRM service
        /// </summary>
        /// <param name="org">Name of the Org that writing to</param>
        /// <returns>Instance of IOrganizationService</returns>
        private IOrganizationService GetXrmService(string org)
        {
 
            try
            {
                Uri OrganizationUri = new Uri(String.Format("{0}/{1}/XRMServices/2011/Organization.svc", _server, org));
 
                ////INITIALIZE PIPELINE SERVICE
                using (OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(OrganizationUri, null, _credentials, null))
                {
                    serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
                    return (IOrganizationService)serviceProxy;
                }
 
 
            }
            catch (Exception ex)
            {
               
                throw;
            }
        }
 
        #endregion
 
 
        #region General public methods
        /// <summary>
        /// Updates the specified XRM BillHeader with the name of the PDF document
        /// </summary>
        /// <param name="org">The name of the org that coontains the bill header</param>
        /// <param name="billHeaderId">The unique identifier for this bill header instance</param>
        /// <param name="pdfUrl">The fully qualified location where this PDF got saved.</param>
        public void SaveBillFileNameToXRM(string org, Guid billHeaderId, string pdfUrl)
        {
 
            IOrganizationService xrmService = GetXrmService(org);
 
 
 
            // the ConditionExpression generates a where-clause:
            // (fcbt_billheaderid = billHeaderId)
            ConditionExpression condition = new ConditionExpression();
            condition.AttributeName = "fcbt_billheaderid";
            condition.Operator = ConditionOperator.Equal;
            condition.Values.Add(billHeaderId);
 
 
           
            //Now add the the condition(where-clause) to the filter expression:
            // (WHERE fcbt_billheaderid = billHeaderId)
            FilterExpression filter = new FilterExpression();
            filter.FilterOperator = LogicalOperator.And;
            filter.Conditions.Add(condition);
 
 
            //Now use the filter expression in a query:
            //(SELECT * from [fcbt_billheader] WHERE fcbt_billheaderid = billHeaderId
            QueryExpression query = new QueryExpression();
            query.EntityName = "fcbt_billheader";
 
            ColumnSet columnSet = new ColumnSet();       // new ColumnSet(true) -> will retreive all properties.
            columnSet.Columns.Add("fcbt_pdfurl");
            columnSet.Columns.Add("fcbt_lastrendereddate");
 
            query.ColumnSet = columnSet;
            query.Criteria = filter;
 
 
            EntityCollection entityCollection = xrmService.RetrieveMultiple(query);
 
            Entity BillHeaderEntity = entityCollection[0];
 
            BillHeaderEntity.Attributes["fcbt_pdfurl"] = pdfUrl;
            BillHeaderEntity.Attributes["fcbt_lastrendereddate"] = DateTime.Now;
            xrmService.Update(BillHeaderEntity);
 
        }
 
        #endregion
 
    }
}

Thursday, January 17, 2013

Host another web page as a CRM web resource

CRL2011 allows only web pages of type .html to be stored as a web resource. What happens if you want to add an external web page as a resource?  This article shows how to do that.

Important Note that the internal HTML editor tends to add its own bits and pieces to web resource pages, and sometimes interferes with the META tags. This is a nuisance, so the approach I adopt when this becomes a problem is to import the .htm as an external file when editing my web resources - and not using the internal html "text editor" button.
  1. Create a separate file as the web resource with this content:
    <HEAD>
    <TITLE>HTML Redirect</TITLE>
    </HEAD>
    <BODY onload=window.location='http://microsoft.com'>
    Nothing to see here folks...
    </BODY>
    </HTML>
2. Embed that as a web resource. The magic happens because of the onload redirect...

Using early binding (typed WCF classes) in CRM 2011

Introduction

Strongly typed entities are NOT part of the WSDL - which means that custom entities are not discoverable using WSDL.

The parent type is Entity, and then the instances, i.e. Account, are based on Entity. This means that the any proxies you register are uniform across all organisations. The Entity class has a generic list of attributes, which can contain all the fields and other properties of the original CRM entity. Therefore, you do not have to have a strongly typed "local" class in your client to access the data. An example of this can be seen at this blog: Use WCF to get CRM version and user name.

These are some useful properties and methods of the Entity class:


// You can identify specific named columns using this collection
public AttributeCollection Attributes

//You can get the special metadataformatting originally. For example,
// string s=   account.FormattedValues["creditlimit"] 
// s now = 1,500,000.00 instead of 1500000
public FormattedValueCollection FormattedValues

//The unique identifier for the Entity
public virtual Guid Id

//The friendly name for the Entity, i.e. Account
public string LogicalName

//Names of related entities;
public RelatedEntityCollection RelatedEntities

//Simplifies casting when assigning values
protected virtual void SetAttributeValue(string attributeLogicalName, object value);

//Simplifies casting a retrieved Entity object to a specific CRM Entity
public T ToEntity<T>() where T : Entity;

So if this is the only content of the retrieved WSDL, how do you strongly type retrieved entities? Regular WCF provides the ability to create local proxy classes, by virtue of WSDL containing all the entity classes.

The Code Generator

Source Code Generator Tool generates these entity classes for you. It bases the generation of the code based on the CRM Metadata. It is included in the SDK. Once you have generated these classes, it would be useful to create a separate library for them and reference them across multiple projects for example.

To make use of this library you pass in the assembly when defining the behavior. For example, instead of this:

//INITIALIZE PIPELINE SERVICE
serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());

You would do this:
serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior(MyGeneratedClassLibrary));

Somewhat similar to WCF Odata services, where you pass in the name of the Entity Data Model to the service.

Running the crmsvcutil.exe tool

Note: More information is available on MSDN at Code Generation Using the CrmSvcUtil Tool

You will also need to install Windows Identity Foundation from http://www.microsoft.com/en-us/download/details.aspx?id=17331


On my PC the tool is located at C:\CRM\sdk\bin\crmsvcutil.exe.
Open a command line in this folder and run the following:

CrmSvcUtil.exe /url:http://crmdevmt.develop.fcbt:5555/JamesDev/XRMServices/2011/Organization.svc /out:GeneratedCode.cs /namespace:fcbt.crm.baseline /serviceContextName:CrmDataContext
Where:
  • http://crmdevmt.develop.fcbt:5555 is the Server and Port of the CRM server
  • JamesDev is the name of the organization, and
  • XRMServices/2011/Organization.svc should stay the same for everything.
  • GeneratedCode.cs is the name of the file generated.
  • fcbt.crm.baseline is the name of the namespace I want to use for the generated code
  • CrmDataContext is the name of the service context for Linq. Its optional.

Using the Generated File

Its huge. Yes, a common experience for some. I recommend unloading Devexpress before you open the file in Visual Studio.

Copy the generated file and include it in your Visual Studio project.

Taken from MSDN:
In order to work with the classes you also need to add references to the following Microsoft Dynamics CRM and .NET assemblies:
Add references to these assembliesAssembly location
Microsoft.Crm.Sdk.dll
Microsoft.Crm.SdkTypeProxy.dll
For on-premises or IFD, browse to the SDK\Bin (32-bit) or SDK\Bin\64-bit folder.
For online, browse to the SDK\Bin\Online\64-bit folder. These assemblies work in both 32-bit and 64-bit environments.
Microsoft.Xrm.Client
Microsoft.Xrm.Portal
Microsoft.Xrm.Portal.Files
From the SDK\Microsoft.Xrm\Bin folder.
System.Data.Services
System.Data.Services.Client
From the .NET tab.

  1. Create a windows form and add the following Usings:

    using System;
    using System.ServiceModel.Description;
    using System.Windows.Forms;
    using Microsoft.Crm.Sdk.Messages;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Client;
    using Microsoft.Xrm.Sdk.Query;
     
  2. Add the following labels and textboxes to the form:
    • Server URL: textBoxServerUrl
    • Org: textBoxOrg
    • Domain: textBoxDomain
    • UserName: textBoxName
    • Password: textBoxPassword
  3. Add another label and textbox, with an Add button:
    • Account name: textboxAccountName
    • buttonAdd: Add account
  4. You should have something that looks like this: 
  5. Embed this code in the click event of the AddNewRecord button:
    //SET UP CREDENTIALS
    Uri OrganizationUri = new Uri(textBoxServerUrl.Text + "/" + textBoxOrg.Text + "/XRMServices/2011/Organization.svc");
    ClientCredentials Credentials = new ClientCredentials();
    Credentials.Windows.ClientCredential = new System.Net.NetworkCredential(textBoxName.Text, textBoxPassword.Text, textBoxDomain.Text);

    //INITIALIZE PIPELINE SERVICE
    OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(OrganizationUri, null, Credentials, null);
    serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
    IOrganizationService service = (IOrganizationService)serviceProxy;

    fcbt.crm.baseline.Account a = new fcbt.crm.baseline.Account();
    a.Name = textBoxAccountName.Text;
    a.Telephone1 = "123";
    Guid g = service.Create(a); 



  6.  Run the code, and fill in a value in the account name, then click the Add new record button. Go to CRM and refresh the account list, there will be your new account.



Wednesday, January 16, 2013

Use WCF to get CRM version and user name

The standard interface for interacting programmatically with CRM is to use WCF and the SDK toolkit. The following steps show how to do this. This example was derived from the SDK examples, in this case the solution located on my PC at C:\CRM\sdk\samplecode\cs\quickstart\. My preference is to remove the obfuscating (but useful another day) clutter and get to the root of what we are trying to accomplish.
  1. Download the CRM SDK from here and unzip it to your hard drive, for example at c:\crm\sdk
  2. Create a new Visual Studio windows forms project (.net 4.0).
  3. Add the following 2 references to dlls located at C:\CRM\sdk\bin:
    1. microsoft.crm.sdk.proxy.dll
    2. microsoft.xrm.sdk.dll
  4. Add the following .net references to the project:
    1. System.ServiceModel
    2. System.Runtime.Serialization
  5. Open the windows form in code view and add the following Usings to the top of the page:

    using System;
    using System.ServiceModel.Description;
    using System.Windows.Forms;
    using Microsoft.Crm.Sdk.Messages;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Client;
    using Microsoft.Xrm.Sdk.Query;
  6. Create a windows form with a label, button and text box:
  7. Add the following code to the Click Click event of the button:

  8. Uri OrganizationUri = new Uri(textBoxServerUrl.Text + "/" + textBoxOrg.Text + "/XRMServices/2011/Organization.svc");
    ClientCredentials Credentials = new ClientCredentials();
    Credentials.Windows.ClientCredential = new System.Net.NetworkCredential(textBoxName.Text, textBoxPassword.Text, textBoxDomain.Text);

    //INITIALIZE PIPELINE SERVICE
    OrganizationServiceProxy serviceProxy = new OrganizationServiceProxy(OrganizationUri, null, Credentials, null);
    serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior());
    IOrganizationService service = (IOrganizationService)serviceProxy;

    // Display information about the logged on user.
    Guid userid = ((WhoAmIResponse)service.Execute(new WhoAmIRequest())).UserId;
    Entity systemUser = service.Retrieve("systemuser", userid, new ColumnSet(new string[] { "firstname", "lastname" }));
    MessageBox.Show("Hello " + systemUser.Attributes["firstname"] + " " + systemUser.Attributes["lastname"]);

    // Retrieve the version of Microsoft Dynamics CRM.
    RetrieveVersionRequest versionRequest = new RetrieveVersionRequest();
    RetrieveVersionResponse versionResponse = (RetrieveVersionResponse)service.Execute(versionRequest);
    MessageBox.Show("You are using CRM version number " + versionResponse.Version); 
  9. Note that you will have to rename the controls on the window to match that of the code.

How to Query Xrm page context from an HTML web resourc

Since the webresource is essentially a child of the CRM page, you can access it using Parent:
This is a standard web resource embedded into a CRM form:

<HTML>
  <HEAD>
    <TITLE>HTML Web Resource</TITLE>
  </HEAD>
  <BODY onload="window.status='It\'s a wild ride';" contentEditable=true>
    <SCRIPT type=text/javascript>
      var firstname = parent.window.Xrm.Page.getAttribute('firstname').getValue()
      document.write("Hello "+ firstname);
    </SCRIPT>
  </BODY>
</HTML>