1

I need to retrieve an extended attribute from Active Directory, for which there is a private method in the ComputerPrincipal class. I understand I can only access the private method through a derived class, so I've derived a class ComputerPrincipalEx from the base class. I've then created (defined?) a method in the derived class which calls the private method in the base class. This part seems OK.

The problem comes when I try to use a (public) method of the base class to assign a value to a variable with the type of the derived class. Here's the code, then I'll try to explain more:

The derived class:

public class ComputerPrincipalEx : ComputerPrincipal
{
    public ComputerPrincipalEx(PrincipalContext context) : base(context) { }

    public ComputerPrincipalEx(PrincipalContext context, string samAccountName, string password, bool enabled) : base(context, samAccountName, password, enabled) { }

    public string ExtensionGet(string extendedattribute)
    {
        return (string) base.ExtensionGet(extendedattribute)[0];
    }

}

The problem code, which itself is a method of another class I've created:

public string GetExtendedAttribute(string attributeName)
    {
        PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
        ComputerPrincipalEx cp = new ComputerPrincipalEx(ctx);
        cp = ComputerPrincipalEx.FindByIdentity(ctx, Name);
        return cp.ExtensionGet(attributeName);
    }

ExtensionGet is the private method of the base class that I need to expose, and I think I have this correct because once I create an object of type ComputerPrincipalEx, I can access ExtensionGet, which is otherwise inaccessible.

The problem is the type conversion in this line: cp = ComputerPrincipalEx.FindByIdentity(ctx, Name);

cp is defined as ComputerPrincipalEx; ComputerPrincipalEx.FindByIdentity references the base ComputerPrincipal.FindByIdentity method, and returns a ComputerPrincipal. The compiler balks about an implicit conversion between types. Casting ComputerPrincipal to ComputerPrincipalEx satisfies the compiler but the app crashes at runtime because it can't perform the conversion.

Now, I pretty much understand all of that, but I'm assuming there has to be some way to call a method from the base class and return valid data to an object of the derived class' type, and that's what I'm hoping to find out how to do.

Stedy
  • 7,359
  • 14
  • 57
  • 77
Endaar
  • 21
  • 4
  • Maybe a silly question, but does `PrincipalContext ctx = new PrincipalContext(ContextType.Domain); ComputerPrincipalEx cp = (ComputerPrincipalEx)ComputerPrincipal.FindByIdentity(ctx, Name); return cp.ExtensionGet(attributeName);` work? By the way, in the first two statements the first word can be replaced by `var` if one prefers. – Jeppe Stig Nielsen Jul 04 '14 at 12:00
  • @JeppeStigNielsen - The explicit cast satisfies the compiler but crashes at runtime because the app cannot convert from `ComputerPrincipal` to `ComputerPrincipalEx`. – Endaar Jul 05 '14 at 02:30

3 Answers3

1

I was able to figure this out based on information I found here and here.

In the derived ComputerPrincipalEx class, I needed to hide the base class' FindByIdentity method and redefine it to call FindByIdentityWithType. This let me cast the return type to be ComputerPrincipalEx and thus access the ExtensionGet method. (Hopefully I'm explaining that properly; this is still very new to me.)

For this to work however, the below lines needed to be added as well. Otherwise the compiler threw an error that ComputerPrincipalEx was not a valid object type to search with.

[DirectoryObjectClass("computer")]
[DirectoryRdnPrefix("CN")]

Here's the relevant excerpt from the ComputerPrincipalEx class: (Yes, I know the try/catch block needs some work.)

[DirectoryObjectClass("computer")]
[DirectoryRdnPrefix("CN")]

public class ComputerPrincipalEx : ComputerPrincipal
{
    public ComputerPrincipalEx(PrincipalContext context) : base(context) { }

    public ComputerPrincipalEx(PrincipalContext context, string samAccountName, string password, bool enabled) : base(context, samAccountName, password, enabled) { }

    new public string ExtensionGet(string extendedattribute)
    {
        try
        {
            if (base.ExtensionGet(extendedattribute).Length != 1)
            {
                return null;
            }
            else
            {
                return (string)base.ExtensionGet(extendedattribute)[0];
            }
        }
        catch (Exception ex)
        {
            // This should be broken down to individual exceptions
            string message = string.Format("Exception occurred while retrieving extended attribute {0}. \r\nThe following error occurred:\r\n {1}", extendedattribute, ex);
            MessageBox.Show(message);
            Application.Exit();
            return null;
        }
    }

    public static new ComputerPrincipalEx FindByIdentity(PrincipalContext ctx, string identityValue)
    {
        return (ComputerPrincipalEx)FindByIdentityWithType(ctx, typeof(ComputerPrincipalEx), identityValue);
    }
}

And the revised GetExtendedAttribute call:

    public string GetExtendedAttribute(string attributeName)
    {
        PrincipalContext ctx = new PrincipalContext(ContextType.Domain); 
        var cp = ComputerPrincipalEx.FindByIdentity(ctx, Name); 
        return cp.ExtensionGet(attributeName);

    }

Thanks all for the help. Hopefully this helps someone else who runs into the same problem.

Community
  • 1
  • 1
Endaar
  • 21
  • 4
0

Change to this:

public string GetExtendedAttribute(string attributeName)
    {
        PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
        var cp = ComputerPrincipalEx.FindByIdentity(ctx, Name);
        return cp.ExtensionGet(attributeName);
    }
Jon
  • 3,230
  • 1
  • 16
  • 28
  • When I make that change, "cp" becomes type ComputerPrincipal, and thus cp.ExtensionGet is again inaccessible. – Endaar Jul 04 '14 at 02:39
0

In my impression, you need a constructor (or static method) in ComputerPrincipalEx which creates it from an instance of the base class.

Bernhard Hiller
  • 2,163
  • 2
  • 18
  • 33