3

I have C# code running to compare pre and post-images in CRM to determine if a field changed (long story short: external process I can't control is updating every field on a record every time, even if fields haven't changed). I want to use the CRM GetAttributeValue(attributeName) to do it, but I want to do it dynamically when I may not know the field name. So, for example, I want to do this:

// pretend the value of firstname is not hard-coded but submitted on a form    
// (not really on a form, but just know it's not hard-coded like it is below.)
string fieldToCheck = "firstname"; 
if (preImage.GetAttributeValue<T>(fieldToCheck) != postImage.GetAttributeValue<T>(fieldToCheck))
{
  // do something here. I've tried something like the below, but it doesn't work with error "t is a variable but used like a type". 
  Type t = preImage.Attributes[fieldToCheck].GetType();
  var val = preImage.GetAttributeValue<t>(fieldToCheck);

}

The problem I'm having is that <T> could be different depending on the value of fieldToCheck. In the case of firstname it would be <string>, in the case of new_DateOpened it would be <DateTime>, etc. I must be having a brain spasm because I should be able to figure out how to dynamically get the value of T, but can't.

Jeff
  • 611
  • 5
  • 21
  • Since you're using var why don't you just use GetType ? to determine the type of data you have in fieldToCheck? – Aizen Mar 06 '16 at 20:35
  • Modified the code. fieldToCheck will always be a string. It's simply the name of the attribute in CRM that I want to get. – Jeff Mar 06 '16 at 20:50

2 Answers2

1

A generic type parameter T is not equal to an instance of type Type. You can go from type param T -> Type using typeof(T), but not from Type -> T easily. Type T must be known at compile time normally.

You CAN apparently do this with reflection per here (How do I use reflection to call a generic method?):

MethodInfo method = typeof(Entity).GetMethod("GetAttributeValue");
MethodInfo generic = method.MakeGenericMethod(t);
generic.Invoke(preImage, fieldToCheck); // and postImage
Community
  • 1
  • 1
trousyt
  • 360
  • 2
  • 12
  • I support this answer, but for some reason, he is pushing the type to be var as an anonymous type. Don't know why. – Aizen Mar 06 '16 at 20:58
  • You mean when he did `var fieldToCheck = "firstname"`? `var` didn't imply an anonymous type in that instance, it implied an inferred type of string. – trousyt Mar 06 '16 at 21:02
  • yep cause of the " " but check his comment on top. Pretend that the code is not hard coded. – Aizen Mar 06 '16 at 21:08
  • fieldToCheck will always be a string. It represents the name of an attribute on an entity in Microsoft CRM. The attribute that name represents, though, may be of any type: string, date/time, numeric, a CRM option set, etc., which is why I don't know what the type will be. I'm going down this route because an external process that I can't control updates every field even if the values haven't changed. This is causing my CRM workflow to fire because the email field value changed when it really hasn't changed - hence me using the pre & post images to compare. – Jeff Mar 07 '16 at 17:10
  • 1
    Jeff I would use the answer by @Henk-van-Boeijen if that works. I would avoid reflection whenever possible due to the performance implications. – trousyt Mar 07 '16 at 19:09
  • That was my inclination, but I do have a follow-up question on his answer. – Jeff Mar 07 '16 at 22:57
1

For most (if not all) attribute types you can rely on the common Equals(object o) method. This also works for the attributes based on classes EntityReference, OptionSetValue and Money.

You only need to do an extra check on null values. (When an attribute has a null value in the system it will not be present in the attribute collection of a pre- or post-image.)

public static bool IsAttributeModified(string attributeName, Entity preImage, Entity postImage)
{
    object preValue;

    if (preImage.Attributes.TryGetValue(attributeName, out preValue))
    {
        object postValue;
        return !postImage.Attributes.TryGetValue(attributeName, out postValue) || !preValue.Equals(postValue);
    }

    return postImage.Attributes.ContainsKey(attributeName);
}
Henk van Boeijen
  • 7,357
  • 6
  • 32
  • 42