Created
October 11, 2009 03:13
-
-
Save JeffreyZhao/207370 to your computer and use it in GitHub Desktop.
Generic base to use custom set in NHibernate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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