Custom Attributes, Shared methods & reflection

Custom Attributes, Shared methods & reflection

Old forum URL: forums.lhotka.net/forums/t/767.aspx


jemmer posted on Monday, July 31, 2006

Hi,

We're currently using CSLA version 1.0.  We're in the process of converting a couple of apps to 2.0.

We'd like to modify BusinessBase (in both versions, if possible) just a bit to allow for a Custom Attribute to be defined for classes which derive from BusinessBase.  This attribute would be used to define a "Friendly Name" for the class.  This seems to be simple enough to do, for instances of derived classes, via code in BusinessBase like:

-----

Public ReadOnly Property FriendlyName() As String

   Get

      Dim classtype As Type = Me.GetType

      Dim attr As FriendlyNameAttribute = DirectCast(Attribute.GetCustomAttribute(classtype, GetType (FriendlyNameAttribute)), FriendlyNameAttribute)

      If attr IsNot Nothing Then

         Return attr.FriendlyName

      Else

         Return classtype.Name

      End If

   End Get

End Property

-----

where FriendlyNameAttribute is a straightforward attribute class implementing a FriendlyName string property, and the derived class's definition is decorated appropriately with the FriendlyName attribute.

The problem is that we'd like to be able to access this attribute via a shared method in BusinessBase. This is because there are situations where no instance of the derived class may be available, but we'd still like to display something in the UI using the "Friendly Name" of that class.  For example, it may not be possible to create an instance of the class (e.g, because some error has occurred, or the user is not authorized to do so, etc.), so we'd like to display an informational message with a more user friendly description of the class which is at the root of whatever the problem may be.

For example, as a programmer I might define a class called EntLOB.  A more friendly name for this class might be "Enterprise Line of Business".  Thus I'd like the error display to read something like:

   Unable to retrieve the Enterprise Line of Business because ...

rather than

   Unable to retrieve the EntLOB because ...

Creating the error string might be done as :

   errormsg = string.format("Unable to retrieve the {0} because ...", SomeEntLOB.FriendlyName, ...) ...

I would define the derived class like:

<Serializable, FriendlyName("Enterprise Line of Business")> _

Public Class EntLOB

   Inherits BusinessBase ...

As I said, this all works when I have an instance, but if I try to define the FriendlyName property in BusinessBase as shared, of course, the Me.GetType no longer works.

The creation of the error message when there is no instance presumably would be:

   errormsg = string.format("Unable to retrieve the {0} because ...", EntLOB.FriendlyName, ...) ...

So, the question is, is it possible to use reflection in a shared method in a base class to retrieve an attribute which decorates the class definition on a derived class of that base? Sure feels like I ought to be able to do that, but I just don't see how.

Thanks for any help and insight anyone can offer.

Brian Criswell replied on Monday, July 31, 2006

May I suggest that you take a look at the string resources for your business object library?  You could then add strings to that table with a lookup key such as MyApp.Library.Type1 which could point to a string that said "Type 1".  A single method could then be accessible from within your library to the outside world that would take an object, get its type, and use the type name to lookup up the friendly name in the resources table.  This would then also be localizable.

jemmer replied on Monday, July 31, 2006

Hi Brian,

Thanks for your reply.

As I stated in my original post, my problem is that I may not have an instance of the derived class to get its Type (if I do, this is easy and my problem goes away).  The method in BusinessBase (or elsewhere) would have to be shared, and I can't figure out what sort of reflection incantation I need to get the type of a derived class from within that shared method.

It turns out that we originally dealt with this by defining a MustImplement property in BusinessBase.  This seems to me, on some level, to be equivalent to your suggestion of using a string resource by the typename. The author of a derived class is then required to implement the FriendlyName method that BusinessBase insisted on.  That was OK, but we found that in the majority of the classes that we defined, the Friendly Name was the same as the class name (e.g. Customer).  Thus, most of our class definitions were "cluttered" with what appeared to be redundant FreindlyName property implementations.

Assuming I can get the type name of the derived class, then it seems to me that the string resource would also be "cluttered" with redundant entries in a similar way.

I came up with the bright idea of a Custom Attribute, and figured that the developer would only need to provide the attribute in the case where the Friendly Name was different than the class name.  The implementation of (my modified) BusinessBase's FriendlyName would return the Type's ClassName property in the case where no attribute was defined.

Works great when I have an instance of the derived class.  I can't figure out how to do it when there is none...

Brian Criswell replied on Monday, July 31, 2006

Well, in your example you know the Type of the class.  So you could just use GetType(SomeEntLOB).

jemmer replied on Monday, July 31, 2006

But this FriendlyName method would be implemented in BusinessBase.

I certainly can't encode a GetType() in that method for all the classes which might inherit from BusinessBase...

Is there no way to use reflection in a base class to get the Type of a class derived from that base when there is no instance of the derived class, i.e in a shared method of the base?

Brian Criswell replied on Monday, July 31, 2006

jemmer:

But this FriendlyName method would be implemented in BusinessBase.

I certainly can't encode a GetType() in that method for all the classes which might inherit from BusinessBase...

Is there no way to use reflection in a base class to get the Type of a class derived from that base when there is no instance of the derived class, i.e in a shared method of the base?



Um, no.  No instance == No polymorphism, which is what you need for the base class to pull behaviour from the derived class.  You could do something like this:

public class MyBase<T> :  BusinessBase<T>
    where T : MyBase<T>
{
    public string GetFriendlyName()
    {
       return ResourceManager.GetString(this.GetType().FullName, System.Globalization.CultureInfo);
    }

    // Not exactly sure of the syntax on this one... If it does not work use GetFriendlyName(Type type)
    public static string GetFriendlyName<T>()
    {
       return ResourceManager.GetString(typeof(T).FullName, System.Globalization.CultureInfo);
    }
}

Copyright (c) Marimer LLC