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

 


Today I found out that you don't need a Mac if you want to debug a quirky looking web site on a iOS device.. something I did not think was possible..
But as it turns out this can quite easily be done using a windows 10 machine, a USB cable and an iOS device.

Here are the steps to do it.

Start out by getting these prerequisites: 

  • Install Itunes on your windows 10 Machine (there are drivers that come with it that we want) link.
  • Install Firefox link
  • Download iOS WebKit Debug Proxy Win32 but don't do anything with it yet. Make sure to take the x64 version if you have an x64 OS installed.
  • Have USB cable for you iOS device.

Once you have those things ready, enable debug mode in Safari on your iOS device by going to Settings > Safari > Advanced > Enable: Web Inspector.

SmallImage

Now connect you iOS device using a USB cable and make sure it comes up in ITunes, don't connect it through any type of USB hub (it refused to work through a HUB for me, and searching revealed several others had the same issue). 

Now unzip the iOS WebKit Debug Proxy Win32 into a folder of your choosing and run the .exe file. You might need to go through and unblock ALL the DLL files for it to work properly (open up their properties and click UNBLOCK, before I did this the console app just opened and closed with no error).

SmallImage

When you run the program you should see a console window with the following text. If you get errors here, make sure the device is connected and visible in Itunes and that it is unlocked. You might also want to try the x64 version if you are running the x86 version of the Webkit debug program. If that dosen't work, try disabling your firewall and see if that fixes it.

BigImage

Once you have the console app working, open up Safari on the iOS device and start Firefox. Press Shift + F8 to open up the firefox WebIDE. It should look something like this, you might want to open the Console as well as it might provide som insight into possible errors when connecting (Ctrl + Shift + K with the main firefox window active):

SmallImage

Now you might assume that you should press the Safari Firefox and others button in the WebIDE, and you can try. For me this just threw a bunch of annoying errors in the console. What you should press is Chrome Desktop, which oddly enough just worked right away.

There are a lot of people complaining about this not working here: https://github.com/mozilla/valence/issues/199 but as far as I can tell, as long as you press the Chrome button and not the Safari button it works.

The way this works is that you browse on the device as normal and in the Firefox WebIDE you can inspect the DOM and debug the scripts. When selecting a DOM element it is actually highlighted on the device too and updates and changes you make are reflected on the device as well. The network traffic itself seems to be unavailable though. 

SmallImageSmallImage

 


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);

Here is a little utility class I wrote a in order to convert any C# class to a string and back again. The serialzied string can be saved to disk, or sent over the network to be deserialized some where else. The usage is quite straight forward.

To serialize an object to a string do this:

string serializedData = XMLDataSerializer.SerializeToString(objectToSerialize);

and to deserialize an object to a specific type use: 

string object = DataSerializer.DeSerializeFromString<ObjectType>(serializedData ); 

Here is a unit test demonstrating:

[TestMethod]
    public void SerializeToStringTest()
    {
        var testClass = new TestClass()
        {
            testint = 12345,
            teststring = "SyntaxWarriors XML Serializer"
        };
        var dataString = DataSerializer.SerializeToString(testClass);
        var testClassDeserialized = DataSerializer.DeSerializeFromString<TestClass>(dataString);

        Assert.IsTrue(testClass.testint == testClassDeserialized.testint);
        Assert.IsTrue(testClass.teststring == testClassDeserialized.teststring);
}

This is the value of dataString in the unit test.

<TestClass>
    <testint>12345</testint>
    <teststring>SyntaxWarriors XML Serializer</teststring>
</TestClass>

And here is the class itself:

using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
 
namespace SyntaxWarriors.Serializers
{
    internal static class XMLDataSerializer
    {
        public static string SerializeToString(object obj)
        {
            return SerializeToXml(obj);
        }
 
        public static T DeSerializeFromString<T>(string input)
        {
            T obj = DeserializeFromXml<T>(input);
            return obj;
        }

        private static string SerializeToXml<T>(T obj)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.OmitXmlDeclaration = true;
            settings.NewLineChars = Environment.NewLine;
            settings.NewLineHandling = NewLineHandling.None;
            settings.Encoding = Encoding.UTF8;
            using (var stringWriter = new StringWriter())
            {
                using (var xmlWriter = XmlWriter.Create(stringWriter, settings))
                {
                    var namespaces = new XmlSerializerNamespaces();
                    namespaces.Add(string.Empty, string.Empty);

                    var serializer = new XmlSerializer(obj.GetType());
                    serializer.Serialize(xmlWriter, obj, namespaces);
 
                    var xmlStr = stringWriter.ToString();
                    return xmlStr;
                }
            }
        }
 
        private static T DeserializeFromXml<T>(string xml)
        {
            T result;
            var ser = new System.Xml.Serialization.XmlSerializer(typeof(T));
            using (var tr = new System.IO.StringReader(xml))
            {
                result = (T)ser.Deserialize(tr);
            }
            return result;
        }
    }
}