I think I've come across a better solution (or at least one that I'm happier with) to this problem. Turns out that the KnownType attribute can take a method name instead of a type. The type it's applied to then has to implement a static method with that name that returns an array of Type. That method can then decide which types to return. So I could do something like this
[DataContract]
[KnownType("CheckForConfiguredOverrides")]
public class UserBase
{
[DataMember]
public string FirstName;
[DataMember]
public string LastName;
public static Type[] CheckForConfiguredOverrides()
{
List<Type> result = new List<Type>();
string thisTypeName = typeof(UserBase).FullName;
string overrideType = System.Configuration.ConfigurationManager.AppSettings.Get(thisTypeName);
if (!string.IsNullOrEmpty(overrideType))
{
Type type = Type.GetType(overrideType);
if (type != null) result.Add(type);
}
return result.ToArray();
}
}
Then in the config file (this ultimately will use our distributed configuration service) I add
<appSettings>
<add key="UserBaseType.UserBase" value="UserBaseType.BankUser"/>
</appSettings>
To return the right type. Everybody is happy! As a side benefit, this allows me to limit "overridability" to only those types I apply the attribute to. That lets me (as the designer) decide which types are OK to subclass and which aren't (obviously I could use sealed here as well, but that's a separate issue) using this KnownType attribute. I think this will work out much better than marking up base classes with specific types.