39

I've got an object that has a collection of properties. When I get the specific entity I can see the field I'm looking for (opportunityid) and that it's Value attribute is the Guid for this opportunity. This is the value I want, but it won't always be for an opportunity, and therefore I can't always look at opportunityid, so I need to get the field based on input supplied by the user.

My code so far is:

Guid attrGuid = new Guid();

BusinessEntityCollection members = CrmWebService.RetrieveMultiple(query);

if (members.BusinessEntities.Length > 0)
{
    try
    {
        dynamic attr = members.BusinessEntities[0];
        //Get collection of opportunity properties
        System.Reflection.PropertyInfo[] Props = attr.GetType().GetProperties();
        System.Reflection.PropertyInfo info = Props.FirstOrDefault(x => x.Name == GuidAttributeName);
        attrGuid = info.PropertyType.GUID; //doesn't work.
    }
    catch (Exception ex)
    {
        throw new Exception("An error occurred when retrieving the value for " + attributeName + ". Error: " + ex.Message);
    }
}

The dynamic attr contains the field I'm looking for (in this case opportunityid), which in turn contains a value field, which is the correct Guid. However, when I get the PropertyInfo info (opportunityid) it no longer has a Value attribute. I tried looking at the PropertyType.GUID but this doesn't return the correct Guid. How can I get the value for this property?

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
sr28
  • 4,728
  • 5
  • 36
  • 67

5 Answers5

64

Unless the property is static, it is not enough to get a PropertyInfo object to get a value of a property. When you write "plain" C# and you need to get a value of some property, say, MyProperty, you write this:

var val = obj.MyProperty;

You supply two things - the property name (i.e. what to get) and the object (i.e. from where to get it).

PropertyInfo represents the "what". You need to specify "from where" separately. When you call

var val = info.GetValue(obj);

you pass the "from where" to the PropertyInfo, letting it extract the value of the property from the object for you.

Note: prior to .NET 4.5 you need to pass null as a second argument:

var val = info.GetValue(obj, null);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Sounds like what I need. Just going to test it to make sure I understand. – sr28 Oct 15 '14 at 12:47
  • Hmmm, I'm sure this method would work if I understood it. However, I'm getting an exception saying it can't convert CRMWebService.Key to System.Guid. I've basically done attrGuid = (Guid)info.GetValue(attr, null); – sr28 Oct 15 '14 at 12:57
  • @sr28 It looks like the property returns `CRMWebService.Key`, not `Guid`. Try `attrGuid = ((CRMWebService.Key)info.GetValue(attr, null)).Value` (assuming that `Value` is of `Guid` type). – Sergey Kalinichenko Oct 15 '14 at 13:05
  • That worked, though I didn't need the (CRMWebService.Key) bit. I was able to get it using attrGuid = info.GetValue(attr, null).Value; Thanks! – sr28 Oct 15 '14 at 13:09
  • Uhm.. okay, so, with reference to where it says, _"Unless the property is static"_ ... What do you do pass-in when the Property **IS** static? I'm getting the PropertyInfo data via `{TypeInfoObject}.GetProperties( BindingFlags.Public | BindingFlags.Static )` so I don't think that I _have_ an Object instance to pass to `item.GetValue( {object} )` – NetXpert Oct 07 '20 at 18:55
  • ..for anyone else with the same question, it turns out that you can simply pass `null` to the GetValue call if you're calling a static Accessor in a static Class. – NetXpert Oct 07 '20 at 20:12
6

If the name of the property is changing, you should use GetValue:

info.GetValue(attr, null);

The last attribute of that method can be null, since it is the index value, and that is only needed when accessing arrays, like Value[1,2].

If you know the name of the attribute on beforehand, you could use the dynamic behavior of it: you can call the property without the need of doing reflection yourself:

var x = attr.Guid;
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
  • Would this not give me the Guid of attr? In my example attr represents an entity, which in turn has the property opportunityid, which in turn has a value (guid), so do I not need to get the property called opportunityid first? – sr28 Oct 15 '14 at 12:47
  • You can type behind it whatever you want, as long as it is an existing property, since it is JIT'ed anyway. – Patrick Hofman Oct 15 '14 at 12:47
  • I'm not sure I understand what you mean by 'type behind it whatever you want'. Are you saying I should type attr.opportunityid.Value? If so that won't work for me as what goes after attr can change. – sr28 Oct 15 '14 at 12:49
  • @sr28: Sorry, missed that. Let me have a look. – Patrick Hofman Oct 15 '14 at 12:50
  • I think it should be attr. not attr.Value – helb Oct 15 '14 at 12:51
  • @sr28: Checked, but this won't work indeed if the `Value` is not known on beforehand. I updated the answer to be correct now, my bad. (I upvoted the other answers since I think they should prevail above mine. Feel free to accept one of them) – Patrick Hofman Oct 15 '14 at 13:00
3

Use PropertyInfo.GetValue(). Assuming your property has the type Guid? then this should work:

attrGuid = ((System.Guid?)info.GetValue(attr, null)).Value;

Note that an exception will be thrown if the property value is null.

helb
  • 7,609
  • 8
  • 36
  • 58
2

try with:

attrGuid = (Guid)info.GetValue(attr,null)
Alberto
  • 15,626
  • 9
  • 43
  • 56
0

Jut to add that this can be achieved via reflection (even for properties in un-instantiated, nested classes / types)

Imports System
Imports System.Reflection

Public Class Program
    Public Shared Sub Main()
        Dim p = New Person()
        For Each nestedtype in p.Gettype().GetNestedTypes()
            For Each prop in nestedtype.GetProperties(BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Static)
                Console.WriteLine(nestedtype.Name & " => " & prop.Name)
            Next
        Next
    End Sub
End Class

Public Class Person
    Public Property Name As String
    Public Property AddressDetail As Address
                    
    Public Class Address                        
        Public Property Street As String
        Public Property CountryDetail As Country
    End Class

    Public Class Country
        Public Property CountryName As String
    End Class                   
End Class

Prints the following:

Address => Street

Address => CountryDetail

Country => CountryName

fiddle: https://dotnetfiddle.net/6OsRkp

Nepaluz
  • 669
  • 1
  • 12
  • 27