Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataEntity / System / Data / Metadata / Edm / LightweightCodeGenerator.cs / 3 / LightweightCodeGenerator.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
namespace System.Data.Objects
{
using System;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Security;
using System.Security.Permissions;
using System.Data.Objects.DataClasses;
using System.Data.Common.Utils;
using System.Linq;
///
/// CodeGenerator class: use lightweight code gen to dynamically generate code to get/set properties.
///
internal static class LightweightCodeGenerator
{
/// For an OSpace ComplexType or EntityType, returns the delegate to construct the clr instance.
internal static Delegate GetConstructorDelegateForType(EdmType clrType)
{
return (Helper.IsEntityType(clrType)
? GetConstructorDelegateForType((ClrEntityType)clrType)
: GetConstructorDelegateForType((ClrComplexType)clrType));
}
/// For an OSpace ComplexType or EntityType, returns the delegate to construct the clr instance.
internal static Delegate GetConstructorDelegateForType(ClrEntityType clrType)
{
return (clrType.Constructor ?? (clrType.Constructor = CreateEntityConstructor(clrType.ClrType)));
}
internal static Delegate GetConstructorDelegateForType(ClrComplexType clrType)
{
return (clrType.Constructor ?? (clrType.Constructor = CreateConstructor(clrType.ClrType)));
}
/// for an OSpace property, get the property value from a clr instance
internal static object GetValue(NavigationProperty property, object target)
{
Func getter = property.ValueGetter;
if (null == getter)
{
getter = CreatePropertyGetter(property.PropertyGetterHandle);
property.ValueGetter = getter;
}
Debug.Assert(null != getter, "null getter");
//Not tracing every property get
//EntityBid.Trace(" Name='%ls'\n", property.Name);
return getter(target);
}
/// for an OSpace property, get the property value from a clr instance
internal static object GetValue(EdmProperty property, object target)
{
Func getter = GetGetterDelegateForProperty(property);
Debug.Assert(null != getter, "null getter");
//Not tracing every property get
//EntityBid.Trace(" Name='%ls'\n", property.Name);
return getter(target);
}
internal static Func GetGetterDelegateForProperty(EdmProperty property)
{
return property.ValueGetter ?? (property.ValueGetter = CreatePropertyGetter(property.PropertyGetterHandle));
}
/// for an OSpace property, set the property value on a clr instance
///
/// If is null for a non nullable property.
///
///
/// Invalid cast of to property type.
///
///
/// From generated enties via StructuralObject.SetValidValue.
///
///
/// If the property setter is not public or declaring class is not public.
///
///
/// Demand for FullTrust if the property setter or declaring class has a
///
internal static void SetValue(EdmProperty property, object target, object value)
{
Action setter = GetSetterDelegateForProperty(property);
if (Bid.TraceOn)
{
EntityBid.Trace(" Name='%ls'\n", property.Name);
}
setter(target, value);
}
/// For an OSpace property, gets the delegate to set the property value on a clr instance.
internal static Action GetSetterDelegateForProperty(EdmProperty property)
{
Action setter = property.ValueSetter;
if (null == setter)
{
setter = CreatePropertySetter(property.PropertySetterHandle,
property.Nullable);
property.ValueSetter = setter;
}
Debug.Assert(null != setter, "null setter");
return setter;
}
///
/// Gets the related end instance for the source AssociationEndMember by creating a DynamicMethod to
/// call GetRelatedCollection or GetRelatedReference
///
internal static IRelatedEnd GetRelatedEnd(RelationshipManager sourceRelationshipManager, AssociationEndMember sourceMember, AssociationEndMember targetMember, RelatedEnd existingRelatedEnd)
{
RelationshipManager.GetRelatedEndMethod getRelatedEnd = sourceMember.GetRelatedEnd as RelationshipManager.GetRelatedEndMethod;
if (null == getRelatedEnd)
{
getRelatedEnd = CreateGetRelatedEndMethod(sourceMember, targetMember, existingRelatedEnd);
sourceMember.GetRelatedEnd = getRelatedEnd;
}
Debug.Assert(null != getRelatedEnd, "null getRelatedEnd");
//Not tracing every property get
//EntityBid.Trace(" Name='%ls'\n", property.Name);
return getRelatedEnd(sourceRelationshipManager, existingRelatedEnd);
}
#region get the delegate
/// Gets a parameterless constructor for the specified type.
/// Type to get constructor for.
/// Parameterless constructor for the specified type.
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();
}
return ci;
}
///
/// generate a delegate equivalent to
/// private object Constructor() { return new XClass(); }
///
private 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));
}
///
/// generate a delegate equivalent to
/// private object Constructor(EntityKey, ObjectContext, EntitySet, MergeOption) {
/// object value = new XClass();
///
/// if (null != entityKey) {
/// #if XClass implements IEntityKey
/// ((IEntityWithKey)this).EntityKey = entityKey;
/// #endif
///
/// #if XClass implements IEntityWithRelationships
/// RelationshipManager manager = ((IEntityWithRelationships)this).RelationshipManager;
/// if (null != manager) { manager.AttachContext(ObjectContext, EntitySet, MergeOption); }
/// }
///
/// return value;
/// return new XClass(); }
///
private static Delegate CreateEntityConstructor(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), new Type[] { typeof(EntityKey), typeof(ObjectContext), typeof(EntitySet), typeof(MergeOption) });
ILGenerator gen = method.GetILGenerator();
GenerateNecessaryPermissionDemands(gen, ci);
gen.Emit(OpCodes.Newobj, ci);
bool withKey = typeof(IEntityWithKey).IsAssignableFrom(type);
bool withRel = typeof(IEntityWithRelationships).IsAssignableFrom(type);
if (withKey || withRel)
{
Label nullEntityKey = gen.DefineLabel();
gen.Emit(OpCodes.Ldarg_0); // EntityKey
gen.Emit(OpCodes.Brfalse_S, nullEntityKey); // null EntityKey
if (withKey)
{
gen.Emit(OpCodes.Dup); // the Newobj
gen.Emit(OpCodes.Castclass, typeof(IEntityWithKey));
gen.Emit(OpCodes.Ldarg_0); // EntityKey
gen.Emit(OpCodes.Callvirt, typeof(IEntityWithKey).GetMethod("set_EntityKey", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(EntityKey) }, null));
}
// The only reason we would call RelationshipManager.AttachContext would be to enable loading related entities,
// but if there’s no entitykey (aka NoEntitySetKey), then there’s no way that should work.
// So it’s reasonable not to call RelationshipManager.AttachContext unless there is a valid EntityKey
if (withRel)
{
Label nullContext = gen.DefineLabel();
gen.Emit(OpCodes.Ldarg_1); // ObjectContext
gen.Emit(OpCodes.Brfalse_S, nullContext); // null context, don't AttachContext
gen.Emit(OpCodes.Dup); // the Newobj
gen.Emit(OpCodes.Castclass, typeof(IEntityWithRelationships));
gen.Emit(OpCodes.Ldarg_1); // ObjectContext
gen.Emit(OpCodes.Ldarg_2); // EntitySet
gen.Emit(OpCodes.Ldarg_3); // MergeOption
Debug.Assert(null != (Action)EntityUtil.AttachContext, "missing method AttachContext(IEntityWithRelationships, ObjectContext, EntitySet, MergeOption)");
gen.Emit(OpCodes.Call, typeof(EntityUtil).GetMethod("AttachContext", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(IEntityWithRelationships), typeof(ObjectContext), typeof(EntitySet), typeof(MergeOption) }, null));
gen.MarkLabel(nullContext);
}
gen.MarkLabel(nullEntityKey);
}
gen.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Func));
}
///
/// 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 y = target.PropertyX; return ((y.HasValue) ? y.Value : null); }
///
private static Func CreatePropertyGetter(RuntimeMethodHandle rmh)
{
MethodInfo mi = ((IntPtr.Zero != rmh.Value) ? (MethodInfo)MethodBase.GetMethodFromHandle(rmh) : null);
if (null == mi)
{
ThrowPropertyNoGetter();
}
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)method.CreateDelegate(typeof(Func));
}
///
/// 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
/// }
///
///
/// 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 .
///
private static Action CreatePropertySetter(RuntimeMethodHandle rmh, bool allowNull)
{
MethodInfo mi;
Type realType;
ValidateSetterProperty(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
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)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)method.CreateDelegate(typeof(Action));
}
internal static void ValidateSetterProperty(RuntimeMethodHandle setterMethodHandle, out MethodInfo setterMethodInfo, out Type realType)
{
setterMethodInfo = ((IntPtr.Zero != setterMethodHandle.Value) ? (MethodInfo)MethodBase.GetMethodFromHandle(setterMethodHandle) : null);
if (null == setterMethodInfo)
{
ThrowPropertyNoSetter();
}
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();
}
}
/// Determines if the specified method requires permission demands to be invoked safely.
/// Method instance to check.
/// true if the specified method requires permission demands to be invoked safely, false otherwise.
internal static bool RequiresPermissionDemands(MethodBase mi)
{
System.Diagnostics.Debug.Assert(mi != null);
return !IsPublic(mi) || HasLinkDemand(mi);
}
// Future Enhancement: resolve problem with StrongNameIdentityPermission, demands that only allow specific assemblies to call
//
private static void GenerateNecessaryPermissionDemands(ILGenerator gen, MethodBase mi)
{
if (HasLinkDemand(mi))
{ // keep this here, but we are otherwise working around LinkDemands to by using Reflection instead of the DynamicMethod
gen.Emit(OpCodes.Ldsfld, typeof(LightweightCodeGenerator).GetField("FullTrustPermission", BindingFlags.Static | BindingFlags.NonPublic));
gen.Emit(OpCodes.Callvirt, typeof(NamedPermissionSet).GetMethod("Demand"));
}
else 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 HasLinkDemand(MemberInfo info)
{
// we only need link demands directly on the method or class that we invoke
// not the link demands on base classes, derived classes, overriden methods ...
bool result = false;
foreach (SecurityAttribute attribute in info.GetCustomAttributes(typeof(SecurityAttribute), false).Concat(
info.DeclaringType.GetCustomAttributes(typeof(SecurityAttribute), false)))
{
if (attribute is StrongNameIdentityPermissionAttribute)
{
// With StrongNameIdentityPermission, the caller is System.Data.Entity and needs
// to be validated for actual calling assembly.
ThrowPropertyStrongNameIdentity();
}
if (SecurityAction.LinkDemand == attribute.Action)
{
result = true;
// do not break or return here, must search all SecurityAttribute for StrongNameIdentityPermission
}
}
return result;
}
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)));
}
///
/// Generates a non-generic type version of GetRelatedEnd
/// Create the code:
/// IRelatedEnd GetRelatedEnd(RelationshipManager mgr, RelatedEnd existingRelatedEnd)
/// {
/// return mgr.GetRelated{Reference|Collection}(of sourceType, targetType)(sourceMember.DeclaringType.FullName,
/// sourceMember.Name,
/// targetMember.Name,
/// sourceMember.RelationshipMultiplicity,
/// existingRelatedEnd);
/// }
///
/// source end of the relationship for the requested navigation
/// target end of the relationship for the requested navigation
/// Delegate that can be used to call the generated method
private static RelationshipManager.GetRelatedEndMethod CreateGetRelatedEndMethod(AssociationEndMember sourceMember, AssociationEndMember targetMember, RelatedEnd existingRelatedEnd)
{
Debug.Assert(sourceMember.DeclaringType == targetMember.DeclaringType, "Source and Target members must be in the same DeclaringType");
EntityType sourceEntityType = MetadataHelper.GetEntityTypeForEnd(sourceMember);
Debug.Assert(sourceEntityType.DataSpace == DataSpace.OSpace && sourceEntityType.ClrType != null, "sourceEntityType must contain an ospace type");
Type sourceType = sourceEntityType.ClrType;
EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd(targetMember);
Debug.Assert(targetEntityType.DataSpace == DataSpace.OSpace && targetEntityType.ClrType != null, "targetEntityType must contain an ospace type");
Type targetType = targetEntityType.ClrType;
// Get the appropriate method, either collection or reference depending on the target multiplicity
MethodInfo getRelatedItem = null;
switch (targetMember.RelationshipMultiplicity)
{
case RelationshipMultiplicity.ZeroOrOne:
case RelationshipMultiplicity.One:
{
// reference
MethodInfo getRelatedReferenceGeneric = typeof(RelationshipManager).GetMethod("GetRelatedReference", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string), typeof(string), typeof(RelationshipMultiplicity), typeof(RelatedEnd) }, null);
if (getRelatedReferenceGeneric == null)
{
throw EntityUtil.MissingMethod("GetRelatedReference");
}
getRelatedItem = getRelatedReferenceGeneric.MakeGenericMethod(sourceType, targetType);
break;
}
case RelationshipMultiplicity.Many:
{
// collection
MethodInfo getRelatedCollectionGeneric = typeof(RelationshipManager).GetMethod("GetRelatedCollection", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string), typeof(string), typeof(RelationshipMultiplicity), typeof(RelatedEnd) }, null);
if (getRelatedCollectionGeneric == null)
{
throw EntityUtil.MissingMethod("GetRelatedCollection");
}
getRelatedItem = getRelatedCollectionGeneric.MakeGenericMethod(sourceType, targetType);
break;
}
default:
throw EntityUtil.InvalidEnumerationValue(typeof(RelationshipMultiplicity), (int)targetMember.RelationshipMultiplicity);
}
DynamicMethod method = CreateDynamicMethod("GetRelatedEnd", typeof(IRelatedEnd), new Type[] { typeof(RelationshipManager), typeof(RelatedEnd)});
ILGenerator generator = method.GetILGenerator();
Debug.Assert(getRelatedItem.IsPrivate && !getRelatedItem.IsVirtual && (typeof(RelationshipManager) == getRelatedItem.DeclaringType), "Unexpected characteristics for GetRelatedCollection or GetRelatedReference");
// not generating reflection permission demand for calling RelationshipManager GetRelatedReference or GetRelatedCollection private non-interface methods
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Castclass, getRelatedItem.DeclaringType);
generator.Emit(OpCodes.Ldstr, sourceMember.DeclaringType.FullName);
generator.Emit(OpCodes.Ldstr, sourceMember.Name);
generator.Emit(OpCodes.Ldstr, targetMember.Name);
switch (sourceMember.RelationshipMultiplicity)
{
case RelationshipMultiplicity.ZeroOrOne:
generator.Emit(OpCodes.Ldc_I4_0); break;
case RelationshipMultiplicity.One: generator.Emit(OpCodes.Ldc_I4_1); break;
case RelationshipMultiplicity.Many: generator.Emit(OpCodes.Ldc_I4_2); break;
default:
throw EntityUtil.InvalidEnumerationValue(typeof(RelationshipMultiplicity), (int)sourceMember.RelationshipMultiplicity);
}
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Call, getRelatedItem);
generator.Emit(OpCodes.Ret);
RelationshipManager.GetRelatedEndMethod getRelatedEndMethod = (RelationshipManager.GetRelatedEndMethod)method.CreateDelegate(typeof(RelationshipManager.GetRelatedEndMethod));
return getRelatedEndMethod;
}
private static void ThrowConstructorNoParameterless()
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_ConstructorNoParameterless);
}
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 readonly NamedPermissionSet FullTrustPermission = new NamedPermissionSet("FullTrust");
// 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 visbility check & ReflectionEmit so we can generate the method
[System.Security.SecurityCritical]
[System.Security.SecurityTreatAsSafe]
[ReflectionPermission(SecurityAction.Assert, MemberAccess = true, ReflectionEmit = true)]
private static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes)
{
// Consider: write unit test to verify assumptions for why we don't use security transparent dynamic method (new in orcas)
// expecation is the security for the transparent jitted method is fixed on for the fist caller
// and we reuse the delegate for all callers
return new DynamicMethod(name, returnType, parameterTypes, typeof(LightweightCodeGenerator).Module, true);
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
namespace System.Data.Objects
{
using System;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Security;
using System.Security.Permissions;
using System.Data.Objects.DataClasses;
using System.Data.Common.Utils;
using System.Linq;
///
/// CodeGenerator class: use lightweight code gen to dynamically generate code to get/set properties.
///
internal static class LightweightCodeGenerator
{
/// For an OSpace ComplexType or EntityType, returns the delegate to construct the clr instance.
internal static Delegate GetConstructorDelegateForType(EdmType clrType)
{
return (Helper.IsEntityType(clrType)
? GetConstructorDelegateForType((ClrEntityType)clrType)
: GetConstructorDelegateForType((ClrComplexType)clrType));
}
/// For an OSpace ComplexType or EntityType, returns the delegate to construct the clr instance.
internal static Delegate GetConstructorDelegateForType(ClrEntityType clrType)
{
return (clrType.Constructor ?? (clrType.Constructor = CreateEntityConstructor(clrType.ClrType)));
}
internal static Delegate GetConstructorDelegateForType(ClrComplexType clrType)
{
return (clrType.Constructor ?? (clrType.Constructor = CreateConstructor(clrType.ClrType)));
}
/// for an OSpace property, get the property value from a clr instance
internal static object GetValue(NavigationProperty property, object target)
{
Func getter = property.ValueGetter;
if (null == getter)
{
getter = CreatePropertyGetter(property.PropertyGetterHandle);
property.ValueGetter = getter;
}
Debug.Assert(null != getter, "null getter");
//Not tracing every property get
//EntityBid.Trace(" Name='%ls'\n", property.Name);
return getter(target);
}
/// for an OSpace property, get the property value from a clr instance
internal static object GetValue(EdmProperty property, object target)
{
Func getter = GetGetterDelegateForProperty(property);
Debug.Assert(null != getter, "null getter");
//Not tracing every property get
//EntityBid.Trace(" Name='%ls'\n", property.Name);
return getter(target);
}
internal static Func GetGetterDelegateForProperty(EdmProperty property)
{
return property.ValueGetter ?? (property.ValueGetter = CreatePropertyGetter(property.PropertyGetterHandle));
}
/// for an OSpace property, set the property value on a clr instance
///
/// If is null for a non nullable property.
///
///
/// Invalid cast of to property type.
///
///
/// From generated enties via StructuralObject.SetValidValue.
///
///
/// If the property setter is not public or declaring class is not public.
///
///
/// Demand for FullTrust if the property setter or declaring class has a
///
internal static void SetValue(EdmProperty property, object target, object value)
{
Action setter = GetSetterDelegateForProperty(property);
if (Bid.TraceOn)
{
EntityBid.Trace(" Name='%ls'\n", property.Name);
}
setter(target, value);
}
/// For an OSpace property, gets the delegate to set the property value on a clr instance.
internal static Action GetSetterDelegateForProperty(EdmProperty property)
{
Action setter = property.ValueSetter;
if (null == setter)
{
setter = CreatePropertySetter(property.PropertySetterHandle,
property.Nullable);
property.ValueSetter = setter;
}
Debug.Assert(null != setter, "null setter");
return setter;
}
///
/// Gets the related end instance for the source AssociationEndMember by creating a DynamicMethod to
/// call GetRelatedCollection or GetRelatedReference
///
internal static IRelatedEnd GetRelatedEnd(RelationshipManager sourceRelationshipManager, AssociationEndMember sourceMember, AssociationEndMember targetMember, RelatedEnd existingRelatedEnd)
{
RelationshipManager.GetRelatedEndMethod getRelatedEnd = sourceMember.GetRelatedEnd as RelationshipManager.GetRelatedEndMethod;
if (null == getRelatedEnd)
{
getRelatedEnd = CreateGetRelatedEndMethod(sourceMember, targetMember, existingRelatedEnd);
sourceMember.GetRelatedEnd = getRelatedEnd;
}
Debug.Assert(null != getRelatedEnd, "null getRelatedEnd");
//Not tracing every property get
//EntityBid.Trace(" Name='%ls'\n", property.Name);
return getRelatedEnd(sourceRelationshipManager, existingRelatedEnd);
}
#region get the delegate
/// Gets a parameterless constructor for the specified type.
/// Type to get constructor for.
/// Parameterless constructor for the specified type.
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();
}
return ci;
}
///
/// generate a delegate equivalent to
/// private object Constructor() { return new XClass(); }
///
private 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));
}
///
/// generate a delegate equivalent to
/// private object Constructor(EntityKey, ObjectContext, EntitySet, MergeOption) {
/// object value = new XClass();
///
/// if (null != entityKey) {
/// #if XClass implements IEntityKey
/// ((IEntityWithKey)this).EntityKey = entityKey;
/// #endif
///
/// #if XClass implements IEntityWithRelationships
/// RelationshipManager manager = ((IEntityWithRelationships)this).RelationshipManager;
/// if (null != manager) { manager.AttachContext(ObjectContext, EntitySet, MergeOption); }
/// }
///
/// return value;
/// return new XClass(); }
///
private static Delegate CreateEntityConstructor(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), new Type[] { typeof(EntityKey), typeof(ObjectContext), typeof(EntitySet), typeof(MergeOption) });
ILGenerator gen = method.GetILGenerator();
GenerateNecessaryPermissionDemands(gen, ci);
gen.Emit(OpCodes.Newobj, ci);
bool withKey = typeof(IEntityWithKey).IsAssignableFrom(type);
bool withRel = typeof(IEntityWithRelationships).IsAssignableFrom(type);
if (withKey || withRel)
{
Label nullEntityKey = gen.DefineLabel();
gen.Emit(OpCodes.Ldarg_0); // EntityKey
gen.Emit(OpCodes.Brfalse_S, nullEntityKey); // null EntityKey
if (withKey)
{
gen.Emit(OpCodes.Dup); // the Newobj
gen.Emit(OpCodes.Castclass, typeof(IEntityWithKey));
gen.Emit(OpCodes.Ldarg_0); // EntityKey
gen.Emit(OpCodes.Callvirt, typeof(IEntityWithKey).GetMethod("set_EntityKey", BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(EntityKey) }, null));
}
// The only reason we would call RelationshipManager.AttachContext would be to enable loading related entities,
// but if there’s no entitykey (aka NoEntitySetKey), then there’s no way that should work.
// So it’s reasonable not to call RelationshipManager.AttachContext unless there is a valid EntityKey
if (withRel)
{
Label nullContext = gen.DefineLabel();
gen.Emit(OpCodes.Ldarg_1); // ObjectContext
gen.Emit(OpCodes.Brfalse_S, nullContext); // null context, don't AttachContext
gen.Emit(OpCodes.Dup); // the Newobj
gen.Emit(OpCodes.Castclass, typeof(IEntityWithRelationships));
gen.Emit(OpCodes.Ldarg_1); // ObjectContext
gen.Emit(OpCodes.Ldarg_2); // EntitySet
gen.Emit(OpCodes.Ldarg_3); // MergeOption
Debug.Assert(null != (Action)EntityUtil.AttachContext, "missing method AttachContext(IEntityWithRelationships, ObjectContext, EntitySet, MergeOption)");
gen.Emit(OpCodes.Call, typeof(EntityUtil).GetMethod("AttachContext", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(IEntityWithRelationships), typeof(ObjectContext), typeof(EntitySet), typeof(MergeOption) }, null));
gen.MarkLabel(nullContext);
}
gen.MarkLabel(nullEntityKey);
}
gen.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Func));
}
///
/// 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 y = target.PropertyX; return ((y.HasValue) ? y.Value : null); }
///
private static Func CreatePropertyGetter(RuntimeMethodHandle rmh)
{
MethodInfo mi = ((IntPtr.Zero != rmh.Value) ? (MethodInfo)MethodBase.GetMethodFromHandle(rmh) : null);
if (null == mi)
{
ThrowPropertyNoGetter();
}
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)method.CreateDelegate(typeof(Func));
}
///
/// 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
/// }
///
///
/// 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 .
///
private static Action CreatePropertySetter(RuntimeMethodHandle rmh, bool allowNull)
{
MethodInfo mi;
Type realType;
ValidateSetterProperty(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
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)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)method.CreateDelegate(typeof(Action));
}
internal static void ValidateSetterProperty(RuntimeMethodHandle setterMethodHandle, out MethodInfo setterMethodInfo, out Type realType)
{
setterMethodInfo = ((IntPtr.Zero != setterMethodHandle.Value) ? (MethodInfo)MethodBase.GetMethodFromHandle(setterMethodHandle) : null);
if (null == setterMethodInfo)
{
ThrowPropertyNoSetter();
}
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();
}
}
/// Determines if the specified method requires permission demands to be invoked safely.
/// Method instance to check.
/// true if the specified method requires permission demands to be invoked safely, false otherwise.
internal static bool RequiresPermissionDemands(MethodBase mi)
{
System.Diagnostics.Debug.Assert(mi != null);
return !IsPublic(mi) || HasLinkDemand(mi);
}
// Future Enhancement: resolve problem with StrongNameIdentityPermission, demands that only allow specific assemblies to call
//
private static void GenerateNecessaryPermissionDemands(ILGenerator gen, MethodBase mi)
{
if (HasLinkDemand(mi))
{ // keep this here, but we are otherwise working around LinkDemands to by using Reflection instead of the DynamicMethod
gen.Emit(OpCodes.Ldsfld, typeof(LightweightCodeGenerator).GetField("FullTrustPermission", BindingFlags.Static | BindingFlags.NonPublic));
gen.Emit(OpCodes.Callvirt, typeof(NamedPermissionSet).GetMethod("Demand"));
}
else 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 HasLinkDemand(MemberInfo info)
{
// we only need link demands directly on the method or class that we invoke
// not the link demands on base classes, derived classes, overriden methods ...
bool result = false;
foreach (SecurityAttribute attribute in info.GetCustomAttributes(typeof(SecurityAttribute), false).Concat(
info.DeclaringType.GetCustomAttributes(typeof(SecurityAttribute), false)))
{
if (attribute is StrongNameIdentityPermissionAttribute)
{
// With StrongNameIdentityPermission, the caller is System.Data.Entity and needs
// to be validated for actual calling assembly.
ThrowPropertyStrongNameIdentity();
}
if (SecurityAction.LinkDemand == attribute.Action)
{
result = true;
// do not break or return here, must search all SecurityAttribute for StrongNameIdentityPermission
}
}
return result;
}
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)));
}
///
/// Generates a non-generic type version of GetRelatedEnd
/// Create the code:
/// IRelatedEnd GetRelatedEnd(RelationshipManager mgr, RelatedEnd existingRelatedEnd)
/// {
/// return mgr.GetRelated{Reference|Collection}(of sourceType, targetType)(sourceMember.DeclaringType.FullName,
/// sourceMember.Name,
/// targetMember.Name,
/// sourceMember.RelationshipMultiplicity,
/// existingRelatedEnd);
/// }
///
/// source end of the relationship for the requested navigation
/// target end of the relationship for the requested navigation
/// Delegate that can be used to call the generated method
private static RelationshipManager.GetRelatedEndMethod CreateGetRelatedEndMethod(AssociationEndMember sourceMember, AssociationEndMember targetMember, RelatedEnd existingRelatedEnd)
{
Debug.Assert(sourceMember.DeclaringType == targetMember.DeclaringType, "Source and Target members must be in the same DeclaringType");
EntityType sourceEntityType = MetadataHelper.GetEntityTypeForEnd(sourceMember);
Debug.Assert(sourceEntityType.DataSpace == DataSpace.OSpace && sourceEntityType.ClrType != null, "sourceEntityType must contain an ospace type");
Type sourceType = sourceEntityType.ClrType;
EntityType targetEntityType = MetadataHelper.GetEntityTypeForEnd(targetMember);
Debug.Assert(targetEntityType.DataSpace == DataSpace.OSpace && targetEntityType.ClrType != null, "targetEntityType must contain an ospace type");
Type targetType = targetEntityType.ClrType;
// Get the appropriate method, either collection or reference depending on the target multiplicity
MethodInfo getRelatedItem = null;
switch (targetMember.RelationshipMultiplicity)
{
case RelationshipMultiplicity.ZeroOrOne:
case RelationshipMultiplicity.One:
{
// reference
MethodInfo getRelatedReferenceGeneric = typeof(RelationshipManager).GetMethod("GetRelatedReference", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string), typeof(string), typeof(RelationshipMultiplicity), typeof(RelatedEnd) }, null);
if (getRelatedReferenceGeneric == null)
{
throw EntityUtil.MissingMethod("GetRelatedReference");
}
getRelatedItem = getRelatedReferenceGeneric.MakeGenericMethod(sourceType, targetType);
break;
}
case RelationshipMultiplicity.Many:
{
// collection
MethodInfo getRelatedCollectionGeneric = typeof(RelationshipManager).GetMethod("GetRelatedCollection", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string), typeof(string), typeof(RelationshipMultiplicity), typeof(RelatedEnd) }, null);
if (getRelatedCollectionGeneric == null)
{
throw EntityUtil.MissingMethod("GetRelatedCollection");
}
getRelatedItem = getRelatedCollectionGeneric.MakeGenericMethod(sourceType, targetType);
break;
}
default:
throw EntityUtil.InvalidEnumerationValue(typeof(RelationshipMultiplicity), (int)targetMember.RelationshipMultiplicity);
}
DynamicMethod method = CreateDynamicMethod("GetRelatedEnd", typeof(IRelatedEnd), new Type[] { typeof(RelationshipManager), typeof(RelatedEnd)});
ILGenerator generator = method.GetILGenerator();
Debug.Assert(getRelatedItem.IsPrivate && !getRelatedItem.IsVirtual && (typeof(RelationshipManager) == getRelatedItem.DeclaringType), "Unexpected characteristics for GetRelatedCollection or GetRelatedReference");
// not generating reflection permission demand for calling RelationshipManager GetRelatedReference or GetRelatedCollection private non-interface methods
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Castclass, getRelatedItem.DeclaringType);
generator.Emit(OpCodes.Ldstr, sourceMember.DeclaringType.FullName);
generator.Emit(OpCodes.Ldstr, sourceMember.Name);
generator.Emit(OpCodes.Ldstr, targetMember.Name);
switch (sourceMember.RelationshipMultiplicity)
{
case RelationshipMultiplicity.ZeroOrOne:
generator.Emit(OpCodes.Ldc_I4_0); break;
case RelationshipMultiplicity.One: generator.Emit(OpCodes.Ldc_I4_1); break;
case RelationshipMultiplicity.Many: generator.Emit(OpCodes.Ldc_I4_2); break;
default:
throw EntityUtil.InvalidEnumerationValue(typeof(RelationshipMultiplicity), (int)sourceMember.RelationshipMultiplicity);
}
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Call, getRelatedItem);
generator.Emit(OpCodes.Ret);
RelationshipManager.GetRelatedEndMethod getRelatedEndMethod = (RelationshipManager.GetRelatedEndMethod)method.CreateDelegate(typeof(RelationshipManager.GetRelatedEndMethod));
return getRelatedEndMethod;
}
private static void ThrowConstructorNoParameterless()
{
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.CodeGen_ConstructorNoParameterless);
}
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 readonly NamedPermissionSet FullTrustPermission = new NamedPermissionSet("FullTrust");
// 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 visbility check & ReflectionEmit so we can generate the method
[System.Security.SecurityCritical]
[System.Security.SecurityTreatAsSafe]
[ReflectionPermission(SecurityAction.Assert, MemberAccess = true, ReflectionEmit = true)]
private static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes)
{
// Consider: write unit test to verify assumptions for why we don't use security transparent dynamic method (new in orcas)
// expecation is the security for the transparent jitted method is fixed on for the fist caller
// and we reuse the delegate for all callers
return new DynamicMethod(name, returnType, parameterTypes, typeof(LightweightCodeGenerator).Module, true);
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.