Skip to content

Instantly share code, notes, and snippets.

@JeffreyZhao
Created October 11, 2009 03:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JeffreyZhao/207370 to your computer and use it in GitHub Desktop.
Save JeffreyZhao/207370 to your computer and use it in GitHub Desktop.
Generic base to use custom set in NHibernate
namespace NHTest.Collections
{
using System.Reflection;
using Iesi.Collections.Generic;
using NHibernate.Collection.Generic;
using NHibernate.Engine;
public class PersistentSetBase<TItem, TAbstractSet> : PersistentGenericSet<TItem>
where TAbstractSet : ISet<TItem>
{
public static readonly MethodInfo InnerSetGetter =
typeof(PersistentSetBase<TItem, TAbstractSet>).GetProperty(
"InnerSet",
BindingFlags.Instance | BindingFlags.NonPublic).GetGetMethod(true);
public PersistentSetBase(ISessionImplementor session)
: base(session) { }
public PersistentSetBase(ISessionImplementor session, TAbstractSet innerSet)
: base(session, innerSet) { }
protected TAbstractSet InnerSet
{
get
{
return (TAbstractSet)this.set;
}
}
}
}
namespace NHTest.Collections
{
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using Iesi.Collections.Generic;
using NHibernate.Collection;
using NHibernate.Engine;
public static class PersistentSetFactory<TItem, TAbstractSet>
where TAbstractSet : ISet<TItem>
{
static PersistentSetFactory()
{
if (!typeof(TAbstractSet).IsInterface)
{
throw new ArgumentException("TAbstractSet must be interface.", "TAbstractSet");
}
var typeBuilder = CreateTypeBuilder(); // 1
CreateConstructors(typeBuilder); // 2
ImplementTAbstractSet(typeBuilder); // 3
GenerateCreators(typeBuilder.CreateType()); // 4
}
private static TypeBuilder CreateTypeBuilder()
{
var asmName = new AssemblyName("DynamicAssembly_" + Guid.NewGuid());
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
var moduleBuilder = asmBuilder.DefineDynamicModule("DynamicModule_" + Guid.NewGuid());
return moduleBuilder.DefineType(
typeof(TAbstractSet) + "$PersistentProxy",
TypeAttributes.Public,
typeof(PersistentSetBase<TItem, TAbstractSet>));
}
private static void CreateConstructors(TypeBuilder typeBuilder)
{
var baseType = typeof(PersistentSetBase<TItem, TAbstractSet>);
typeBuilder.CreateConstructor(GetConstructor(baseType, false));
typeBuilder.CreateConstructor(GetConstructor(baseType, true));
}
private static ConstructorInfo GetConstructor(Type type, bool wrapping)
{
var parameterTypes = wrapping ?
new Type[] { typeof(ISessionImplementor), typeof(TAbstractSet) } :
new Type[] { typeof(ISessionImplementor) };
return type.GetConstructor(parameterTypes);
}
private static void CollectMethods(Type interfaceType, HashSet<MethodInfo> methods)
{
if (interfaceType.IsAssignableFrom(typeof(ISet<TItem>))) return;
foreach (var m in interfaceType.GetMethods())
{
methods.Add(m);
}
foreach (var parent in interfaceType.GetInterfaces())
{
CollectMethods(parent, methods);
}
}
private static void ImplementTAbstractSet(TypeBuilder typeBuilder)
{
typeBuilder.AddInterfaceImplementation(typeof(TAbstractSet));
var methods = new HashSet<MethodInfo>();
CollectMethods(typeof(TAbstractSet), methods);
foreach (var m in methods)
{
typeBuilder.CreateDelegateMethod(
PersistentSetBase<TItem, TAbstractSet>.InnerSetGetter,
m);
}
}
private static void GenerateCreators(Type dynamicType)
{
var paramSession = Expression.Parameter(typeof(ISessionImplementor), "session");
s_creator =
Expression.Lambda<Func<ISessionImplementor, IPersistentCollection>>(
Expression.Convert(
Expression.New(
GetConstructor(dynamicType, false),
paramSession),
typeof(IPersistentCollection)),
paramSession).Compile();
var paramInnerSet = Expression.Parameter(typeof(TAbstractSet), "innerSet");
s_wrapCreator =
Expression.Lambda<Func<ISessionImplementor, TAbstractSet, IPersistentCollection>>(
Expression.Convert(
Expression.New(
GetConstructor(dynamicType, true),
paramSession,
paramInnerSet),
typeof(IPersistentCollection)),
paramSession,
paramInnerSet).Compile();
}
private static Func<ISessionImplementor, IPersistentCollection> s_creator;
private static Func<ISessionImplementor, TAbstractSet, IPersistentCollection> s_wrapCreator;
public static IPersistentCollection Create(ISessionImplementor session)
{
return s_creator(session);
}
public static IPersistentCollection Create(ISessionImplementor session, TAbstractSet innerSet)
{
return s_wrapCreator(session, innerSet);
}
}
}
namespace NHTest.Collections
{
using System;
using System.Collections;
using System.Collections.Generic;
using Iesi.Collections.Generic;
using NHibernate.Collection;
using NHibernate.Engine;
using NHibernate.Persister.Collection;
using NHibernate.UserTypes;
public class SetType<TItem, TSet, TAbstractSet> : IUserCollectionType
where TSet : TAbstractSet, new()
where TAbstractSet : ISet<TItem>
{
#region IUserCollectionType Members
public bool Contains(object collection, object entity)
{
return ((TAbstractSet)collection).Contains((TItem)entity);
}
public IEnumerable GetElements(object collection)
{
return (IEnumerable)collection;
}
public object IndexOf(object collection, object entity)
{
throw new NotSupportedException();
}
public object Instantiate(int anticipatedSize)
{
return new TSet();
}
public object ReplaceElements(object original, object target, ICollectionPersister persister, object owner, IDictionary copyCache, ISessionImplementor session)
{
var result = (ISet<TItem>)target;
result.Clear();
foreach (var item in (IEnumerable<TItem>)original)
{
result.Add(item);
}
return result;
}
public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
{
return PersistentSetFactory<TItem, TAbstractSet>.Create(session);
}
public IPersistentCollection Wrap(ISessionImplementor session, object collection)
{
return PersistentSetFactory<TItem, TAbstractSet>.Create(session, (TAbstractSet)collection);
}
#endregion
}
}
namespace NHTest.Collections
{
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
public static class TypeBuilderExtensions
{
private static readonly OpCode[] LoadArgsOpCodes = new OpCode[] { OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2, OpCodes.Ldarg_3 };
public static void CreateConstructor(this TypeBuilder typeBuilder, ConstructorInfo baseConstructor)
{
if (baseConstructor == null) return;
if (!baseConstructor.IsPublic && baseConstructor.IsFamily) return;
var parameters = baseConstructor.GetParameters();
if (parameters.Length == 0)
{
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
return;
}
var builder = typeBuilder.DefineConstructor(
MethodAttributes.Public,
CallingConventions.HasThis,
parameters.Select(p => p.ParameterType).ToArray());
var ilGenerator = builder.GetILGenerator();
LoadArgs(ilGenerator, 0, parameters.Length + 1);
ilGenerator.Emit(OpCodes.Call, baseConstructor);
ilGenerator.Emit(OpCodes.Ret);
}
public static void CreateDelegateMethod(this TypeBuilder typeBuilder, MethodInfo delegateGet, MethodInfo method)
{
var parameters = method.GetParameters();
var ma = MethodAttributes.Private | MethodAttributes.HideBySig |
MethodAttributes.NewSlot | MethodAttributes.Virtual |
MethodAttributes.Final;
var methodBuilder = typeBuilder.DefineMethod(
method.DeclaringType.FullName + method.Name, // can be anything
ma,
method.ReturnType,
parameters.Select(m => m.ParameterType).ToArray());
var ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0); // load "this"
ilGenerator.Emit(OpCodes.Call, delegateGet);
LoadArgs(ilGenerator, 1, parameters.Length);
ilGenerator.Emit(OpCodes.Callvirt, method);
ilGenerator.Emit(OpCodes.Ret);
typeBuilder.DefineMethodOverride(methodBuilder, method);
}
private static void LoadArgs(ILGenerator ilGenerator, int begin, int count)
{
for (int i = begin; i < begin + count; i++)
{
if (i < LoadArgsOpCodes.Length)
{
ilGenerator.Emit(LoadArgsOpCodes[i]);
}
else
{
ilGenerator.Emit(OpCodes.Ldarg_S, i + 1);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment