Posts with tag: CRM

CRM Fielder

2016-11-18 14:36 

BigImage

 

This is a tool I've been creating over the last few months to simplify my work with Microsoft CRM. It's purpose is to expose and make it extremly quick to reach CRM Metadata such as entity names and  and field types etc.

The program currently has these functions implemented.

  • Search through all entities using logical or display names.
  • Search through all fields using logical or display names.
  • Search through both entities and fields at the same time finding any that match your query.
  • Display detailed information about specific fields
  • Generate enums that are copy paste ready for optionsets (se pictures below for examples).
  • Generate Excel documentation of entire CRM database metadata.
    • This includes all entities, both custom and standard
    • As well as all fields custom or not.
    • It lists all display names and field types.
    • Creates a separate sheet for all optionsets.
  • Lists all users and their guids (copy paste friendly for C#).

 

Read more about it and download here

 


I think most people whom use the generated service context for Microsoft CRM quickly note that it does not use the full fledged entity framework and as such does not support all queries. One of these limitations that I usually hit on is that I cant use contains in a Lambda query.

Ie. This does not work and fails with this error: Invalid 'where' condition. An entity member is invoking an invalid property or method.

private List<Xrm.Account> GetAccountsFromOrgNoList(string[] orgNos)
{
    return serviceContext.Xrm.AccountSet
        .Where(x => orgNos.Contains(x.lgc_Organizationnumber))  // <-- contains is invalid
        .Select( x=> x)
        .ToList();
}

The work around for this is to use Query Expressions. Because they can do this, although not as nicely. The below code does the same as the above code with the added exception of actually working.

private List<Xrm.Account> GetAccountsFromOrgNoList(string[] orgNos)
{
    var qe = new QueryExpression(Xrm.Account.EntityLogicalName);
    qe.ColumnSet = new ColumnSet("accountid", "name", "cst_organizationnumber"); // can use new ColumnSet(true); if you just want all columns instead of specifying specif ones
    qe.Criteria.AddCondition("cst_organizationnumber", ConditionOperator.In, orgNos.Cast<Object>().ToArray());
    qe.Distinct = true;

    var results = serviceContext.Instance.RetrieveMultiple(qe).Entities
        .Select(x => x.ToEntity<Xrm.Account>())
        .ToList();

    return results;
}

This is a piece of code I tend to need every few months but always need a few minutes to find. So I'm just going to put it on here for mine and others future reference.

The code returns a KeyValuePair list of all options in a CRM optionset.

public static List<KeyValuePair<string, int>> GetOptionSets(string entityName, string attributeName, IOrganizationService service)
{
    string AttributeName = attributeName;
    string EntityLogicalName = entityName;
    RetrieveEntityRequest retrieveDetails = new RetrieveEntityRequest
    {
        EntityFilters = EntityFilters.All,
        LogicalName = EntityLogicalName
    };
    RetrieveEntityResponse retrieveEntityResponseObj = (RetrieveEntityResponse)service.Execute(retrieveDetails);
    Microsoft.Xrm.Sdk.Metadata.EntityMetadata metadata = retrieveEntityResponseObj.EntityMetadata;
    Microsoft.Xrm.Sdk.Metadata.PicklistAttributeMetadata picklistMetadata = metadata.Attributes.FirstOrDefault(attribute => String.Equals(attribute.LogicalName, attributeName, StringComparison.OrdinalIgnoreCase)) as Microsoft.Xrm.Sdk.Metadata.PicklistAttributeMetadata;
    Microsoft.Xrm.Sdk.Metadata.OptionSetMetadata options = picklistMetadata.OptionSet;
    var ret = options.Options
        .Where(x => x.Label != null && x.Label.UserLocalizedLabel != null && x.Label.UserLocalizedLabel.Label != null && x.Value.HasValue)
        .Select(x => new KeyValuePair<string, int>(x.Label.UserLocalizedLabel.Label, x.Value.Value))
        .ToList();

    return ret;
}

To use it call it as such:

GetOptionSets(Account.EntityLogicalName, "cst_sttributeWithOptionset", serviceContext.Instance);

I have an Angular project in which I need to interact with a CRM 2016 instance, that is, I need to create a record and open/navigate to it from within Angular.

I dont want to use the javascript SDK files because well they just seem klunky and non angular -ish.. So instead I wanted to do it with just a regular service.. Turns out it was quite easy. The hardest part was figuring out where Microsoft stored the GUID of the created record (spoiler: they stuck it in the header, more details on that here).

First off you need to add a service (or copy paste the CreateRecord method from this service to your own).

myApp.service('dataService', function ($http, $rootScope, $timeout, $q) {
    var pub = {};

    var crmAPIURL = Xrm.Page.context.getClientUrl() + "/api/data/v8.1/";

    pub.CreateRecord = function (entityName, record) {
        var url = crmAPIURL + entityName;
        return $http
        (
            {
                method: "POST",
                url: url,
                data: record,
                dataType: 'json',
                timeout: 15000
        }).then(function (data) { return data; });
    }

    return pub;
});

Now with that service method defined we can call it like this from one of our controllers:

$scope.CreateCompany = function ()
{
    // define the entity record with all the fields you want to set.
    var account = {
        name: "My Test Company",
        address1_line1: "SyntaxWarriors road 1337"
    };

    // call the service defined previously with the data and the name of the entity according to the web api
    // the entity names are listed here: https://your_organization_name.crm4.dynamics.com/api/data/v8.1/
    // in my case I wanted to create an account record which is called "accounts" (yes, the service tries to pluralize all things).
    $dataService.CreateRecord("accounts", account).then(function (data)
    {
        // this will return something like: "https://your_organization_name.crm4.dynamics.com/api/data/v8.1/accounts(59e11f14-4489-e611-80de-c4346bacbdc4)"
        // the service returns the GUID of the created record in the header.. for some unexplained annoying reason I dont know. 
        var entityUrl = data.headers("OData-EntityId");

        // do some regex to get the data in between parentheses.
        var getDataBetweenParenthesesRegex = /\(([^)]+)\)/;
        var matches = getDataBetweenParenthesesRegex.exec(entityUrl);

        // uncomment these if you want to see what you're actually getting back.
        // console.log(data)
        // console.log(matches); the guid matches

        // the second match (1) is the guid without the (). Lets open a new window to that.
        if (matches.length >= 1) {
            Xrm.Utility.openEntityForm("account", matches[1]);
        }
    });
}


I received this error after switching from the CRM 2013 SDK dlls to the new CRM 2015 SDK .Dll’s.

The type or namespace name ‘Xrm’ does not exist in the namespace ‘Microsoft’ (are you missing an assembly reference?)

Which led me to this StackOverflow question; “Can’t access Microsoft.Xrm namespace” which seems very similar but does not solve my problem.

What eventually led me to a solution was a warning listed further down in the list of errors. It seems that Microsoft with the new SDK has switched to .Net 4.5.2 so the whole problem for me was that I needed to install a later version of .Net (minimum 4.5.2) and then switch target frameworks to this.

This is the warning that led me to this solution:

The primary reference “Microsoft.Xrm.Client, Version=5.0.9689.2166, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL” could not be resolved because it was built against the “.NETFramework,Version=v4.5.2″ framework. This is a higher version than the currently targeted framework “.NETFramework,Version=v4.5.1″.

So the solution turned out to be as simple as getting the developper pack of .Net 4.5.2. and switching target frameworks on my project to this in visual studio.

SmallImage