600 lines
28 KiB
C#
600 lines
28 KiB
C#
|
//---------------------------------------------------------------------
|
||
|
// <copyright file="LightweightCodeGenerator.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
//
|
||
|
// @owner [....]
|
||
|
// @backupOwner [....]
|
||
|
//---------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Data.Objects
|
||
|
{
|
||
|
using System;
|
||
|
using System.Data.Common.Utils;
|
||
|
using System.Data.Metadata.Edm;
|
||
|
using System.Data.Objects.DataClasses;
|
||
|
using System.Diagnostics;
|
||
|
using System.Reflection;
|
||
|
using System.Reflection.Emit;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Security;
|
||
|
using System.Security.Permissions;
|
||
|
|
||
|
/// <summary>
|
||
|
/// CodeGenerator class: use lightweight code gen to dynamically generate code to get/set properties.
|
||
|
/// </summary>
|
||
|
internal static class LightweightCodeGenerator
|
||
|
{
|
||
|
/// <summary>For an OSpace ComplexType returns the delegate to construct the clr instance.</summary>
|
||
|
internal static Delegate GetConstructorDelegateForType(ClrComplexType clrType)
|
||
|
{
|
||
|
return (clrType.Constructor ?? (clrType.Constructor = CreateConstructor(clrType.ClrType)));
|
||
|
}
|
||
|
|
||
|
/// <summary>For an OSpace EntityType returns the delegate to construct the clr instance.</summary>
|
||
|
internal static Delegate GetConstructorDelegateForType(ClrEntityType clrType)
|
||
|
{
|
||
|
return (clrType.Constructor ?? (clrType.Constructor = CreateConstructor(clrType.ClrType)));
|
||
|
}
|
||
|
|
||
|
/// <summary>for an OSpace property, get the property value from a clr instance</summary>
|
||
|
internal static object GetValue(EdmProperty property, object target)
|
||
|
{
|
||
|
Func<object, object> getter = GetGetterDelegateForProperty(property);
|
||
|
Debug.Assert(null != getter, "null getter");
|
||
|
|
||
|
return getter(target);
|
||
|
}
|
||
|
|
||
|
internal static Func<object,object> GetGetterDelegateForProperty(EdmProperty property)
|
||
|
{
|
||
|
return property.ValueGetter ?? (property.ValueGetter = CreatePropertyGetter(property.EntityDeclaringType, property.PropertyGetterHandle));
|
||
|
}
|
||
|
|
||
|
/// <summary>for an OSpace property, set the property value on a clr instance</summary>
|
||
|
/// <exception cref="System.Data.ConstraintException">
|
||
|
/// If <paramref name="value"/> is null for a non nullable property.
|
||
|
/// </exception>
|
||
|
/// <exception cref="System.InvalidOperationException">
|
||
|
/// Invalid cast of <paramref name="value"/> to property type.
|
||
|
/// </exception>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">
|
||
|
/// From generated enties via StructuralObject.SetValidValue.
|
||
|
/// </exception>
|
||
|
/// <permission cref="System.Security.Permissions.ReflectionPermission">
|
||
|
/// If the property setter is not public or declaring class is not public.
|
||
|
/// </permission>
|
||
|
/// <permission cref="System.Security.NamedPermissionSet">
|
||
|
/// Demand for FullTrust if the property setter or declaring class has a <see cref="System.Security.Permissions.SecurityAction.LinkDemand"/>
|
||
|
/// </permission>
|
||
|
internal static void SetValue(EdmProperty property, object target, object value)
|
||
|
{
|
||
|
Action<object, object> setter = GetSetterDelegateForProperty(property);
|
||
|
setter(target, value);
|
||
|
}
|
||
|
|
||
|
/// <summary>For an OSpace property, gets the delegate to set the property value on a clr instance.</summary>
|
||
|
internal static Action<object, object> GetSetterDelegateForProperty(EdmProperty property)
|
||
|
{
|
||
|
Action<object, object> setter = property.ValueSetter;
|
||
|
if (null == setter)
|
||
|
{
|
||
|
setter = CreatePropertySetter(property.EntityDeclaringType, property.PropertySetterHandle,
|
||
|
property.Nullable);
|
||
|
property.ValueSetter = setter;
|
||
|
}
|
||
|
Debug.Assert(null != setter, "null setter");
|
||
|
return setter;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the related end instance for the source AssociationEndMember by creating a DynamicMethod to
|
||
|
/// call GetRelatedCollection or GetRelatedReference
|
||
|
/// </summary>
|
||
|
internal static RelatedEnd GetRelatedEnd(RelationshipManager sourceRelationshipManager, AssociationEndMember sourceMember, AssociationEndMember targetMember, RelatedEnd existingRelatedEnd)
|
||
|
{
|
||
|
Func<RelationshipManager, RelatedEnd, RelatedEnd> getRelatedEnd = sourceMember.GetRelatedEnd;
|
||
|
if (null == getRelatedEnd)
|
||
|
{
|
||
|
getRelatedEnd = CreateGetRelatedEndMethod(sourceMember, targetMember);
|
||
|
sourceMember.GetRelatedEnd = getRelatedEnd;
|
||
|
}
|
||
|
Debug.Assert(null != getRelatedEnd, "null getRelatedEnd");
|
||
|
|
||
|
return getRelatedEnd(sourceRelationshipManager, existingRelatedEnd);
|
||
|
}
|
||
|
|
||
|
#region Navigation Property
|
||
|
|
||
|
internal static Action<object, object> CreateNavigationPropertySetter(Type declaringType, PropertyInfo navigationProperty)
|
||
|
{
|
||
|
MethodInfo mi = navigationProperty.GetSetMethod(true);
|
||
|
Type realType = navigationProperty.PropertyType;
|
||
|
|
||
|
if (null == mi)
|
||
|
{
|
||
|
ThrowPropertyNoSetter();
|
||
|
}
|
||
|
if (mi.IsStatic)
|
||
|
{
|
||
|
ThrowPropertyIsStatic();
|
||
|
}
|
||
|
if (mi.DeclaringType.IsValueType)
|
||
|
{
|
||
|
ThrowPropertyDeclaringTypeIsValueType();
|
||
|
}
|
||
|
|
||
|
// the setter always skips visibility so that we can call our internal method to handle errors
|
||
|
// because CreateDynamicMethod asserts ReflectionPermission, method is "elevated" and must be treated carefully
|
||
|
DynamicMethod method = CreateDynamicMethod(mi.Name, typeof(void), new Type[] { typeof(object), typeof(object) });
|
||
|
ILGenerator gen = method.GetILGenerator();
|
||
|
GenerateNecessaryPermissionDemands(gen, mi);
|
||
|
|
||
|
gen.Emit(OpCodes.Ldarg_0);
|
||
|
gen.Emit(OpCodes.Castclass, declaringType);
|
||
|
gen.Emit(OpCodes.Ldarg_1);
|
||
|
gen.Emit(OpCodes.Castclass, navigationProperty.PropertyType);
|
||
|
gen.Emit(OpCodes.Callvirt, mi); // .Property =
|
||
|
gen.Emit(OpCodes.Ret);
|
||
|
|
||
|
return (Action<object, object>)method.CreateDelegate(typeof(Action<object, object>));
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region get the delegate
|
||
|
|
||
|
/// <summary>Gets a parameterless constructor for the specified type.</summary>
|
||
|
/// <param name="type">Type to get constructor for.</param>
|
||
|
/// <returns>Parameterless constructor for the specified type.</returns>
|
||
|
internal static ConstructorInfo GetConstructorForType(Type type)
|
||
|
{
|
||
|
System.Diagnostics.Debug.Assert(type != null);
|
||
|
ConstructorInfo ci = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance, null, System.Type.EmptyTypes, null);
|
||
|
if (null == ci)
|
||
|
{
|
||
|
ThrowConstructorNoParameterless(type);
|
||
|
}
|
||
|
return ci;
|
||
|
}
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// generate a delegate equivalent to
|
||
|
/// private object Constructor() { return new XClass(); }
|
||
|
/// </summary>
|
||
|
internal static Delegate CreateConstructor(Type type)
|
||
|
{
|
||
|
ConstructorInfo ci = GetConstructorForType(type);
|
||
|
|
||
|
// because CreateDynamicMethod asserts ReflectionPermission, method is "elevated" and must be treated carefully
|
||
|
DynamicMethod method = CreateDynamicMethod(ci.DeclaringType.Name, typeof(object), Type.EmptyTypes);
|
||
|
ILGenerator gen = method.GetILGenerator();
|
||
|
GenerateNecessaryPermissionDemands(gen, ci);
|
||
|
|
||
|
gen.Emit(OpCodes.Newobj, ci);
|
||
|
gen.Emit(OpCodes.Ret);
|
||
|
return method.CreateDelegate(typeof(Func<object>));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// generate a delegate equivalent to
|
||
|
/// private object MemberGetter(object target) { return target.PropertyX; }
|
||
|
/// or if the property is Nullable<> generate a delegate equivalent to
|
||
|
/// private object MemberGetter(object target) { Nullable<X> y = target.PropertyX; return ((y.HasValue) ? y.Value : null); }
|
||
|
/// </summary>
|
||
|
private static Func<object, object> CreatePropertyGetter(RuntimeTypeHandle entityDeclaringType, RuntimeMethodHandle rmh)
|
||
|
{
|
||
|
if (default(RuntimeMethodHandle).Equals(rmh))
|
||
|
{
|
||
|
ThrowPropertyNoGetter();
|
||
|
}
|
||
|
|
||
|
Debug.Assert(!default(RuntimeTypeHandle).Equals(entityDeclaringType), "Type handle of entity should always be known.");
|
||
|
var mi = (MethodInfo)MethodBase.GetMethodFromHandle(rmh, entityDeclaringType);
|
||
|
|
||
|
if (mi.IsStatic)
|
||
|
{
|
||
|
ThrowPropertyIsStatic();
|
||
|
}
|
||
|
if (mi.DeclaringType.IsValueType)
|
||
|
{
|
||
|
ThrowPropertyDeclaringTypeIsValueType();
|
||
|
}
|
||
|
|
||
|
if (0 != mi.GetParameters().Length)
|
||
|
{
|
||
|
ThrowPropertyIsIndexed();
|
||
|
}
|
||
|
|
||
|
Type realType = mi.ReturnType;
|
||
|
if ((null == realType) || (typeof(void) == realType))
|
||
|
{
|
||
|
ThrowPropertyUnsupportedForm();
|
||
|
}
|
||
|
if (realType.IsPointer)
|
||
|
{
|
||
|
ThrowPropertyUnsupportedType();
|
||
|
}
|
||
|
|
||
|
// because CreateDynamicMethod asserts ReflectionPermission, method is "elevated" and must be treated carefully
|
||
|
DynamicMethod method = CreateDynamicMethod(mi.Name, typeof(object), new Type[] { typeof(object) });
|
||
|
ILGenerator gen = method.GetILGenerator();
|
||
|
GenerateNecessaryPermissionDemands(gen, mi);
|
||
|
|
||
|
// the 'this' target pointer
|
||
|
gen.Emit(OpCodes.Ldarg_0);
|
||
|
gen.Emit(OpCodes.Castclass, mi.DeclaringType);
|
||
|
gen.Emit(mi.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi);
|
||
|
|
||
|
if (realType.IsValueType)
|
||
|
{
|
||
|
Type elementType;
|
||
|
if (realType.IsGenericType && (typeof(Nullable<>) == realType.GetGenericTypeDefinition()))
|
||
|
{
|
||
|
elementType = realType.GetGenericArguments()[0];
|
||
|
|
||
|
Label lableFalse = gen.DefineLabel();
|
||
|
LocalBuilder local = gen.DeclareLocal(realType);
|
||
|
gen.Emit(OpCodes.Stloc_S, local);
|
||
|
|
||
|
gen.Emit(OpCodes.Ldloca_S, local);
|
||
|
gen.Emit(OpCodes.Call, realType.GetMethod("get_HasValue"));
|
||
|
gen.Emit(OpCodes.Brfalse_S, lableFalse);
|
||
|
|
||
|
gen.Emit(OpCodes.Ldloca_S, local);
|
||
|
gen.Emit(OpCodes.Call, realType.GetMethod("get_Value"));
|
||
|
gen.Emit(OpCodes.Box, elementType = realType.GetGenericArguments()[0]);
|
||
|
gen.Emit(OpCodes.Ret);
|
||
|
|
||
|
gen.MarkLabel(lableFalse);
|
||
|
gen.Emit(OpCodes.Ldnull);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// need to box to return value as object
|
||
|
elementType = realType;
|
||
|
gen.Emit(OpCodes.Box, elementType);
|
||
|
}
|
||
|
}
|
||
|
gen.Emit(OpCodes.Ret);
|
||
|
return (Func<object, object>)method.CreateDelegate(typeof(Func<object, object>));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// generate a delegate equivalent to
|
||
|
///
|
||
|
/// // if Property is Nullable value type
|
||
|
/// private void MemberSetter(object target, object value) {
|
||
|
/// if (AllwNull && (null == value)) {
|
||
|
/// ((TargetType)target).PropertyName = default(PropertyType?);
|
||
|
/// return;
|
||
|
/// }
|
||
|
/// if (value is PropertyType) {
|
||
|
/// ((TargetType)target).PropertyName = new (PropertyType?)((PropertyType)value);
|
||
|
/// return;
|
||
|
/// }
|
||
|
/// ThrowInvalidValue(value, TargetType.Name, PropertyName);
|
||
|
/// return
|
||
|
/// }
|
||
|
///
|
||
|
/// // when PropertyType is a value type
|
||
|
/// private void MemberSetter(object target, object value) {
|
||
|
/// if (value is PropertyType) {
|
||
|
/// ((TargetType)target).PropertyName = (PropertyType)value;
|
||
|
/// return;
|
||
|
/// }
|
||
|
/// ThrowInvalidValue(value, TargetType.Name, PropertyName);
|
||
|
/// return
|
||
|
/// }
|
||
|
///
|
||
|
/// // when PropertyType is a reference type
|
||
|
/// private void MemberSetter(object target, object value) {
|
||
|
/// if ((AllwNull && (null == value)) || (value is PropertyType)) {
|
||
|
/// ((TargetType)target).PropertyName = ((PropertyType)value);
|
||
|
/// return;
|
||
|
/// }
|
||
|
/// ThrowInvalidValue(value, TargetType.Name, PropertyName);
|
||
|
/// return
|
||
|
/// }
|
||
|
/// </summary>
|
||
|
/// <exception cref="System.InvalidOperationException">
|
||
|
/// If the method is missing or static or has indexed parameters.
|
||
|
/// Or if the delcaring type is a value type.
|
||
|
/// Or if the parameter type is a pointer.
|
||
|
/// Or if the method or declaring class has a <see cref="System.Security.Permissions.StrongNameIdentityPermissionAttribute"/>.
|
||
|
/// </exception>
|
||
|
private static Action<object, object> CreatePropertySetter(RuntimeTypeHandle entityDeclaringType, RuntimeMethodHandle rmh, bool allowNull)
|
||
|
{
|
||
|
MethodInfo mi;
|
||
|
Type realType;
|
||
|
ValidateSetterProperty(entityDeclaringType, rmh, out mi, out realType);
|
||
|
|
||
|
// the setter always skips visibility so that we can call our internal method to handle errors
|
||
|
// because CreateDynamicMethod asserts ReflectionPermission, method is "elevated" and must be treated carefully
|
||
|
DynamicMethod method = CreateDynamicMethod(mi.Name, typeof(void), new Type[] { typeof(object), typeof(object) });
|
||
|
ILGenerator gen = method.GetILGenerator();
|
||
|
GenerateNecessaryPermissionDemands(gen, mi);
|
||
|
|
||
|
Type elementType = realType;
|
||
|
Label labelContinueNull = gen.DefineLabel();
|
||
|
Label labelContinueValue = gen.DefineLabel();
|
||
|
Label labelInvalidValue = gen.DefineLabel();
|
||
|
if (realType.IsValueType)
|
||
|
{
|
||
|
if (realType.IsGenericType && (typeof(Nullable<>) == realType.GetGenericTypeDefinition()))
|
||
|
{
|
||
|
elementType = realType.GetGenericArguments()[0];
|
||
|
}
|
||
|
else
|
||
|
{ // force allowNull false for non-nullable value types
|
||
|
allowNull = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ((TargetType)instance)
|
||
|
gen.Emit(OpCodes.Ldarg_0);
|
||
|
gen.Emit(OpCodes.Castclass, mi.DeclaringType);
|
||
|
|
||
|
// if (value is elementType) {
|
||
|
gen.Emit(OpCodes.Ldarg_1);
|
||
|
gen.Emit(OpCodes.Isinst, elementType);
|
||
|
|
||
|
if (allowNull)
|
||
|
{ // reference type or nullable type
|
||
|
gen.Emit(OpCodes.Ldarg_1);
|
||
|
if (elementType == realType)
|
||
|
{
|
||
|
gen.Emit(OpCodes.Brfalse_S, labelContinueNull); // if (null ==
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gen.Emit(OpCodes.Brtrue, labelContinueValue);
|
||
|
gen.Emit(OpCodes.Pop); // pop Isinst
|
||
|
|
||
|
LocalBuilder local = gen.DeclareLocal(realType);
|
||
|
gen.Emit(OpCodes.Ldloca_S, local); // load valuetype&
|
||
|
gen.Emit(OpCodes.Initobj, realType); // init &
|
||
|
gen.Emit(OpCodes.Ldloc_0); // load valuetype
|
||
|
gen.Emit(OpCodes.Br_S, labelContinueNull);
|
||
|
gen.MarkLabel(labelContinueValue);
|
||
|
}
|
||
|
}
|
||
|
gen.Emit(OpCodes.Dup);
|
||
|
gen.Emit(OpCodes.Brfalse_S, labelInvalidValue); // (arg1 is Inst)
|
||
|
|
||
|
if (elementType.IsValueType)
|
||
|
{
|
||
|
gen.Emit(OpCodes.Unbox_Any, elementType); // ((PropertyType)value)
|
||
|
|
||
|
if (elementType != realType)
|
||
|
{ // new Nullable<PropertyType>
|
||
|
gen.Emit(OpCodes.Newobj, realType.GetConstructor(new Type[] { elementType }));
|
||
|
}
|
||
|
}
|
||
|
gen.MarkLabel(labelContinueNull);
|
||
|
gen.Emit(mi.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, mi); // .Property =
|
||
|
gen.Emit(OpCodes.Ret);
|
||
|
|
||
|
// ThrowInvalidValue(value, typeof(PropertyType), DeclaringType.Name, PropertyName
|
||
|
gen.MarkLabel(labelInvalidValue);
|
||
|
gen.Emit(OpCodes.Pop); // pop Ldarg_0
|
||
|
gen.Emit(OpCodes.Pop); // pop IsInst'
|
||
|
gen.Emit(OpCodes.Ldarg_1); // determine if InvalidCast or NullReference
|
||
|
gen.Emit(OpCodes.Ldtoken, elementType);
|
||
|
gen.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public));
|
||
|
gen.Emit(OpCodes.Ldstr, mi.DeclaringType.Name);
|
||
|
gen.Emit(OpCodes.Ldstr, mi.Name.Substring(4)); // substring to strip "set_"
|
||
|
Debug.Assert(null != (Action<Object,Type,String,String>)EntityUtil.ThrowSetInvalidValue, "missing method ThrowSetInvalidValue(object,Type,string,string)");
|
||
|
gen.Emit(OpCodes.Call, typeof(EntityUtil).GetMethod("ThrowSetInvalidValue", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[] { typeof(object),typeof(Type),typeof(string),typeof(string)},null));
|
||
|
gen.Emit(OpCodes.Ret);
|
||
|
return (Action<object, object>)method.CreateDelegate(typeof(Action<object, object>));
|
||
|
}
|
||
|
|
||
|
internal static void ValidateSetterProperty(RuntimeTypeHandle entityDeclaringType, RuntimeMethodHandle setterMethodHandle, out MethodInfo setterMethodInfo, out Type realType)
|
||
|
{
|
||
|
if (default(RuntimeMethodHandle).Equals(setterMethodHandle))
|
||
|
{
|
||
|
ThrowPropertyNoSetter();
|
||
|
}
|
||
|
|
||
|
Debug.Assert(!default(RuntimeTypeHandle).Equals(entityDeclaringType), "Type handle of entity should always be known.");
|
||
|
setterMethodInfo = (MethodInfo)MethodBase.GetMethodFromHandle(setterMethodHandle, entityDeclaringType);
|
||
|
|
||
|
if (setterMethodInfo.IsStatic)
|
||
|
{
|
||
|
ThrowPropertyIsStatic();
|
||
|
}
|
||
|
if (setterMethodInfo.DeclaringType.IsValueType)
|
||
|
{
|
||
|
ThrowPropertyDeclaringTypeIsValueType();
|
||
|
}
|
||
|
|
||
|
ParameterInfo[] parameters = setterMethodInfo.GetParameters();
|
||
|
if ((null == parameters) || (1 != parameters.Length))
|
||
|
{ // if no parameters (i.e. not a set_Property method), will still throw this message
|
||
|
ThrowPropertyIsIndexed();
|
||
|
}
|
||
|
realType = setterMethodInfo.ReturnType;
|
||
|
if ((null != realType) && (typeof(void) != realType))
|
||
|
{
|
||
|
ThrowPropertyUnsupportedForm();
|
||
|
}
|
||
|
|
||
|
realType = parameters[0].ParameterType;
|
||
|
if (realType.IsPointer)
|
||
|
{
|
||
|
ThrowPropertyUnsupportedType();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>Determines if the specified method requires permission demands to be invoked safely.</summary>
|
||
|
/// <param name="mi">Method instance to check.</param>
|
||
|
/// <returns>true if the specified method requires permission demands to be invoked safely, false otherwise.</returns>
|
||
|
internal static bool RequiresPermissionDemands(MethodBase mi)
|
||
|
{
|
||
|
System.Diagnostics.Debug.Assert(mi != null);
|
||
|
return !IsPublic(mi);
|
||
|
}
|
||
|
|
||
|
private static void GenerateNecessaryPermissionDemands(ILGenerator gen, MethodBase mi)
|
||
|
{
|
||
|
if (!IsPublic(mi))
|
||
|
{
|
||
|
gen.Emit(OpCodes.Ldsfld, typeof(LightweightCodeGenerator).GetField("MemberAccessReflectionPermission", BindingFlags.Static | BindingFlags.NonPublic));
|
||
|
gen.Emit(OpCodes.Callvirt, typeof(ReflectionPermission).GetMethod("Demand"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static bool IsPublic(MethodBase method)
|
||
|
{
|
||
|
return (method.IsPublic && IsPublic(method.DeclaringType));
|
||
|
}
|
||
|
|
||
|
internal static bool IsPublic(Type type)
|
||
|
{
|
||
|
return ((null == type) || (type.IsPublic && IsPublic(type.DeclaringType)));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Create delegate used to invoke either the GetRelatedReference or GetRelatedCollection generic method on the RelationshipManager.
|
||
|
/// </summary>
|
||
|
/// <param name="sourceMember">source end of the relationship for the requested navigation</param>
|
||
|
/// <param name="targetMember">target end of the relationship for the requested navigation</param>
|
||
|
/// <returns>Delegate that can be used to invoke the corresponding method.</returns>
|
||
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||
|
private static Func<RelationshipManager, RelatedEnd, RelatedEnd> CreateGetRelatedEndMethod(AssociationEndMember sourceMember, AssociationEndMember targetMember)
|
||
|
{
|
||
|
Debug.Assert(sourceMember.DeclaringType == targetMember.DeclaringType, "Source and Target members must be in the same DeclaringType");
|
||
|
|
||
|
EntityType sourceEntityType = MetadataHelper.GetEntityTypeForEnd(sourceMember);
|
||
|
EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd(targetMember);
|
||
|
NavigationPropertyAccessor sourceAccessor = MetadataHelper.GetNavigationPropertyAccessor(targetEntityType, targetMember, sourceMember);
|
||
|
NavigationPropertyAccessor targetAccessor = MetadataHelper.GetNavigationPropertyAccessor(sourceEntityType, sourceMember, targetMember);
|
||
|
|
||
|
MethodInfo genericCreateRelatedEndMethod = typeof(LightweightCodeGenerator).GetMethod("CreateGetRelatedEndMethod", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(AssociationEndMember), typeof(AssociationEndMember), typeof(NavigationPropertyAccessor), typeof(NavigationPropertyAccessor) }, null);
|
||
|
Debug.Assert(genericCreateRelatedEndMethod != null, "Could not find method LightweightCodeGenerator.CreateGetRelatedEndMethod");
|
||
|
|
||
|
MethodInfo createRelatedEndMethod = genericCreateRelatedEndMethod.MakeGenericMethod(sourceEntityType.ClrType, targetEntityType.ClrType);
|
||
|
object getRelatedEndDelegate = createRelatedEndMethod.Invoke(null, new object[] { sourceMember, targetMember, sourceAccessor, targetAccessor });
|
||
|
|
||
|
return (Func<RelationshipManager, RelatedEnd, RelatedEnd>)getRelatedEndDelegate;
|
||
|
}
|
||
|
|
||
|
private static Func<RelationshipManager, RelatedEnd, RelatedEnd> CreateGetRelatedEndMethod<TSource, TTarget>(AssociationEndMember sourceMember, AssociationEndMember targetMember, NavigationPropertyAccessor sourceAccessor, NavigationPropertyAccessor targetAccessor)
|
||
|
where TSource : class
|
||
|
where TTarget : class
|
||
|
{
|
||
|
Func<RelationshipManager, RelatedEnd, RelatedEnd> getRelatedEnd;
|
||
|
|
||
|
// Get the appropriate method, either collection or reference depending on the target multiplicity
|
||
|
switch (targetMember.RelationshipMultiplicity)
|
||
|
{
|
||
|
case RelationshipMultiplicity.ZeroOrOne:
|
||
|
case RelationshipMultiplicity.One:
|
||
|
{
|
||
|
getRelatedEnd = (manager, relatedEnd) =>
|
||
|
manager.GetRelatedReference<TSource, TTarget>(sourceMember.DeclaringType.FullName,
|
||
|
sourceMember.Name,
|
||
|
targetMember.Name,
|
||
|
sourceAccessor,
|
||
|
targetAccessor,
|
||
|
sourceMember.RelationshipMultiplicity,
|
||
|
relatedEnd);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case RelationshipMultiplicity.Many:
|
||
|
{
|
||
|
getRelatedEnd = (manager, relatedEnd) =>
|
||
|
manager.GetRelatedCollection<TSource, TTarget>(sourceMember.DeclaringType.FullName,
|
||
|
sourceMember.Name,
|
||
|
targetMember.Name,
|
||
|
sourceAccessor,
|
||
|
targetAccessor,
|
||
|
sourceMember.RelationshipMultiplicity,
|
||
|
relatedEnd);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
throw EntityUtil.InvalidEnumerationValue(typeof(RelationshipMultiplicity), (int)targetMember.RelationshipMultiplicity);
|
||
|
}
|
||
|
|
||
|
return getRelatedEnd;
|
||
|
}
|
||
|
|
||
|
private static void ThrowConstructorNoParameterless(Type type)
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_ConstructorNoParameterless(type.FullName));
|
||
|
}
|
||
|
private static void ThrowPropertyDeclaringTypeIsValueType()
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyDeclaringTypeIsValueType);
|
||
|
}
|
||
|
private static void ThrowPropertyUnsupportedForm()
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyUnsupportedForm);
|
||
|
}
|
||
|
private static void ThrowPropertyUnsupportedType()
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyUnsupportedType);
|
||
|
}
|
||
|
private static void ThrowPropertyStrongNameIdentity()
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyStrongNameIdentity);
|
||
|
}
|
||
|
private static void ThrowPropertyIsIndexed()
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyIsIndexed);
|
||
|
}
|
||
|
private static void ThrowPropertyIsStatic()
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyIsStatic);
|
||
|
}
|
||
|
private static void ThrowPropertyNoGetter()
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyNoGetter);
|
||
|
}
|
||
|
private static void ThrowPropertyNoSetter()
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_PropertyNoSetter);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Lightweight code generation
|
||
|
|
||
|
internal static readonly ReflectionPermission MemberAccessReflectionPermission = new ReflectionPermission(ReflectionPermissionFlag.MemberAccess);
|
||
|
|
||
|
internal static bool HasMemberAccessReflectionPermission()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
MemberAccessReflectionPermission.Demand();
|
||
|
return true;
|
||
|
}
|
||
|
catch (SecurityException)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// we could cache more, like 'new Type[] { ... }' and 'typeof(object)'
|
||
|
// but pruned as much as possible for the workingset helps, even little things
|
||
|
|
||
|
// Assert MemberAccess to skip visibility check & ReflectionEmit so we can generate the method (make calls to EF internals).
|
||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2128")]
|
||
|
[System.Security.SecuritySafeCritical]
|
||
|
[ReflectionPermission(SecurityAction.Assert, MemberAccess = true)]
|
||
|
internal static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes)
|
||
|
{
|
||
|
// Create a transparent dynamic method (Module not specified) to ensure we do not satisfy any link demands
|
||
|
// in method callees.
|
||
|
return new DynamicMethod(name, returnType, parameterTypes, true);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|