Update to view dictionary adapter

43 views
Skip to first unread message

Lee Henson

unread,
Mar 22, 2007, 9:09:49 AM3/22/07
to Castle Project Development List
I've added a couple of extra features to that reflecting view
dictionary adapter I stole from the eleutian guys. I'm also using it
to now strongly-type my Session usage, as well as Flash and
PropertyBag. To stop key collisions, I've got a custom attribute on
the dictionary adapter interface which allows me to specify a prefix
to the generated dictionary key. For example:

[DictionaryAdapterKeyPrefix("Artist.Registration.")]
public interface IArtistRegistrationSessionAdapter :
IDictionaryAdapter
{
AccountRegistration AccountRegistration { get; set; }
ContactDetails ContactDetails { get; set; }
Nullable<PackageType> PackageType { get; set; }
string RegisteredUserName { get; set; }
}

You can also do session adapter interface inheritance, with the key
prefix defaulting to the most specific definition it can find. If I
recall correctly, there were a few bugs in the old code which are
fixed here.

If people find this useful perhaps it might be worth adding to the
code generator contrib project?

=============================

public interface IDictionaryAdapter
{
}

public interface IDictionaryAdapterFactory
{
T GetAdapter<T>(IDictionary session) where T : IDictionaryAdapter;
}

[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false,
Inherited = true)]
public class DictionaryAdapterKeyPrefixAttribute : Attribute
{
private string keyPrefix;

public DictionaryAdapterKeyPrefixAttribute()
{
}

public DictionaryAdapterKeyPrefixAttribute(string keyPrefix)
{
this.keyPrefix = keyPrefix;
}

public string KeyPrefix
{
get { return keyPrefix; }
set { keyPrefix = value; }
}
}

public class DictionaryAdapterFactory : IDictionaryAdapterFactory
{
public T GetAdapter<T>(IDictionary dictionary) where T :
IDictionaryAdapter
{
AppDomain appDomain = Thread.GetDomain();
string adapterAssemblyName = GetAdapterAssemblyName<T>();
Assembly adapterAssembly = GetExistingAdapterAssembly(appDomain,
adapterAssemblyName);

if (adapterAssembly == null)
adapterAssembly = CreateAdapterAssembly<T>(appDomain,
adapterAssemblyName);

return GetExistingAdapter<T>(adapterAssembly, dictionary);
}

private static Assembly CreateAdapterAssembly<T>(AppDomain appDomain,
string adapterAssemblyName)
{
AssemblyName assemblyName = new AssemblyName(adapterAssemblyName);
AssemblyBuilder assemblyBuilder =
appDomain.DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder =
assemblyBuilder.DefineDynamicModule(adapterAssemblyName);

TypeBuilder typeBuilder = CreateAdapterType<T>(moduleBuilder);
FieldBuilder dictionaryField =
CreateAdapterDictionaryField(typeBuilder);
CreateAdapterConstructor(typeBuilder, dictionaryField);

List<PropertyInfo> properties = new List<PropertyInfo>();
RecursivelyDiscoverProperties(properties, typeof(T));
properties.ForEach(delegate(PropertyInfo propertyInfo)
{ CreateAdapterProperty(typeBuilder, dictionaryField,
propertyInfo); });

typeBuilder.CreateType();

return assemblyBuilder;
}

private static void CreateAdapterConstructor(TypeBuilder typeBuilder,
FieldBuilder dictionaryField)
{
ConstructorBuilder constructorBuilder =
typeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.HideBySig,
CallingConventions.Standard, new Type[] { typeof(IDictionary) });
constructorBuilder.DefineParameter(1, ParameterAttributes.None,
"dictionary");

ILGenerator ilGenerator = constructorBuilder.GetILGenerator();

Type objType = Type.GetType("System.Object");
ConstructorInfo objectConstructorInfo = objType.GetConstructor(new
Type[0]);

ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, objectConstructorInfo);
ilGenerator.Emit(OpCodes.Nop);
ilGenerator.Emit(OpCodes.Nop);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.Emit(OpCodes.Stfld, dictionaryField);
ilGenerator.Emit(OpCodes.Nop);
ilGenerator.Emit(OpCodes.Ret);
}

private static FieldBuilder CreateAdapterDictionaryField(TypeBuilder
typeBuilder)
{
return typeBuilder.DefineField("dictionary", typeof(IDictionary),
FieldAttributes.Private);
}

private static void CreateAdapterProperty(TypeBuilder typeBuilder,
FieldBuilder dictionaryField, PropertyInfo property)
{
PropertyBuilder propertyBuilder =
typeBuilder.DefineProperty(property.Name, property.Attributes,
property.PropertyType, null);
MethodAttributes propertyMethodAttributes = MethodAttributes.Public
| MethodAttributes.SpecialName | MethodAttributes.HideBySig |
MethodAttributes.Virtual;

if (property.CanRead)
CreateAdapterPropertyGetMethod(typeBuilder, dictionaryField,
propertyBuilder, property, propertyMethodAttributes);

if (property.CanWrite)
CreateAdapterPropertySetMethod(typeBuilder, dictionaryField,
propertyBuilder, property, propertyMethodAttributes);
}

private static void CreateAdapterPropertyGetMethod(TypeBuilder
typeBuilder, FieldBuilder dictionaryField, PropertyBuilder
propertyBuilder, PropertyInfo property, MethodAttributes
propertyMethodAttributes)
{
MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" +
property.Name, propertyMethodAttributes, property.PropertyType, null);

ILGenerator getILGenerator = getMethodBuilder.GetILGenerator();
Label label = getILGenerator.DefineLabel();

getILGenerator.DeclareLocal(property.PropertyType);
getILGenerator.Emit(OpCodes.Nop);
getILGenerator.Emit(OpCodes.Ldarg_0);
getILGenerator.Emit(OpCodes.Ldfld, dictionaryField);
getILGenerator.Emit(OpCodes.Ldstr,
GetDictionaryFieldName(property));
getILGenerator.Emit(OpCodes.Callvirt,
typeof(IDictionary).GetMethod("get_Item", new Type[]
{ typeof(Object) }));

if (property.PropertyType.IsValueType)
getILGenerator.Emit(OpCodes.Unbox_Any, property.PropertyType);
else
getILGenerator.Emit(OpCodes.Castclass, property.PropertyType);

getILGenerator.Emit(OpCodes.Stloc_0);
getILGenerator.Emit(OpCodes.Br_S, label);
getILGenerator.MarkLabel(label);
getILGenerator.Emit(OpCodes.Ldloc_0);
getILGenerator.Emit(OpCodes.Ret);

propertyBuilder.SetGetMethod(getMethodBuilder);
}

private static void CreateAdapterPropertySetMethod(TypeBuilder
typeBuilder, FieldBuilder dictionaryField, PropertyBuilder
propertyBuilder, PropertyInfo property, MethodAttributes
propertyMethodAttributes)
{
MethodBuilder setMethodBuilder = typeBuilder.DefineMethod(
"set_" + property.Name, propertyMethodAttributes, null, new Type[]
{ property.PropertyType });

ILGenerator setILGenerator = setMethodBuilder.GetILGenerator();

setILGenerator.Emit(OpCodes.Nop);
setILGenerator.Emit(OpCodes.Ldarg_0);
setILGenerator.Emit(OpCodes.Ldfld, dictionaryField);
setILGenerator.Emit(OpCodes.Ldstr,
GetDictionaryFieldName(property));
setILGenerator.Emit(OpCodes.Ldarg_1);

if (property.PropertyType.IsValueType)
setILGenerator.Emit(OpCodes.Box, property.PropertyType);

setILGenerator.Emit(OpCodes.Callvirt,
typeof(IDictionary).GetMethod("set_Item", new Type[] { typeof(Object),
typeof(Object) }));
setILGenerator.Emit(OpCodes.Nop);
setILGenerator.Emit(OpCodes.Ret);

propertyBuilder.SetSetMethod(setMethodBuilder);
}

private static TypeBuilder CreateAdapterType<T>(ModuleBuilder
moduleBuilder)
{
TypeBuilder typeBuilder =
moduleBuilder.DefineType(GetAdapterFullTypeName<T>(),
TypeAttributes.Public | TypeAttributes.Class |
TypeAttributes.BeforeFieldInit);
typeBuilder.AddInterfaceImplementation(typeof(T));

return typeBuilder;
}

private static string GetAdapterAssemblyName<T>()
{
return typeof(T).Assembly.GetName().Name + "." + typeof(T).FullName
+ ".DictionaryAdapter";
}

private static string GetAdapterFullTypeName<T>()
{
return typeof(T).Namespace + "." + GetAdapterTypeName<T>();
}

private static string GetAdapterTypeName<T>()
{
return typeof(T).Name.Substring(1) + "DictionaryAdapter";
}

private static DictionaryAdapterKeyPrefixAttribute
GetDictionaryAdapterAttribute(PropertyInfo property)
{
List<Type> interfaces = new List<Type>();

interfaces.Add(property.DeclaringType);
interfaces.AddRange(property.DeclaringType.GetInterfaces());

foreach (Type type in interfaces)
{
object[] attributes =
type.GetCustomAttributes(typeof(DictionaryAdapterKeyPrefixAttribute),
false);

if (attributes.Length > 0)
return (DictionaryAdapterKeyPrefixAttribute) attributes[0];
}

return null;
}

private static string GetDictionaryFieldName(PropertyInfo property)
{
DictionaryAdapterKeyPrefixAttribute
dictionaryAdapterKeyPrefixAttribute =
GetDictionaryAdapterAttribute(property);

string prefix = dictionaryAdapterKeyPrefixAttribute == null
? string.Empty
: dictionaryAdapterKeyPrefixAttribute.KeyPrefix;

return prefix + property.Name;
}

private static T GetExistingAdapter<T>(Assembly assembly, IDictionary
dictionary)
{
string adapterFullTypeName = GetAdapterFullTypeName<T>();
return
(T)Activator.CreateInstance(assembly.GetType(adapterFullTypeName,
true), dictionary);
}

private static Assembly GetExistingAdapterAssembly(AppDomain
appDomain, string assemblyName)
{
return Array.Find(appDomain.GetAssemblies(),
delegate(Assembly assembly) { return assembly.GetName().Name ==
assemblyName; });
}

private static void RecursivelyDiscoverProperties(List<PropertyInfo>
properties, Type currentType)
{
properties.AddRange(currentType.GetProperties(BindingFlags.Public |
BindingFlags.Instance));

Array.ForEach(currentType.GetInterfaces(),
delegate(Type parentInterface)
{ RecursivelyDiscoverProperties(properties, parentInterface); });
}
}

Jacob Lewallen

unread,
Mar 22, 2007, 12:53:45 PM3/22/07
to castle-pro...@googlegroups.com
I like it Lee. I'll commit that as soon as I get a chance. I was
planning a blog post soon about some other stuff you can do, I'll have
to toss it up before it becomes outdated... heheh. Nothing like the
attribute though, again, great stuff.

jacob

Lee Henson

unread,
Mar 22, 2007, 1:16:12 PM3/22/07
to Castle Project Development List
> I was planning a blog post soon about some other stuff you can do

Great! I look forward to your posts with equal parts excitement
(because you guys are always coming up with interesting ideas) and
dread (because then I decide I *HAVE TO* implement them immendiately
on my project).

>:]

On Mar 22, 4:53 pm, "Jacob Lewallen" <jlewa...@gmail.com> wrote:
> I like it Lee. I'll commit that as soon as I get a chance. I was
> planning a blog post soon about some other stuff you can do, I'll have
> to toss it up before it becomes outdated... heheh. Nothing like the
> attribute though, again, great stuff.
>
> jacob
>

> ...
>
> read more »

jacob

unread,
Mar 22, 2007, 1:26:07 PM3/22/07
to Castle Project Development List
Haha, well there I tossed it up. I had written in yesterday and wasn't
sure if I was done... so yeah I guess I am. It's nothing new code-
wise, just suggestions on other ways to use it. I've definitely
learned my lesson, code+ideas... not just ideas. Hehe.

jacob

> ...
>
> read more »

Jacob Lewallen

unread,
Mar 22, 2007, 1:41:07 PM3/22/07
to castle-pro...@googlegroups.com
I checked that code in Lee.

jacob

Reply all
Reply to author
Forward
0 new messages