Wednesday, February 24, 2016

Using and mocking OrganizationServiceProxy (part 1)

How to use the OrganizationServiceProxy with Dynamics CRM, and mocking it

This is a two-part blog series about how to use the OrganizationServiceProxy class with MSCRM. I'll demonstrate how to connect using explicit, hard coded credentials as well as using the service credentials. I'll finish up by giving some tips on mocking the OrganizationServiceProxy to simplify unit testing in your projects.
In part 1 we'll look at utilizing the OrganizationServiceProxy and creating some code which allows us to easily and flexibly integrate with MSCRM.

Prerequisites

To follow the steps described in this post you'll need to have a version of Microsoft Visual Studio as well as the Windows Identity Foundation framework added to your operating system.
Visual Studio is available in a free (community) version found here
Windows Identity Foundation can be activated with the methods described here

Using OrganizationServiceProxy

Set up the project

I'm going ahead and creating a new Web Application project in Visual Studio. I'm not worrying about hosting and which functions I'll need, so I'll just set up a simple MVC project with defaults. I'm also going ahead and creating a unit test project at the same time, which will be used to demonstrate mocking a bit later on.


When the project has been created, open up the nuget package manager and search for Microsoft.CrmSdk.CoreAssemblies. Add this package to both the MVC project and the Test project. You can also add it using the package manager console with the following commands:
Install-Package Microsoft.CrmSdk.CoreAssemblies

Next add a new, empty controller to your MVC project named CrmController. In the index method we're gonna start by defining a new OrganizationServiceProxy with values as described:

public class CrmController : Controller
{
    // GET: Crm
    public ActionResult Index()
    {
        var crmUrl = new Uri(@"https://crmviking.crm4.dynamics.com");
        var clientCredentials = new ClientCredentials();
        authCredentials.ClientCredentials.UserName.UserName = "username@domain.com";
        authCredentials.ClientCredentials.userName.Password = "password";

        var service = new OrganizationServiceProxy(uri: crmUrl, homeRealmUri: null, clientCredentials: authCredentials.ClientCredentials, deviceCredentials: null);
     
        return View();
    }

}

With just a few lines of code you've already got a working service context which can be used to send and retrieve from Dynamics CRM. I'll explain the different inputs to create a new organizationserviceproxy:
uri: This is the URL to your Dynamics CRM instance
homeRealmUri: This is the WS-TRUST URL to your secondary ADFS server, for example if you're federating across domains. I'm not using it in my case but it could be applicable in your case.
clientCredentials: This is the user credentials used to authenticate with CRM.
deviceCredentials: This is for when you generate device credentials for your service

Refactoring service generation

Now, the next logical step (to me) is moving this out into it's own class, so we can reuse for our other methods. What we're doing is generating new a new service context based on predefined values, so we'll refactor the into it's own CrmServiceFactory class. At the same time we'll extract the credentials values and put them into our web.config file (how to store and use your credentials is a discussion better left for another post, but out of two evils, I'd rather specify them in the web.config than hard coded in your class).
Add the following lines to your web.config, inside the "Configuration" namespace.
<appSettings>
  <add key="CrmUserName" value="name@domain.com" />
  <add key="CrmPassword" value="password" />
</appSettings>
<connectionStrings>
  <add name="CrmWebServer" connectionString="https://crmviking.crm4.dynamics.com" />
</connectionStrings>
<configSections>


Refactoring our code into a factory for generating a new OrganizationServiceProxy gives us the following factory-code:

public static OrganizationServiceProxy GetCrmService()
{
    var crmUrl = new Uri(ConfigurationManager.ConnectionStrings["CrmWebServer"].ConnectionString);
    var authCredentials = new AuthenticationCredentials();
    authCredentials.ClientCredentials.UserName.UserName = ConfigurationManager.AppSettings["CrmUserName"];
    authCredentials.ClientCredentials.UserName.Password = ConfigurationManager.AppSettings["CrmPassword"];

    var creds = new AuthenticationCredentials();
    var service = new OrganizationServiceProxy(uri: crmUrl, homeRealmUri: null, clientCredentials: authCredentials.ClientCredentials, deviceCredentials: null);

    return service;
}

Now we can change the implementation in our controller to simply:
var service = CrmServiceFactory.GetCrmService();


Using service credentials

If we want to use service credentials we start by specifying which credentials will be used to run our application. For an MVC application we do that by specifying the user account settings in the IIS Application Pool. More information about setting the service credentials in IIS is described here (technet).
Next we need to change our code implementation as follows:

public static OrganizationServiceProxy GetCrmService()
{
    var crmUrl = new Uri(ConfigurationManager.ConnectionStrings["CrmWebServer"].ConnectionString);
    var authCredentials = new AuthenticationCredentials();
    authCredentials.ClientCredentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;

    var creds = new AuthenticationCredentials();
    var service = new OrganizationServiceProxy(uri: crmUrl, homeRealmUri: null, clientCredentials: authCredentials.ClientCredentials, deviceCredentials: null);

    return service;
}

As you can see, what we've changed is replacing the explicit declaration of the username and password and converted to using the credentials that our application is running with.
This way we won't be relying on hard coded values, and we don't risk "giving away" our credentials if somebody snatches up your source code.

Using the organizationserviceproxy

First of, technet has a lot of information and examples on how to use the CRM components, and I highly recommend that you spend a fair amount of time reading up on them. There's a lot more to coding against CRM than using classes in .Net. Here's the url to the OrganizationServiceProxy

Implementing a create method

OK. We'll just create a super simple class which will create an account. We'll name it AccountRepository.

public void Create()
{
    var service = CrmServiceFactory.GetCrmService();
    var account = new Entity(entityName: "account");
    account.Attributes["name"] = "Contoso";
    service.Create(account);
}

That was simple, good to go right? Not quite, if I left it at that the Marvelous Hosk would throw harsh words my way. We have some basic principles we should adhere to, mainly Dependency Injection. We'll modify our code to take in the service in the default constructor, and we'll take the name used to create the account as input for the "Create" method.

private readonly OrganizationServiceProxy service;
public AccountRepository(OrganizationServiceProxy service)
{
    this.service = service;
}
public Guid Create(string name)
{
    var account = new Entity(entityName: "account");
    account.Attributes["name"] = name;
    var accountId = service.Create(account);

    return accountId;
}


OK, that's a bit better, we can reuse the class in different projects without having to rewrite any logic, and we can create accounts with different names as well. In addition, we're returning the unique identity (Guid) of your newly created account, which is useful in a number of different scenarios.

Implementing a retrieve method

Implementing a retrieve method is really simple. We'll just add a method to our existing class as follows:

public Entity Retrieve(Guid id)

{

    var account = service.Retrieve("account", id, new ColumnSet(true));

    return account;

}

That's easy and self explanatory, but unfortunately it requires us to know the account id of the account we're retrieving, and I for one do not go around remembering the Guid of my accounts.
So what we'll do is that we'll change this implementation to querying CRM for an account based on the account name, because that's a value we'll remember. The only thing is, when we do a query we'll retrieve a list of entities. Querying for the account name will potentially give us multiple accounts as a result, so I think we'll go ahead and create a new method, named RetrieveByName.

public EntityCollection RetrieveByName(string name)
{
    var query = new QueryExpression("account");
    query.ColumnSet = new ColumnSet(true);
    query.Criteria.AddCondition(new ConditionExpression("name", ConditionOperator.Equal, name));

    var accounts = service.RetrieveMultiple(query);
    return accounts;
}

Now we're retrieving a collection of entities, if we wanted we could also just return a generic list of entities, but I would rather do that elsewhere in my code than implement logic here which makes the method more rigid and less reusable.

Implementing an update method

As you might expect, updating entities aren't much harder. I'll jump straight into implementing an Update method which an entity. It takes an Entity as input, which means we'll be doing the main manipulation in other classes. This might seem redundant in our example, because we're not doing anything that we couldn't do by just calling the OrganizationServiceProxy's Update method. For most deployments that's probably all you need as well, but for some scenarios you might want to do additional, mandatory manipulation every time an update is performed. You might want to whenever it's called, or you might want to implement a date field which is supposed to be updated whenever an update occurs. Additionally, I like to handle all my organization queries inside my repositories.

public void Update(Entity entity)
{
    service.Update(entity);
}

Easy peasy.

Implementing a status update method

Updating the status of a record is a bit special for Dynamics CRM. Instead of simply updating the state and status using the update method you have to send a SetStateRequest.
Here's the code we'll implement.

public void UpdateStatus(Guid id, int state, int status)
{
    var stateRequest = new SetStateRequest();
    stateRequest.EntityMoniker = new EntityReference("account", id);
    stateRequest.State = new OptionSetValue(state);
    stateRequest.Status = new OptionSetValue(status);

    service.Execute(stateRequest);
}

There's no magic in this code either, but as you might notice it is quite generic. We're already taking in the entity id, the state value and the status value. The only parameter missing is the entity logical name and we could reuse it across all entities, and that's exactly what we'll do. A point I want to make is that we'll be passing in four parameters, and to stay in Uncle Bob's good graces we'll create a model to pass as the input instead.

First off, here's our model

public class CrmStatusModel
{
    public Guid Id { get; set; }
    public string EntityName { get; set; }
    public int StateValue { get; set; }
    public int StatusValue { get; set; }
}


Next, it's our new, generic status update class. I went ahead and named it CrmStatusHandler. Like our repository, I'm going to pass in an organizationserviceproxy in the default constructor.

private readonly OrganizationServiceProxy service;
public CrmStatusHandler(OrganizationServiceProxy service)
{
    this.service = service;
}
public void UpdateStatus(CrmStatusModel model)
{
    var stateRequest = new SetStateRequest();
    stateRequest.EntityMoniker = new EntityReference(model.EntityName, model.Id);
    stateRequest.State = new OptionSetValue(model.StateValue);
    stateRequest.Status = new OptionSetValue(model.StatusValue);

    service.Execute(stateRequest);
}


Now we can use this handler to update the status for all our entities, and we've also got a model instead of four separate parameters.

Create additional entity repositories

Now we've seen how to implement a repository for the account entity. I'm gonna go ahead and create another repository for the contact entity. I'll implement the same methods as we did in the account repository, with the same input parameters, except for the query by name.

private OrganizationServiceProxy service;

public ContactRepository(OrganizationServiceProxy service)
{
    this.service = service;
}
public Guid Create(string name)
{
    var contact = new Entity("contact");
    contact.Attributes["name"] = name;
    var contactId = service.Create(contact);

    return contactId;
}

public Entity Retrieve(Guid id)
{
    var contact = service.Retrieve("contact", id, new ColumnSet(true));
    return contact;
}

public void Update(Entity entity)
{
    service.Update(entity);
}


As you can see, it's pretty much the same as the account, only for the contact entity. In addition, I'll create two methods for querying by values instead. I'll create one method for querying by first name, and one method for querying by last name.

public EntityCollection RetrieveByFirstName(string name)
{
    var query = new QueryExpression("contact");
    query.ColumnSet = new ColumnSet(true);
    query.Criteria.AddCondition(new ConditionExpression("firstname"ConditionOperator.Equal, name));

    var contacts = service.RetrieveMultiple(query);
    return contacts;
}

public EntityCollection RetrieveByLastName(string name)
{
    var query = new QueryExpression("contact");
    query.ColumnSet = new ColumnSet(true);
    query.Criteria.AddCondition(new ConditionExpression("lastname"ConditionOperator.Equal, name));

    var contacts = service.RetrieveMultiple(query);
    return contacts;
}

Create an adapter

Lastly, we'll create an adapter to utilize our repositories. I'm going to simulate a situation where we'll always create a contact whenever an account is created, and we'll create a method to deactivate a company when a contact is deactivated. These aren't necessarily methods you'd want to implement in an actual useful environment, but it's a good example of where you'd want to utilize an adapter pattern to combine the usage of several repositories.

public void CreateCustomers(string accountName, string contactName)
{
    var service = CrmServiceFactory.GetCrmService();
    var accountRepository = new AccountRepository(service);
    var contactRepository = new ContactRepository(service);

    var accountId = accountRepository.Create(accountName);
    var contactId = contactRepository.Create(contactName);


    var contactId = contactRepository.Retrieve(contactId);
    contact.Attributes["parentcustomer"] = new EntityReference("account", accountid);
    contactRepository.Update(contact);
}

public void DeactivateCustomers(Guid contactId)
{
    var service = CrmServiceFactory.GetCrmService();
    var accountRepository = new AccountRepository(service);
    var contactRepository = new ContactRepository(service);
    var statusHandler = new CrmStatusHandler(service);

    var contact = contactRepository.Retrieve(contactId);
    var accountReference = (EntityReference)contact.Attributes["parentcustomer"];

    var contactStatus = new CrmStatusModel()
    {
        EntityName = "contact",
        Id = contactId,
        StateValue = 1,
        StatusValue = 2
    };
    var accountStatus = new CrmStatusModel()
    {
        EntityName = "account",
        Id = accountReference.Id,
        StateValue = 1,
        StatusValue = 2
    };

    statusHandler.UpdateStatus(contactStatus);
    statusHandler.UpdateStatus(accountStatus);
}

The first thing you might notice is that these two methods have some redundant code. That already gives you an inclination that there potential for refactoring and improvement. There's some immediate changes we could make, mainly the number of instantiated classes and yet again breaking the dependency injection rules. The first thing we'll do to reduce the redundancy and get better DI is that we'll add the OrganiationServiceProxy as an input for the public constructor for our adapter. Then, in the public constructor we'll set up our repositories as private readonly properties, so they're available to both of our methods inside the adapter. Another thing to note is that the second method also uses the status update handler we created earlier. Creating a new class instance is cheap, especially when we've already got the OrganizationServiceProxy for our adapters, so I'm going to instantiate the status handler in the constructor as well, even though we might not even use it for a particular instance of the adapter class.

private readonly AccountRepository accountRepository;
private readonly ContactRepository contactRepository;
private readonly CrmStatusHandler statusHandler;

public CustomerAdapter(OrganizationServiceProxy service)
{
    accountRepository = new AccountRepository(service);
    contactRepository = new ContactRepository(service);
    statusHandler = new CrmStatusHandler(service);
}


As you can see this hasn't reduced the amount of lines mentionably, but we've got control of the instances at the top of our class declaration, and we can easily change or manipulate them in the future without changing the values inside each method. We'll do some more with our code in the next part, which is Unit Testing our new classes using Moq, so if you've got objections to the changes just made I'd check that out first.

Disposing your objects

Remember that the OrganizationServiceProxy creates new network connections, and you should always call the dispose method when you're done (or instantiate it in a using statement). The network connections aren't part of the CLR, so even in your MVC/web api project where your controllers are instantiated and thrown away in milliseconds the connections will stay open until the idle time out occurs.

Wrap-up

In this part we've looked at how we can utilize the OrganizationServiceProxy to integrate with Microsoft Dynamics CRM. We've created some repositories, a generic status handler and mixed all of our classes into a nice, extensible adapter class. In part 2 we'll look at unit testing these classes, and mocking the OrganizationServiceProxy using Moq. To do that we need to take a look at interfaces, and you'll understand the decisions made in this part even better.


Until then, happy CRM-ing! 

1 comment: