January 27, 2015

New CRM 2015 exams available: MB2-707 Customization & Configuration and MB2-706 Online Deployment

The following CRM 2015 exams are now available:

MB2-706: Microsoft Dynamics CRM Online Deployment
MB2-707: Microsoft Dynamics CRM Customization and Configuration

The exams can be already scheduled and until 31 May 2015 there is also the Second Shot offer, meaning that you can retake the exam for free if you didn't pass the first time.

All the Dynamics CRM 2015 exams are listed here:
Dynamics CRM 2015 Certifications

January 20, 2015

Get the Id of records created using Early Bound OrganizationServiceContext

The other day my friend Raj (@RajYRaman) retweeted a post from Dynamics CRM PFE Team about the best practice to let CRM to choose the Guid of new records instead of using Guid.NewGuid():

http://blogs.msdn.com/b/crminthefield/archive/2015/01/19/the-dangers-of-guid-newguid.aspx

With IOrganizationService object is very easy to get the Id of the new record, because it's the value returned by the Create method independently if late bound or early bound is used:
IOrganizationService service = new OrganizationService(crmConnection);

// late bound
Entity lateAccount = new Entity("account");
lateAccount["name"] = "Late Bound Account";
Guid lateAccountId = service.Create(lateAccount);

// early bound
Account earlyAccount = new Account();
earlyAccount.Name = "Early Bound Account";
Guid earlyAccountId = service.Create(earlyAccount);
This works because Early Bound classes inherit from the Entity class.
public partial class Account : Microsoft.Xrm.Sdk.Entity ...
But how we can get the Id of the records when we use the OrganizationServiceContext in combination with the AddObject and SaveChanges methods?
Using Guid.NewGuid() in order to specify the Id before creating the record was one of my mistakes when I first used Early Bound classes. My badly written code was:
IOrganizationService service = new OrganizationService(crmConnection);
XrmContext context = new XrmContext(service);

// WORST CODE EVER, DON'T DO THIS!
Account earlyAccount = new Account();
Guid badGeneratedId = Guid.NewGuid();
earlyAccount.Id = badGeneratedId;
earlyAccount.Name = "Early Bound Account";
context.AddObject(earlyAccount);
context.SaveChanges();
The reason was to avoid parsing the results of the SaveChanges method in order to get the Id. SaveChanges doesn't return void but a SaveChangesResultCollection object with the details of the save operation.

But checking the results is not necessary, in fact the OrganizationServiceContext updates the tracked records adding the Guid value to the Id property. We just need to read the Id property after the SaveChanges:
Account earlyAccount = new Account();
earlyAccount.Name = "Early Bound Account";
context.AddObject(earlyAccount);
context.SaveChanges();

Guid crmGeneratedId = earlyAccount.Id; 
MSDN Documentation (at least at the time I discover this) isn't so clear explaining this behavior, hope it helps!

January 14, 2015

Retrieve the Saved Views (UserQuery) of all CRM users

Dynamics CRM allows the users to create personal views (using Advanced Find) and eventually share them with other users.

The entity used to store these views is called UserQuery and the main properties are:
  • Name: Name given to the saved view
  • FetchXml: String that specifies the query in Fetch XML language
  • OwnerId: Unique identifier of the user or team who owns the saved view
In order to retrieve the saved views we can use FetchXml or a QueryExpression, but the result will always contain only the saved views of the user executing the query.

Consider the following simplified scenario:
  • CRM has only two users: John (System Administrator role) and Bob (Sales Manager role)
  • John has a personal view for the Account Entity (not shared)
  • Bob has a personal view for the Contact Entity (not shared)
If John executes a query to return all the UserQuery records, the result will contain only his Account personal view, despite his System Administrator role.
What if we want to retrieve all the saved views for all the users? A possibility is to impersonate each CRM user, run the query and combine the results.
To implement this solution we rely on the CallerId property of the OrganizationServiceProxy object in combination with the "Act on Behalf of Another User" privilege.
The OrganizationServiceProxy gives us the possibility to impersonate the user by code, the "Act on Behalf of Another User" privilege is required to allow this impersonation.

The following code is a simplified example of the solution:
// url and credentials
string organizationUrl = "https://mycompany.crm.dynamics.com/XRMServices/2011/Organization.svc";
string userName = "john@mycompany.onmicrosoft.com";
string password = "JohnPassword";

// authentication code
ClientCredentials credentials = new ClientCredentials();
credentials.UserName.UserName = userName;
credentials.UserName.Password = password;
IServiceManagement<IOrganizationService> orgServiceManagement = ServiceConfigurationFactory.CreateManagement(new Uri(organizationUrl));
AuthenticationCredentials authCredentials = new AuthenticationCredentials();
authCredentials.ClientCredentials = credentials;
AuthenticationCredentials tokenCredentials = orgServiceManagement.Authenticate(authCredentials);
SecurityTokenResponse organizationTokenResponse = tokenCredentials.SecurityTokenResponse;

// IOrganizationService and OrganizationServiceProxy objects
OrganizationServiceProxy serviceProxy;
IOrganizationService service;

using (serviceProxy = new OrganizationServiceProxy(orgServiceManagement, organizationTokenResponse))
{
    service = (IOrganizationService)serviceProxy;
    
    // Dictionary to contain all the saved views
    Dictionary<Guid, Entity> dictPersonalViews = new Dictionary<Guid, Entity>();

    // retrieve first all the CRM Users
    QueryExpression systemUsers = new QueryExpression("systemuser");
    systemUsers.ColumnSet = new ColumnSet(true);
    EntityCollection userCollection = service.RetrieveMultiple(systemUsers);


    // for each User we launch the query to retrieve the saved views
    foreach (Entity systemUser in userCollection.Entities)
    {
        QueryExpression personalViews = new QueryExpression("userquery");
        personalViews.ColumnSet = new ColumnSet(true);

        // we set the CallerId property to impersonate the current iteration user
        serviceProxy.CallerId = systemUser.Id;
        EntityCollection viewCollection = serviceProxy.RetrieveMultiple(personalViews);

        foreach (Entity personalView in viewCollection.Entities)
        {
            // we want a list without duplicates (shared views or automatically shared to SYSTEM and INTEGRATION users)
            if (!dictPersonalViews.ContainsKey(personalView.Id))
            {
                dictPersonalViews.Add(personalView.Id, personalView);
            }
        }
    }

    // we can process the values (Entity objects) of the dictionary
    foreach (Entity personalView in dictPersonalViews.Values)
    {
       string viewName = personalView["name"].ToString();
       string viewFetchXml = personalView["fetchxml"].ToString();
       EntityReference viewOwnerIdRef = (EntityReference)personalView["ownerid"];
    }
}

January 2, 2015

I am a Microsoft Dynamics CRM MVP!

Yesterday (1 January 2015) I received the Microsoft MVP Award for my contributions to the Dynamics CRM Community. It is a great honor for me to be part of the MVP family.

https://mvp.microsoft.com/en-us/mvp/Guido%20Preite-5001218

I work with Dynamics CRM since 2010 but my online presence started 2 years ago. The reason to join several sites at the time was a bit selfish: I was in a new country, the first Dynamics CRM project at my new job was for the most part server side related and I was worried to forget the client side customizations (the big dilemma: getAttribute or getControl? :) ). Helping others to fix their JavaScript looked like a good idea to refresh my memory but shortly I realized how helpful and pleasant is to frequent the various communities and engage with their members.

I would like to thank the following MVPs:
Andrii Butenko: few months after I joined MSDN he sent me this email: “Hello Guido! Found you at MS Forums, checked your blog! Keep up good work!”. Everybody knows Andrii and his contributions so this message means a lot to me.

Jukka Niiranen: I met Jukka in person during a very cold winter, but previously he helped me by email when I was searching a job in Finland. At the time he wasn’t yet an MVP but what he did for me is only a small example of how he likes to help other community members.

Scott Durow, Jason Lattimer and Tanguy Touzard: Because I am a developer their tools and contributions helped me very much during these years. In addition, many of my forum answers are “use the tool ____ from Scott/Jason/Tanguy” and this shows how important they are to the Dynamics CRM Community.

Posted on Friday, January 02, 2015 | Categories: ,