Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / Internal / EntityProxyFactory.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntityProxyFactory.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       [....]
7 // @backupOwner [....]
8 //---------------------------------------------------------------------
9
10 namespace System.Data.Objects.Internal
11 {
12     using System;
13     using System.Collections.Generic;
14     using System.ComponentModel;
15     using System.Data.Common.Utils;
16     using System.Data.Metadata.Edm;
17     using System.Data.Objects.DataClasses;
18     using System.Diagnostics;
19     using System.Globalization;
20     using System.Linq;
21     using System.Linq.Expressions;
22     using System.Reflection;
23     using System.Reflection.Emit;
24     using System.Runtime.CompilerServices;
25     using System.Runtime.Serialization;
26     using System.Security;
27     using System.Security.Permissions;
28     using System.Threading;
29
30     /// <summary>
31     /// Factory for creating proxy classes that can intercept calls to a class' members.
32     /// </summary>
33     internal class EntityProxyFactory
34     {
35         private const string ProxyTypeNameFormat = "System.Data.Entity.DynamicProxies.{0}_{1}";
36         internal const string ResetFKSetterFlagFieldName = "_resetFKSetterFlag";
37         internal const string CompareByteArraysFieldName = "_compareByteArrays";
38
39         /// <summary>
40         /// A hook such that test code can change the AssemblyBuilderAccess of the
41         /// proxy assembly through reflection into the EntityProxyFactory.
42         /// </summary>
43         private static AssemblyBuilderAccess s_ProxyAssemblyBuilderAccess = AssemblyBuilderAccess.Run;
44         /// <summary>
45         /// Dictionary of proxy class type information, keyed by the pair of the CLR type and EntityType CSpaceName of the type being proxied.
46         /// A null value for a particular EntityType name key records the fact that 
47         /// no proxy Type could be created for the specified type.
48         /// </summary>
49         private static Dictionary<Tuple<Type, string>, EntityProxyTypeInfo> s_ProxyNameMap = new Dictionary<Tuple<Type, string>, EntityProxyTypeInfo>();
50         /// <summary>
51         /// Dictionary of proxy class type information, keyed by the proxy type
52         /// </summary>
53         private static Dictionary<Type, EntityProxyTypeInfo> s_ProxyTypeMap = new Dictionary<Type, EntityProxyTypeInfo>();
54         private static Dictionary<Assembly, ModuleBuilder> s_ModuleBuilders = new Dictionary<Assembly, ModuleBuilder>();
55         private static ReaderWriterLockSlim s_TypeMapLock = new ReaderWriterLockSlim();
56
57         /// <summary>
58         /// The runtime assembly of the proxy types.
59         /// This is not the same as the AssemblyBuilder used to create proxy types.
60         /// </summary>
61         private static HashSet<Assembly> ProxyRuntimeAssemblies = new HashSet<Assembly>();
62
63         private static ModuleBuilder GetDynamicModule(EntityType ospaceEntityType)
64         {
65             Assembly assembly = ospaceEntityType.ClrType.Assembly;
66             ModuleBuilder moduleBuilder;
67             if (!s_ModuleBuilders.TryGetValue(assembly, out moduleBuilder))
68             {
69                 AssemblyName assemblyName = new AssemblyName(String.Format(CultureInfo.InvariantCulture, "EntityFrameworkDynamicProxies-{0}", assembly.FullName));
70                 assemblyName.Version = new Version(1, 0, 0, 0);
71                 
72                 // Mark assembly as security transparent, meaning it cannot cause an elevation of privilege.
73                 // This also means the assembly cannot satisfy a link demand. Instead link demands become full demands.
74                 ConstructorInfo securityTransparentAttributeConstructor = typeof(SecurityTransparentAttribute).GetConstructor(Type.EmptyTypes);
75
76                 // Mark assembly with [SecurityRules(SecurityRuleSet.Level1)]. In memory, the assembly will inherit
77                 // this automatically from SDE, but when persisted it needs this attribute to be considered Level1.
78                 ConstructorInfo securityRulesAttributeConstructor = typeof(SecurityRulesAttribute).GetConstructor(new Type[] { typeof(SecurityRuleSet) });
79
80                 CustomAttributeBuilder[] attributeBuilders = new CustomAttributeBuilder[] { 
81                     new CustomAttributeBuilder(securityTransparentAttributeConstructor, new object[0]),
82                     new CustomAttributeBuilder(securityRulesAttributeConstructor, new object[1] { SecurityRuleSet.Level1 })
83                 };
84
85                 AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, s_ProxyAssemblyBuilderAccess, attributeBuilders);
86
87                 if (s_ProxyAssemblyBuilderAccess == AssemblyBuilderAccess.RunAndSave)
88                 {
89                     // Make the module persistable if the AssemblyBuilderAccess is changed to be RunAndSave.
90                     moduleBuilder = assemblyBuilder.DefineDynamicModule("EntityProxyModule", "EntityProxyModule.dll");
91                 }
92                 else
93                 {
94                     moduleBuilder = assemblyBuilder.DefineDynamicModule("EntityProxyModule");
95                 }
96
97                 s_ModuleBuilders.Add(assembly, moduleBuilder);
98             }
99             return moduleBuilder;
100         }
101
102         internal static bool TryGetProxyType(Type clrType, string entityTypeName, out EntityProxyTypeInfo proxyTypeInfo)
103         {
104             s_TypeMapLock.EnterReadLock();
105             try
106             {
107                 return s_ProxyNameMap.TryGetValue(new Tuple<Type, string>(clrType, entityTypeName), out proxyTypeInfo);
108             }
109             finally
110             {
111                 s_TypeMapLock.ExitReadLock();
112             }
113         }
114
115         internal static bool TryGetProxyType(Type proxyType, out EntityProxyTypeInfo proxyTypeInfo)
116         {
117             s_TypeMapLock.EnterReadLock();
118             try
119             {
120                 return s_ProxyTypeMap.TryGetValue(proxyType, out proxyTypeInfo);
121             }
122             finally
123             {
124                 s_TypeMapLock.ExitReadLock();
125             }
126         }
127
128         internal static bool TryGetProxyWrapper(object instance, out IEntityWrapper wrapper)
129         {
130             Debug.Assert(instance != null, "the instance should not be null");
131             wrapper = null;
132             EntityProxyTypeInfo proxyTypeInfo;
133             if (IsProxyType(instance.GetType()) &&
134                 TryGetProxyType(instance.GetType(), out proxyTypeInfo))
135             {
136                 wrapper = proxyTypeInfo.GetEntityWrapper(instance);
137             }
138             return wrapper != null;
139         }
140
141         /// <summary>
142         /// Return proxy type information for the specified O-Space EntityType.
143         /// </summary>
144         /// <param name="ospaceEntityType">
145         /// EntityType in O-Space that represents the CLR type to be proxied.
146         /// Must not be null.
147         /// </param>
148         /// <returns>
149         /// A non-null EntityProxyTypeInfo instance that contains information about the type of proxy for
150         /// the specified O-Space EntityType; or null if no proxy can be created for the specified type.
151         /// </returns>
152         internal static EntityProxyTypeInfo GetProxyType(ClrEntityType ospaceEntityType)
153         {
154             Debug.Assert(ospaceEntityType != null, "ospaceEntityType must be non-null");
155             Debug.Assert(ospaceEntityType.DataSpace == DataSpace.OSpace, "ospaceEntityType.DataSpace must be OSpace");
156
157             EntityProxyTypeInfo proxyTypeInfo = null;
158
159             // Check if an entry for the proxy type already exists.
160             if (TryGetProxyType(ospaceEntityType.ClrType, ospaceEntityType.CSpaceTypeName, out proxyTypeInfo))
161             {
162                 if (proxyTypeInfo != null)
163                 {
164                     proxyTypeInfo.ValidateType(ospaceEntityType);
165                 }
166                 return proxyTypeInfo;
167             }
168
169             // No entry found, may need to create one.
170             // Acquire an upgradeable read lock so that:
171             // 1. Other readers aren't blocked while the second existence check is performed.
172             // 2. Other threads that may have also detected the absence of an entry block while the first thread handles proxy type creation.
173
174             s_TypeMapLock.EnterUpgradeableReadLock();
175             try
176             {
177                 return TryCreateProxyType(ospaceEntityType);
178             }
179             finally
180             {
181                 s_TypeMapLock.ExitUpgradeableReadLock();
182             }
183         }
184
185         /// <summary>
186         /// A mechanism to lookup AssociationType metadata for proxies for a given entity and association information
187         /// </summary>
188         /// <param name="wrappedEntity">The entity instance used to lookup the proxy type</param>
189         /// <param name="relationshipName">The name of the relationship (FullName or Name)</param>
190         /// <param name="targetRoleName">Target role of the relationship</param>
191         /// <param name="associationType">The AssociationType for that property</param>
192         /// <returns>True if an AssociationType is found in proxy metadata, false otherwise</returns>
193         internal static bool TryGetAssociationTypeFromProxyInfo(IEntityWrapper wrappedEntity, string relationshipName, string targetRoleName, out AssociationType associationType)
194         {
195             EntityProxyTypeInfo proxyInfo = null;
196             associationType = null;
197             return (EntityProxyFactory.TryGetProxyType(wrappedEntity.Entity.GetType(), out proxyInfo) && proxyInfo != null &&
198                     proxyInfo.TryGetNavigationPropertyAssociationType(relationshipName, targetRoleName, out associationType));
199         }
200
201         /// <summary>
202         /// Enumerate list of supplied O-Space EntityTypes, 
203         /// and generate a proxy type for each EntityType (if possible for the particular type).
204         /// </summary>
205         /// <param name="ospaceEntityType">
206         /// Enumeration of O-Space EntityType objects.
207         /// Must not be null.
208         /// In addition, the elements of the enumeration must not be null.
209         /// </param>
210         internal static void TryCreateProxyTypes(IEnumerable<EntityType> ospaceEntityTypes)
211         {
212             Debug.Assert(ospaceEntityTypes != null, "ospaceEntityTypes must be non-null");
213
214             // Acquire an upgradeable read lock for the duration of the enumeration so that:
215             // 1. Other readers aren't blocked while existence checks are performed.
216             // 2. Other threads that may have detected the absence of an entry block while the first thread handles proxy type creation.
217
218             s_TypeMapLock.EnterUpgradeableReadLock();
219             try
220             {
221                 foreach (EntityType ospaceEntityType in ospaceEntityTypes)
222                 {
223                     Debug.Assert(ospaceEntityType != null, "Null EntityType element reference present in enumeration.");
224                     TryCreateProxyType(ospaceEntityType);
225                 }
226             }
227             finally
228             {
229                 s_TypeMapLock.ExitUpgradeableReadLock();
230             }
231         }
232
233         private static EntityProxyTypeInfo TryCreateProxyType(EntityType ospaceEntityType)
234         {
235             Debug.Assert(s_TypeMapLock.IsUpgradeableReadLockHeld, "EntityProxyTypeInfo.TryCreateProxyType method was called without first acquiring an upgradeable read lock from s_TypeMapLock.");
236
237             EntityProxyTypeInfo proxyTypeInfo;
238             ClrEntityType clrEntityType = (ClrEntityType)ospaceEntityType;
239
240             Tuple<Type, string> proxyIdentiy = new Tuple<Type, string>(clrEntityType.ClrType, clrEntityType.HashedDescription);
241
242             if (!s_ProxyNameMap.TryGetValue(proxyIdentiy, out proxyTypeInfo) && CanProxyType(ospaceEntityType))
243             {
244                 ModuleBuilder moduleBuilder = GetDynamicModule(ospaceEntityType);
245                 proxyTypeInfo = BuildType(moduleBuilder, clrEntityType);
246
247                 s_TypeMapLock.EnterWriteLock();
248                 try
249                 {
250                     s_ProxyNameMap[proxyIdentiy] = proxyTypeInfo;
251                     if (proxyTypeInfo != null)
252                     {
253                         // If there is a proxy type, create the reverse lookup
254                         s_ProxyTypeMap[proxyTypeInfo.ProxyType] = proxyTypeInfo;
255                     }
256                 }
257                 finally
258                 {
259                     s_TypeMapLock.ExitWriteLock();
260                 }
261             }
262
263             return proxyTypeInfo;
264         }
265
266         /// <summary>
267         /// Determine if the specified type represents a known proxy type.
268         /// </summary>
269         /// <param name="type">
270         /// The Type to be examined.
271         /// </param>
272         /// <returns>
273         /// True if the type is a known proxy type; otherwise false.
274         /// </returns>
275         internal static bool IsProxyType(Type type)
276         {
277             Debug.Assert(type != null, "type is null, was this intended?");
278             return type != null && ProxyRuntimeAssemblies.Contains(type.Assembly);
279         }
280
281         /// <summary>
282         /// Return an enumerable of the current set of CLR proxy types.
283         /// </summary>
284         /// <returns>
285         /// Enumerable of the current set of CLR proxy types.
286         /// This value will never be null.
287         /// </returns>
288         /// <remarks>
289         /// The enumerable is based on a shapshot of the current list of types.
290         /// </remarks>
291         internal static IEnumerable<Type> GetKnownProxyTypes()
292         {
293             s_TypeMapLock.EnterReadLock();
294             try
295             {
296                 var proxyTypes = from info in s_ProxyNameMap.Values
297                                  where info != null
298                                  select info.ProxyType;
299                 return proxyTypes.ToArray();
300             }
301             finally
302             {
303                 s_TypeMapLock.ExitReadLock();
304             }
305         }
306
307         public Func<object, object> CreateBaseGetter(Type declaringType, PropertyInfo propertyInfo)
308         {
309             Debug.Assert(propertyInfo != null, "Null propertyInfo");
310
311             ParameterExpression Object_Parameter = Expression.Parameter(typeof(object), "instance");
312             Func<object, object> nonProxyGetter = Expression.Lambda<Func<object, object>>(
313                                     Expression.PropertyOrField(
314                                         Expression.Convert(Object_Parameter, declaringType),
315                                         propertyInfo.Name),
316                                     Object_Parameter).Compile();
317
318             string propertyName = propertyInfo.Name;
319             return (entity) =>
320             {
321                 Type type = entity.GetType();
322                 if (IsProxyType(type))
323                 {
324                     object value;
325                     if (TryGetBasePropertyValue(type, propertyName, entity, out value))
326                     {
327                         return value;
328                     }
329                 }
330                 return nonProxyGetter(entity);
331             };
332         }
333
334         private static bool TryGetBasePropertyValue(Type proxyType, string propertyName, object entity, out object value)
335         {
336             EntityProxyTypeInfo typeInfo;
337             value = null;
338             if (TryGetProxyType(proxyType, out typeInfo) && typeInfo.ContainsBaseGetter(propertyName))
339             {
340                 value = typeInfo.BaseGetter(entity, propertyName);
341                 return true;
342             }
343             return false;
344         }
345
346         public Action<object, object> CreateBaseSetter(Type declaringType, PropertyInfo propertyInfo)
347         {
348             Debug.Assert(propertyInfo != null, "Null propertyInfo");
349
350             Action<object, object> nonProxySetter = LightweightCodeGenerator.CreateNavigationPropertySetter(declaringType, propertyInfo);
351
352             string propertyName = propertyInfo.Name;
353             return (entity, value) =>
354             {
355                 Type type = entity.GetType();
356                 if (IsProxyType(type))
357                 {
358                     if (TrySetBasePropertyValue(type, propertyName, entity, value))
359                     {
360                         return;
361                     }
362                 }
363                 nonProxySetter(entity, value);
364             };
365         }
366
367         private static bool TrySetBasePropertyValue(Type proxyType, string propertyName, object entity, object value)
368         {
369             EntityProxyTypeInfo typeInfo;
370             if (TryGetProxyType(proxyType, out typeInfo) && typeInfo.ContainsBaseSetter(propertyName))
371             {
372                 typeInfo.BaseSetter(entity, propertyName, value);
373                 return true;
374             }
375             return false;
376         }
377
378         /// <summary>
379         /// Build a CLR proxy type for the supplied EntityType.
380         /// </summary>
381         /// <param name="ospaceEntityType">
382         /// EntityType in O-Space that represents the CLR type to be proxied.
383         /// </param>
384         /// <returns>
385         /// EntityProxyTypeInfo object that contains the constructed proxy type,
386         /// along with any behaviors associated with that type;
387         /// or null if a proxy type cannot be constructed for the specified EntityType.
388         /// </returns>
389         private static EntityProxyTypeInfo BuildType(ModuleBuilder moduleBuilder, ClrEntityType ospaceEntityType)
390         {
391             Debug.Assert(s_TypeMapLock.IsUpgradeableReadLockHeld, "EntityProxyTypeInfo.BuildType method was called without first acquiring an upgradeable read lock from s_TypeMapLock.");
392
393             EntityProxyTypeInfo proxyTypeInfo;
394
395             ProxyTypeBuilder proxyTypeBuilder = new ProxyTypeBuilder(ospaceEntityType);
396             Type proxyType = proxyTypeBuilder.CreateType(moduleBuilder);
397
398             if (proxyType != null)
399             {
400                 // Set the runtime assembly of the proxy types if it hasn't already been set.
401                 // This is used by the IsProxyType method.
402                 Assembly typeAssembly = proxyType.Assembly;
403                 if (!ProxyRuntimeAssemblies.Contains(typeAssembly))
404                 {
405                     ProxyRuntimeAssemblies.Add(typeAssembly);
406                     AddAssemblyToResolveList(typeAssembly);
407                 }
408
409                 proxyTypeInfo = new EntityProxyTypeInfo(proxyType, ospaceEntityType,
410                     proxyTypeBuilder.CreateInitalizeCollectionMethod(proxyType),
411                     proxyTypeBuilder.BaseGetters, proxyTypeBuilder.BaseSetters);
412
413                 foreach (EdmMember member in proxyTypeBuilder.LazyLoadMembers)
414                 {
415                     InterceptMember(member, proxyType, proxyTypeInfo);
416                 }
417
418                 SetResetFKSetterFlagDelegate(proxyType, proxyTypeInfo);
419                 SetCompareByteArraysDelegate(proxyType, proxyTypeInfo);
420             }
421             else
422             {
423                 proxyTypeInfo = null;
424             }
425
426             return proxyTypeInfo;
427         }
428
429         /// <summary>
430         /// In order for deserialization of proxy objects to succeed in this AppDomain,
431         /// an assembly resolve handler must be added to the AppDomain to resolve the dynamic assembly,
432         /// since it is not present in a location discoverable by fusion.
433         /// </summary>
434         /// <param name="assembly">Proxy assembly to be resolved.</param>
435         [SecuritySafeCritical]
436         private static void AddAssemblyToResolveList(Assembly assembly)
437         {
438             if (ProxyRuntimeAssemblies.Contains(assembly)) // If the assembly is not a known proxy assembly, ignore it.
439             {
440                 ResolveEventHandler resolveHandler = new ResolveEventHandler((sender, args) => args.Name == assembly.FullName ? assembly : null);
441                 AppDomain.CurrentDomain.AssemblyResolve += resolveHandler;
442             }
443         }
444
445         /// <summary>
446         /// Construct an interception delegate for the specified proxy member.
447         /// </summary>
448         /// <param name="member">
449         /// EdmMember that specifies the member to be intercepted.
450         /// </param>
451         /// <param name="proxyType">
452         /// Type of the proxy.
453         /// </param>
454         /// <param name="lazyLoadBehavior">
455         /// LazyLoadBehavior object that supplies the behavior to load related ends.
456         /// </param>
457         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
458         private static void InterceptMember(EdmMember member, Type proxyType, EntityProxyTypeInfo proxyTypeInfo)
459         {
460             PropertyInfo property = EntityUtil.GetTopProperty(proxyType, member.Name);
461             Debug.Assert(property != null, String.Format(CultureInfo.CurrentCulture, "Expected property {0} to be defined on proxy type {1}", member.Name, proxyType.FullName));
462
463             FieldInfo interceptorField = proxyType.GetField(LazyLoadImplementor.GetInterceptorFieldName(member.Name), BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic);
464             Debug.Assert(interceptorField != null, String.Format(CultureInfo.CurrentCulture, "Expected interceptor field for property {0} to be defined on proxy type {1}", member.Name, proxyType.FullName));
465
466             Delegate interceptorDelegate = typeof(LazyLoadBehavior).GetMethod("GetInterceptorDelegate", BindingFlags.NonPublic | BindingFlags.Static).
467                 MakeGenericMethod(proxyType, property.PropertyType).
468                 Invoke(null, new object[] { member, proxyTypeInfo.EntityWrapperDelegate }) as Delegate;
469
470             AssignInterceptionDelegate(interceptorDelegate, interceptorField);
471         }
472
473         /// <summary>
474         /// Set the interceptor on a proxy member.
475         /// </summary>
476         /// <param name="interceptorDelegate">
477         /// Delegate to be set
478         /// </param>
479         /// <param name="interceptorField">
480         /// Field define on the proxy type to store the reference to the interception delegate.
481         /// </param>
482         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2128")]
483         [SecuritySafeCritical]
484         [ReflectionPermission(SecurityAction.Assert, MemberAccess = true)]
485         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
486         private static void AssignInterceptionDelegate(Delegate interceptorDelegate, FieldInfo interceptorField)
487         {
488             interceptorField.SetValue(null, interceptorDelegate);
489         }
490
491         /// <summary>
492         /// Sets a delegate onto the _resetFKSetterFlag field such that it can be executed to make
493         /// a call into the state manager to reset the InFKSetter flag.
494         /// </summary>
495         private static void SetResetFKSetterFlagDelegate(Type proxyType, EntityProxyTypeInfo proxyTypeInfo)
496         {
497             var resetFKSetterFlagField = proxyType.GetField(ResetFKSetterFlagFieldName, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic);
498             Debug.Assert(resetFKSetterFlagField != null, "Expected resetFKSetterFlagField to be defined on the proxy type.");
499
500             var resetFKSetterFlagDelegate = GetResetFKSetterFlagDelegate(proxyTypeInfo.EntityWrapperDelegate);
501
502             AssignInterceptionDelegate(resetFKSetterFlagDelegate, resetFKSetterFlagField);
503         }
504
505         /// <summary>
506         /// Returns the delegate that takes a proxy instance and uses it to reset the InFKSetter flag maintained
507         /// by the state manager of the context associated with the proxy instance.
508         /// </summary>
509         private static Action<object> GetResetFKSetterFlagDelegate(Func<object, object> getEntityWrapperDelegate)
510         {
511             return (proxy) =>
512             {
513                 Debug.Assert(getEntityWrapperDelegate != null, "entityWrapperDelegate must not be null");
514                 
515                 ResetFKSetterFlag(getEntityWrapperDelegate(proxy));
516             };
517         }
518
519         /// <summary>
520         /// Called in the finally clause of each overridden property setter to ensure that the flag
521         /// indicating that we are in an FK setter is cleared.  Note that the wrapped entity is passed as
522         /// an obejct becayse IEntityWrapper is an internal type and is therefore not accessable to
523         /// the proxy type.  Once we're in the framework it is cast back to an IEntityWrapper.
524         /// </summary>
525         private static void ResetFKSetterFlag(object wrappedEntityAsObject)
526         {
527             Debug.Assert(wrappedEntityAsObject == null || wrappedEntityAsObject is IEntityWrapper, "wrappedEntityAsObject must be an IEntityWrapper");
528             var wrappedEntity = (IEntityWrapper)wrappedEntityAsObject; // We want an exception if the cast fails.
529             if (wrappedEntity != null && wrappedEntity.Context != null)
530             {
531                 wrappedEntity.Context.ObjectStateManager.EntityInvokingFKSetter = null;
532             }
533         }
534
535         /// <summary>
536         /// Sets a delegate onto the _compareByteArrays field such that it can be executed to check
537         /// whether two byte arrays are the same by value comparison.
538         /// </summary>
539         private static void SetCompareByteArraysDelegate(Type proxyType, EntityProxyTypeInfo proxyTypeInfo)
540         {
541             var compareByteArraysField = proxyType.GetField(CompareByteArraysFieldName, BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.NonPublic);
542             Debug.Assert(compareByteArraysField != null, "Expected compareByteArraysField to be defined on the proxy type.");
543
544             AssignInterceptionDelegate(new Func<object, object, bool>(ByValueEqualityComparer.Default.Equals), compareByteArraysField);
545         }
546
547         /// <summary>
548         /// Return boolean that specifies if the specified type can be proxied.
549         /// </summary>
550         /// <param name="ospaceEntityType">O-space EntityType</param>
551         /// <returns>
552         /// True if the class is not abstract or sealed, does not implement IEntityWithRelationships,
553         /// and has a public or protected default constructor; otherwise false.
554         /// </returns>
555         /// <remarks>
556         /// While it is technically possible to derive from an abstract type
557         /// in order to create a proxy, we avoid this so that the proxy type 
558         /// has the same "concreteness" of the type being proxied.
559         /// The check for IEntityWithRelationships ensures that codegen'ed
560         /// entities that derive from EntityObject as well as properly
561         /// constructed IPOCO entities will not be proxied.
562         /// 
563         /// </remarks>
564         private static bool CanProxyType(EntityType ospaceEntityType)
565         {
566             TypeAttributes access = ospaceEntityType.ClrType.Attributes & TypeAttributes.VisibilityMask;
567
568             ConstructorInfo ctor = ospaceEntityType.ClrType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance, null, Type.EmptyTypes, null);
569             bool accessableCtor = ctor != null && (((ctor.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public) ||
570                                                    ((ctor.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Family) ||
571                                                    ((ctor.Attributes & MethodAttributes.MemberAccessMask) == MethodAttributes.FamORAssem));
572
573             return (!(ospaceEntityType.Abstract ||
574                      ospaceEntityType.ClrType.IsSealed ||
575                      typeof(IEntityWithRelationships).IsAssignableFrom(ospaceEntityType.ClrType) ||
576                      !accessableCtor) &&
577                      access == TypeAttributes.Public);
578         }
579
580         private static bool CanProxyMethod(MethodInfo method)
581         {
582             bool result = false;
583
584             if (method != null)
585             {
586                 MethodAttributes access = method.Attributes & MethodAttributes.MemberAccessMask;
587                 result = method.IsVirtual &&
588                          !method.IsFinal &&
589                          (access == MethodAttributes.Public || 
590                           access == MethodAttributes.Family || 
591                           access == MethodAttributes.FamORAssem);
592             }
593
594             return result;
595         }
596
597         internal static bool CanProxyGetter(PropertyInfo clrProperty)
598         {
599             Debug.Assert(clrProperty != null, "clrProperty should have a value");
600             return CanProxyMethod(clrProperty.GetGetMethod(true));
601         }
602
603         internal static bool CanProxySetter(PropertyInfo clrProperty)
604         {
605             Debug.Assert(clrProperty != null, "clrProperty should have a value");
606             return CanProxyMethod(clrProperty.GetSetMethod(true));
607         }
608
609         private class ProxyTypeBuilder
610         {
611             private TypeBuilder _typeBuilder;
612             private BaseProxyImplementor _baseImplementor;
613             private IPOCOImplementor _ipocoImplementor;
614             private LazyLoadImplementor _lazyLoadImplementor;
615             private DataContractImplementor _dataContractImplementor;
616             private ISerializableImplementor _iserializableImplementor;
617             private ClrEntityType _ospaceEntityType;
618             private ModuleBuilder _moduleBuilder;
619             private List<FieldBuilder> _serializedFields = new List<FieldBuilder>(3);
620
621             public ProxyTypeBuilder(ClrEntityType ospaceEntityType)
622             {
623                 _ospaceEntityType = ospaceEntityType;
624                 _baseImplementor = new BaseProxyImplementor();
625                 _ipocoImplementor = new IPOCOImplementor(ospaceEntityType);
626                 _lazyLoadImplementor = new LazyLoadImplementor(ospaceEntityType);
627                 _dataContractImplementor = new DataContractImplementor(ospaceEntityType);
628                 _iserializableImplementor = new ISerializableImplementor(ospaceEntityType);
629             }
630
631             public Type BaseType
632             {
633                 get { return _ospaceEntityType.ClrType; }
634             }
635
636             public DynamicMethod CreateInitalizeCollectionMethod(Type proxyType)
637             {
638                 return _ipocoImplementor.CreateInitalizeCollectionMethod(proxyType);
639             }
640
641             public List<PropertyInfo> BaseGetters
642             {
643                 get
644                 {
645                     return _baseImplementor.BaseGetters;
646                 }
647             }
648
649             public List<PropertyInfo> BaseSetters
650             {
651                 get
652                 {
653                     return _baseImplementor.BaseSetters;
654                 }
655             }
656
657             public IEnumerable<EdmMember> LazyLoadMembers
658             {
659                 get { return _lazyLoadImplementor.Members; }
660             }
661
662             public Type CreateType(ModuleBuilder moduleBuilder)
663             {
664                 _moduleBuilder = moduleBuilder;
665                 bool hadProxyProperties = false;
666
667                 if (_iserializableImplementor.TypeIsSuitable)
668                 {
669                     foreach (EdmMember member in _ospaceEntityType.Members)
670                     {
671                         if (_ipocoImplementor.CanProxyMember(member) ||
672                             _lazyLoadImplementor.CanProxyMember(member))
673                         {
674                             PropertyInfo baseProperty = EntityUtil.GetTopProperty(BaseType, member.Name);
675                             PropertyBuilder propertyBuilder = TypeBuilder.DefineProperty(member.Name, System.Reflection.PropertyAttributes.None, baseProperty.PropertyType, Type.EmptyTypes);
676
677                             if (!_ipocoImplementor.EmitMember(TypeBuilder, member, propertyBuilder, baseProperty, _baseImplementor))
678                             {
679                                 EmitBaseSetter(TypeBuilder, propertyBuilder, baseProperty);
680                             }
681                             if (!_lazyLoadImplementor.EmitMember(TypeBuilder, member, propertyBuilder, baseProperty, _baseImplementor))
682                             {
683                                 EmitBaseGetter(TypeBuilder, propertyBuilder, baseProperty);
684                             }
685
686                             hadProxyProperties = true;
687                         }
688                     }
689
690                     if (_typeBuilder != null)
691                     {
692                         _baseImplementor.Implement(TypeBuilder, RegisterInstanceField);
693                         _iserializableImplementor.Implement(TypeBuilder, _serializedFields);
694                     }
695                 }
696
697                 return hadProxyProperties ? TypeBuilder.CreateType() : null;
698             }
699
700             private TypeBuilder TypeBuilder
701             {
702                 get
703                 {
704                     if (_typeBuilder == null)
705                     {
706                         TypeAttributes proxyTypeAttributes = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
707                         if ((BaseType.Attributes & TypeAttributes.Serializable) == TypeAttributes.Serializable)
708                         {
709                             proxyTypeAttributes |= TypeAttributes.Serializable;
710                         }
711
712                         // If the type as a long name, then use only the first part of it so that there is no chance that the generated
713                         // name will be too long.  Note that the full name always gets used to compute the hash.
714                         string baseName = BaseType.Name.Length <= 20 ? BaseType.Name : BaseType.Name.Substring(0, 20);
715                         string proxyTypeName = String.Format(CultureInfo.InvariantCulture, ProxyTypeNameFormat, baseName, _ospaceEntityType.HashedDescription);
716
717                         _typeBuilder = _moduleBuilder.DefineType(proxyTypeName, proxyTypeAttributes, BaseType, _ipocoImplementor.Interfaces);
718                         _typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName);
719
720                         Action<FieldBuilder, bool> registerField = RegisterInstanceField;
721                         _ipocoImplementor.Implement(_typeBuilder, registerField);
722                         _lazyLoadImplementor.Implement(_typeBuilder, registerField);
723
724                         // WCF data contract serialization is not compatible with types that implement ISerializable.
725                         if (!_iserializableImplementor.TypeImplementsISerializable)
726                         {
727                             _dataContractImplementor.Implement(_typeBuilder, registerField);
728                         }
729                     }
730                     return _typeBuilder;
731                 }
732             }
733
734             private void EmitBaseGetter(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, PropertyInfo baseProperty)
735             {
736                 if (CanProxyGetter(baseProperty))
737                 {
738                     MethodInfo baseGetter = baseProperty.GetGetMethod(true);
739                     const MethodAttributes getterAttributes = MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;
740                     MethodAttributes getterAccess = baseGetter.Attributes & MethodAttributes.MemberAccessMask;
741
742                     // Define a property getter override in the proxy type
743                     MethodBuilder getterBuilder = typeBuilder.DefineMethod("get_" + baseProperty.Name, getterAccess | getterAttributes, baseProperty.PropertyType, Type.EmptyTypes);
744                     ILGenerator gen = getterBuilder.GetILGenerator();
745
746                     gen.Emit(OpCodes.Ldarg_0);
747                     gen.Emit(OpCodes.Call, baseGetter);
748                     gen.Emit(OpCodes.Ret);
749
750                     propertyBuilder.SetGetMethod(getterBuilder);
751                 }
752             }
753
754             private void EmitBaseSetter(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, PropertyInfo baseProperty)
755             {
756                 if (CanProxySetter(baseProperty))
757                 {
758
759                     MethodInfo baseSetter = baseProperty.GetSetMethod(true); ;
760                     const MethodAttributes methodAttributes = MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;
761                     MethodAttributes methodAccess = baseSetter.Attributes & MethodAttributes.MemberAccessMask;
762
763                     MethodBuilder setterBuilder = typeBuilder.DefineMethod("set_" + baseProperty.Name, methodAccess | methodAttributes, null, new Type[] { baseProperty.PropertyType });
764                     ILGenerator generator = setterBuilder.GetILGenerator();
765                     generator.Emit(OpCodes.Ldarg_0);
766                     generator.Emit(OpCodes.Ldarg_1);
767                     generator.Emit(OpCodes.Call, baseSetter);
768                     generator.Emit(OpCodes.Ret);
769                     propertyBuilder.SetSetMethod(setterBuilder);
770                 }
771             }
772
773             private void RegisterInstanceField(FieldBuilder field, bool serializable)
774             {
775                 if (serializable)
776                 {
777                     _serializedFields.Add(field);
778                 }
779                 else
780                 {
781                     MarkAsNotSerializable(field); 
782                 }
783             }
784
785             private static readonly ConstructorInfo s_NonSerializedAttributeConstructor = typeof(NonSerializedAttribute).GetConstructor(Type.EmptyTypes);
786             private static readonly ConstructorInfo s_IgnoreDataMemberAttributeConstructor = typeof(IgnoreDataMemberAttribute).GetConstructor(Type.EmptyTypes);
787             private static readonly ConstructorInfo s_XmlIgnoreAttributeConstructor = typeof(System.Xml.Serialization.XmlIgnoreAttribute).GetConstructor(Type.EmptyTypes);
788             private static readonly ConstructorInfo s_ScriptIgnoreAttributeConstructor = TryGetScriptIgnoreAttributeType().GetConstructor(Type.EmptyTypes);
789
790             private static Type TryGetScriptIgnoreAttributeType()
791             {
792                 try
793                 {
794                     var scriptIgnoreAttributeAssembly = Assembly.Load(AssemblyRef.SystemWebExtensions);
795                     return scriptIgnoreAttributeAssembly.GetType(@"System.Web.Script.Serialization.ScriptIgnoreAttribute");
796                 }
797                 catch 
798                 {
799                 }
800                 // We should not assert in EF6, at least when produce a build compatible with .NET 4.0 client SKU
801                 Debug.Assert(false, "Unable to find ScriptIgnoreAttribute type");
802                 return null;
803             }
804
805             private static void MarkAsNotSerializable(FieldBuilder field)
806             {
807                 object[] emptyArray = new object[0];
808
809                 field.SetCustomAttribute(new CustomAttributeBuilder(s_NonSerializedAttributeConstructor, emptyArray));
810
811                 if (field.IsPublic)
812                 {
813                     field.SetCustomAttribute(new CustomAttributeBuilder(s_IgnoreDataMemberAttributeConstructor, emptyArray));
814                     field.SetCustomAttribute(new CustomAttributeBuilder(s_XmlIgnoreAttributeConstructor, emptyArray));
815
816                     if (s_ScriptIgnoreAttributeConstructor != null)
817                     {
818                         field.SetCustomAttribute(new CustomAttributeBuilder(s_ScriptIgnoreAttributeConstructor, emptyArray));
819                     }
820                 }
821             }
822         }
823     }
824
825     internal class LazyLoadImplementor
826     {
827         HashSet<EdmMember> _members;
828
829         public LazyLoadImplementor(EntityType ospaceEntityType)
830         {
831             CheckType(ospaceEntityType);
832         }
833
834         public IEnumerable<EdmMember> Members
835         {
836             get { return _members; }
837         }
838
839         private void CheckType(EntityType ospaceEntityType)
840         {
841             _members = new HashSet<EdmMember>();
842
843             foreach (EdmMember member in ospaceEntityType.Members)
844             {
845                 PropertyInfo clrProperty = EntityUtil.GetTopProperty(ospaceEntityType.ClrType, member.Name);
846                 if (clrProperty != null &&
847                     EntityProxyFactory.CanProxyGetter(clrProperty) &&
848                     LazyLoadBehavior.IsLazyLoadCandidate(ospaceEntityType, member))
849                 {
850                     _members.Add(member);
851                 }
852             }
853         }
854
855         public bool CanProxyMember(EdmMember member)
856         {
857             return _members.Contains(member);
858         }
859
860         public void Implement(TypeBuilder typeBuilder, Action<FieldBuilder, bool> registerField)
861         {
862             // Add instance field to store IEntityWrapper instance
863             // The field is typed as object, for two reasons:
864             // 1. The practical one, IEntityWrapper is internal and not accessible from the dynamic assembly.
865             // 2. We purposely want the wrapper field to be opaque on the proxy type.
866             FieldBuilder wrapperField = typeBuilder.DefineField(EntityProxyTypeInfo.EntityWrapperFieldName, typeof(object), FieldAttributes.Public);
867             registerField(wrapperField, false);
868         }
869
870         public bool EmitMember(TypeBuilder typeBuilder, EdmMember member, PropertyBuilder propertyBuilder, PropertyInfo baseProperty, BaseProxyImplementor baseImplementor)
871         {
872             if (_members.Contains(member))
873             {
874                 MethodInfo baseGetter = baseProperty.GetGetMethod(true);
875                 const MethodAttributes getterAttributes = MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;
876                 MethodAttributes getterAccess = baseGetter.Attributes & MethodAttributes.MemberAccessMask;
877
878                 // Define field to store interceptor Func
879                 // Signature of interceptor Func delegate is as follows:
880                 //
881                 //    bool intercept(ProxyType proxy, PropertyType propertyValue)
882                 //
883                 // where
884                 //     PropertyType is the type of the Property, such as ICollection<Customer>,
885                 //     ProxyType is the type of the proxy object,
886                 //     propertyValue is the value returned from the proxied type's property getter.
887
888                 Type interceptorType = typeof(Func<,,>).MakeGenericType(typeBuilder, baseProperty.PropertyType, typeof(bool));
889                 MethodInfo interceptorInvoke = TypeBuilder.GetMethod(interceptorType, typeof(Func<,,>).GetMethod("Invoke"));
890                 FieldBuilder interceptorField = typeBuilder.DefineField(GetInterceptorFieldName(baseProperty.Name), interceptorType, FieldAttributes.Private | FieldAttributes.Static);
891
892                 // Define a property getter override in the proxy type
893                 MethodBuilder getterBuilder = typeBuilder.DefineMethod("get_" + baseProperty.Name, getterAccess | getterAttributes, baseProperty.PropertyType, Type.EmptyTypes);
894                 ILGenerator generator = getterBuilder.GetILGenerator();
895
896                 // Emit instructions for the following call:
897                 //   T value = base.SomeProperty;
898                 //   if(this._interceptorForSomeProperty(this, value))
899                 //   {  return value; }
900                 //   return base.SomeProperty;
901                 // where _interceptorForSomeProperty represents the interceptor Func field.
902
903                 Label lableTrue = generator.DefineLabel();
904                 generator.DeclareLocal(baseProperty.PropertyType);       // T value
905                 generator.Emit(OpCodes.Ldarg_0);            // call base.SomeProperty
906                 generator.Emit(OpCodes.Call, baseGetter); // call to base property getter
907                 generator.Emit(OpCodes.Stloc_0);            // value = result
908                 generator.Emit(OpCodes.Ldarg_0);            // load this
909                 generator.Emit(OpCodes.Ldfld, interceptorField); // load this._interceptor
910                 generator.Emit(OpCodes.Ldarg_0);            // load this
911                 generator.Emit(OpCodes.Ldloc_0);            // load value
912                 generator.Emit(OpCodes.Callvirt, interceptorInvoke); // call to interceptor delegate with (this, value)
913                 generator.Emit(OpCodes.Brtrue_S, lableTrue); // if true, just return
914                 generator.Emit(OpCodes.Ldarg_0); // else, call the base propertty getter again
915                 generator.Emit(OpCodes.Call, baseGetter); // call to base property getter
916                 generator.Emit(OpCodes.Ret);
917                 generator.MarkLabel(lableTrue);
918                 generator.Emit(OpCodes.Ldloc_0);
919                 generator.Emit(OpCodes.Ret);
920
921                 propertyBuilder.SetGetMethod(getterBuilder);
922
923                 baseImplementor.AddBasePropertyGetter(baseProperty);
924                 return true;
925             }
926             return false;
927         }
928
929
930         internal static string GetInterceptorFieldName(string memberName)
931         {
932             return "ef_proxy_interceptorFor" + memberName;
933         }
934
935     }
936
937     internal class BaseProxyImplementor
938     {
939         private readonly List<PropertyInfo> _baseGetters;
940         private readonly List<PropertyInfo> _baseSetters;
941
942         public BaseProxyImplementor()
943         {
944             _baseGetters = new List<PropertyInfo>();
945             _baseSetters = new List<PropertyInfo>();
946         }
947
948         public List<PropertyInfo> BaseGetters
949         {
950             get { return _baseGetters; }
951         }
952
953         public List<PropertyInfo> BaseSetters
954         {
955             get { return _baseSetters; }
956         }
957         public void AddBasePropertyGetter(PropertyInfo baseProperty)
958         {
959             _baseGetters.Add(baseProperty);
960         }
961
962         public void AddBasePropertySetter(PropertyInfo baseProperty)
963         {
964             _baseSetters.Add(baseProperty);
965         }
966
967         public void Implement(TypeBuilder typeBuilder, Action<FieldBuilder, bool> registerField)
968         {
969             if (_baseGetters.Count > 0)
970             {
971                 ImplementBaseGetter(typeBuilder);
972             }
973             if (_baseSetters.Count > 0)
974             {
975                 ImplementBaseSetter(typeBuilder);
976             }
977         }
978
979         static readonly MethodInfo s_StringEquals = typeof(string).GetMethod("op_Equality", new Type[] { typeof(string), typeof(string) });
980         static readonly ConstructorInfo s_InvalidOperationConstructor = typeof(InvalidOperationException).GetConstructor(Type.EmptyTypes);
981
982         private void ImplementBaseGetter(TypeBuilder typeBuilder)
983         {
984             // Define a property getter in the proxy type
985             MethodBuilder getterBuilder = typeBuilder.DefineMethod("GetBasePropertyValue", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(object), new Type[] { typeof(string) });
986             ILGenerator gen = getterBuilder.GetILGenerator();
987             Label[] labels = new Label[_baseGetters.Count];
988
989             for (int i = 0; i < _baseGetters.Count; i++)
990             {
991                 labels[i] = gen.DefineLabel();
992                 gen.Emit(OpCodes.Ldarg_1);
993                 gen.Emit(OpCodes.Ldstr, _baseGetters[i].Name);
994                 gen.Emit(OpCodes.Call, s_StringEquals);
995                 gen.Emit(OpCodes.Brfalse_S, labels[i]);
996                 gen.Emit(OpCodes.Ldarg_0);
997                 gen.Emit(OpCodes.Call, _baseGetters[i].GetGetMethod(true));
998                 gen.Emit(OpCodes.Ret);
999                 gen.MarkLabel(labels[i]);
1000             }
1001             gen.Emit(OpCodes.Newobj, s_InvalidOperationConstructor);
1002             gen.Emit(OpCodes.Throw);
1003         }
1004
1005         private void ImplementBaseSetter(TypeBuilder typeBuilder)
1006         {
1007             MethodBuilder setterBuilder = typeBuilder.DefineMethod("SetBasePropertyValue", MethodAttributes.Public | MethodAttributes.HideBySig, typeof(void), new Type[] { typeof(string), typeof(object) });
1008             ILGenerator gen = setterBuilder.GetILGenerator();
1009
1010             Label[] labels = new Label[_baseSetters.Count];
1011
1012             for (int i = 0; i < _baseSetters.Count; i++)
1013             {
1014                 labels[i] = gen.DefineLabel();
1015                 gen.Emit(OpCodes.Ldarg_1);
1016                 gen.Emit(OpCodes.Ldstr, _baseSetters[i].Name);
1017                 gen.Emit(OpCodes.Call, s_StringEquals);
1018                 gen.Emit(OpCodes.Brfalse_S, labels[i]);
1019                 gen.Emit(OpCodes.Ldarg_0);
1020                 gen.Emit(OpCodes.Ldarg_2);
1021                 gen.Emit(OpCodes.Castclass, _baseSetters[i].PropertyType);
1022                 gen.Emit(OpCodes.Call, _baseSetters[i].GetSetMethod(true));
1023                 gen.Emit(OpCodes.Ret);
1024                 gen.MarkLabel(labels[i]);
1025             }
1026             gen.Emit(OpCodes.Newobj, s_InvalidOperationConstructor);
1027             gen.Emit(OpCodes.Throw);
1028         }
1029     }
1030
1031     internal class IPOCOImplementor
1032     {
1033         private EntityType _ospaceEntityType;
1034
1035         FieldBuilder _changeTrackerField;
1036         FieldBuilder _relationshipManagerField;
1037         FieldBuilder _resetFKSetterFlagField;
1038         FieldBuilder _compareByteArraysField;
1039
1040         MethodBuilder _entityMemberChanging;
1041         MethodBuilder _entityMemberChanged;
1042         MethodBuilder _getRelationshipManager;
1043
1044         private List<KeyValuePair<NavigationProperty, PropertyInfo>> _referenceProperties;
1045         private List<KeyValuePair<NavigationProperty, PropertyInfo>> _collectionProperties;
1046         private bool _implementIEntityWithChangeTracker;
1047         private bool _implementIEntityWithRelationships;
1048         private HashSet<EdmMember> _scalarMembers;
1049         private HashSet<EdmMember> _relationshipMembers;
1050         
1051         static readonly MethodInfo s_EntityMemberChanging = typeof(IEntityChangeTracker).GetMethod("EntityMemberChanging", new Type[] { typeof(string) });
1052         static readonly MethodInfo s_EntityMemberChanged = typeof(IEntityChangeTracker).GetMethod("EntityMemberChanged", new Type[] { typeof(string) });
1053         static readonly MethodInfo s_CreateRelationshipManager = typeof(RelationshipManager).GetMethod("Create", new Type[] { typeof(IEntityWithRelationships) });
1054         static readonly MethodInfo s_GetRelationshipManager = typeof(IEntityWithRelationships).GetProperty("RelationshipManager").GetGetMethod();
1055         static readonly MethodInfo s_GetRelatedReference = typeof(RelationshipManager).GetMethod("GetRelatedReference", new Type[] { typeof(string), typeof(string) });
1056         static readonly MethodInfo s_GetRelatedCollection = typeof(RelationshipManager).GetMethod("GetRelatedCollection", new Type[] { typeof(string), typeof(string) });
1057         static readonly MethodInfo s_GetRelatedEnd = typeof(RelationshipManager).GetMethod("GetRelatedEnd", new Type[] { typeof(string), typeof(string) });
1058         static readonly MethodInfo s_ObjectEquals = typeof(object).GetMethod("Equals", new Type[] { typeof(object), typeof(object) });
1059         static readonly ConstructorInfo s_InvalidOperationConstructor = typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) });
1060         static readonly MethodInfo s_IEntityWrapper_GetEntity = typeof(IEntityWrapper).GetProperty("Entity").GetGetMethod();
1061         static readonly MethodInfo s_Action_Invoke = typeof(Action<object>).GetMethod("Invoke", new Type[] { typeof(object) });
1062         static readonly MethodInfo s_Func_object_object_bool_Invoke = typeof(Func<object, object, bool>).GetMethod("Invoke", new Type[] { typeof(object), typeof(object) });
1063
1064         private static readonly ConstructorInfo s_BrowsableAttributeConstructor = typeof(BrowsableAttribute).GetConstructor(new Type[] { typeof(bool) });
1065
1066         public IPOCOImplementor(EntityType ospaceEntityType)
1067         {
1068             Type baseType = ospaceEntityType.ClrType;
1069             _referenceProperties = new List<KeyValuePair<NavigationProperty, PropertyInfo>>();
1070             _collectionProperties = new List<KeyValuePair<NavigationProperty, PropertyInfo>>();
1071
1072             _implementIEntityWithChangeTracker = (null == baseType.GetInterface(typeof(IEntityWithChangeTracker).Name));
1073             _implementIEntityWithRelationships = (null == baseType.GetInterface(typeof(IEntityWithRelationships).Name));
1074
1075             CheckType(ospaceEntityType);
1076
1077             _ospaceEntityType = ospaceEntityType;
1078         }
1079
1080         private void CheckType(EntityType ospaceEntityType)
1081         {
1082             _scalarMembers = new HashSet<EdmMember>();
1083             _relationshipMembers = new HashSet<EdmMember>();
1084
1085             foreach (EdmMember member in ospaceEntityType.Members)
1086             {
1087                 PropertyInfo clrProperty = EntityUtil.GetTopProperty(ospaceEntityType.ClrType, member.Name);
1088                 if (clrProperty != null && EntityProxyFactory.CanProxySetter(clrProperty))
1089                 {
1090                     if (member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
1091                     {
1092                         if (_implementIEntityWithChangeTracker)
1093                         {
1094                             _scalarMembers.Add(member);
1095                         }
1096                     }
1097                     else if (member.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
1098                     {
1099                         if (_implementIEntityWithRelationships)
1100                         {
1101                             NavigationProperty navProperty = (NavigationProperty)member;
1102                             RelationshipMultiplicity multiplicity = navProperty.ToEndMember.RelationshipMultiplicity;
1103
1104                             if (multiplicity == RelationshipMultiplicity.Many)
1105                             {
1106                                 if (clrProperty.PropertyType.IsGenericType &&
1107                                     clrProperty.PropertyType.GetGenericTypeDefinition() == typeof(ICollection<>))
1108                                 {
1109                                     _relationshipMembers.Add(member);
1110                                 }
1111                             }
1112                             else
1113                             {
1114                                 _relationshipMembers.Add(member);
1115                             }
1116                         }
1117                     }
1118                 }
1119             }
1120
1121             if (ospaceEntityType.Members.Count != _scalarMembers.Count + _relationshipMembers.Count)
1122             {
1123                 _scalarMembers.Clear();
1124                 _relationshipMembers.Clear();
1125                 _implementIEntityWithChangeTracker = false;
1126                 _implementIEntityWithRelationships = false;
1127             }
1128         }
1129
1130         public void Implement(TypeBuilder typeBuilder, Action<FieldBuilder, bool> registerField)
1131         {
1132             if (_implementIEntityWithChangeTracker)
1133             {
1134                 ImplementIEntityWithChangeTracker(typeBuilder, registerField);
1135             }
1136             if (_implementIEntityWithRelationships)
1137             {
1138                 ImplementIEntityWithRelationships(typeBuilder, registerField);
1139             }
1140
1141             _resetFKSetterFlagField = typeBuilder.DefineField(EntityProxyFactory.ResetFKSetterFlagFieldName, typeof(Action<object>), FieldAttributes.Private| FieldAttributes.Static);
1142             _compareByteArraysField = typeBuilder.DefineField(EntityProxyFactory.CompareByteArraysFieldName, typeof(Func<object, object, bool>), FieldAttributes.Private | FieldAttributes.Static);
1143         }
1144
1145         public Type[] Interfaces
1146         {
1147             get
1148             {
1149                 List<Type> types = new List<Type>();
1150                 if (_implementIEntityWithChangeTracker) { types.Add(typeof(IEntityWithChangeTracker)); }
1151                 if (_implementIEntityWithRelationships) { types.Add(typeof(IEntityWithRelationships)); }
1152                 return types.ToArray();
1153             }
1154         }
1155
1156         public DynamicMethod CreateInitalizeCollectionMethod(Type proxyType)
1157         {
1158             if (_collectionProperties.Count > 0)
1159             {
1160                 DynamicMethod initializeEntityCollections = LightweightCodeGenerator.CreateDynamicMethod(proxyType.Name + "_InitializeEntityCollections", typeof(IEntityWrapper), new Type[] { typeof(IEntityWrapper) });
1161                 ILGenerator generator = initializeEntityCollections.GetILGenerator();
1162                 generator.DeclareLocal(proxyType);
1163                 generator.DeclareLocal(typeof(RelationshipManager));
1164                 generator.Emit(OpCodes.Ldarg_0);
1165                 generator.Emit(OpCodes.Callvirt, s_IEntityWrapper_GetEntity);
1166                 generator.Emit(OpCodes.Castclass, proxyType);
1167                 generator.Emit(OpCodes.Stloc_0);
1168                 generator.Emit(OpCodes.Ldloc_0);
1169                 generator.Emit(OpCodes.Callvirt, s_GetRelationshipManager);
1170                 generator.Emit(OpCodes.Stloc_1);
1171
1172                 foreach (KeyValuePair<NavigationProperty, PropertyInfo> navProperty in _collectionProperties)
1173                 {
1174                     // Update Constructor to initialize this property
1175                     MethodInfo getRelatedCollection = s_GetRelatedCollection.MakeGenericMethod(EntityUtil.GetCollectionElementType(navProperty.Value.PropertyType));
1176
1177                     generator.Emit(OpCodes.Ldloc_0);
1178                     generator.Emit(OpCodes.Ldloc_1);
1179                     generator.Emit(OpCodes.Ldstr, navProperty.Key.RelationshipType.FullName);
1180                     generator.Emit(OpCodes.Ldstr, navProperty.Key.ToEndMember.Name);
1181                     generator.Emit(OpCodes.Callvirt, getRelatedCollection);
1182                     generator.Emit(OpCodes.Callvirt, navProperty.Value.GetSetMethod(true));
1183                 }
1184                 generator.Emit(OpCodes.Ldarg_0);
1185                 generator.Emit(OpCodes.Ret);
1186
1187                 return initializeEntityCollections;
1188             }
1189             return null;
1190         }
1191
1192         public bool CanProxyMember(EdmMember member)
1193         {
1194             return _scalarMembers.Contains(member) || _relationshipMembers.Contains(member);
1195         }
1196
1197         public bool EmitMember(TypeBuilder typeBuilder, EdmMember member, PropertyBuilder propertyBuilder, PropertyInfo baseProperty, BaseProxyImplementor baseImplementor)
1198         {
1199             if (_scalarMembers.Contains(member))
1200             {
1201                 bool isKeyMember = _ospaceEntityType.KeyMembers.Contains(member.Identity);
1202                 EmitScalarSetter(typeBuilder, propertyBuilder, baseProperty, isKeyMember);
1203                 return true;
1204             }
1205             else if (_relationshipMembers.Contains(member))
1206             {
1207                 Debug.Assert(member != null, "member is null");
1208                 Debug.Assert(member.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty);
1209                 NavigationProperty navProperty = member as NavigationProperty;
1210                 if (navProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
1211                 {
1212                     EmitCollectionProperty(typeBuilder, propertyBuilder, baseProperty, navProperty);
1213                 }
1214                 else
1215                 {
1216                     EmitReferenceProperty(typeBuilder, propertyBuilder, baseProperty, navProperty);
1217                 }
1218                 baseImplementor.AddBasePropertySetter(baseProperty);
1219                 return true;
1220             }
1221             return false;
1222         }
1223
1224         private void EmitScalarSetter(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, PropertyInfo baseProperty, bool isKeyMember)
1225         {
1226             MethodInfo baseSetter = baseProperty.GetSetMethod(true); 
1227             const MethodAttributes methodAttributes = MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;
1228             MethodAttributes methodAccess = baseSetter.Attributes & MethodAttributes.MemberAccessMask;
1229
1230             MethodBuilder setterBuilder = typeBuilder.DefineMethod("set_" + baseProperty.Name, methodAccess | methodAttributes, null, new Type[] { baseProperty.PropertyType });
1231             ILGenerator generator = setterBuilder.GetILGenerator();
1232             Label endOfMethod = generator.DefineLabel();
1233
1234             // If the CLR property represents a key member of the Entity Type,
1235             // ignore attempts to set the key value to the same value.
1236             if (isKeyMember)
1237             {
1238                 MethodInfo baseGetter = baseProperty.GetGetMethod(true);
1239
1240                 if (baseGetter != null)
1241                 {
1242                     // if (base.[Property] != value)
1243                     // { 
1244                     //     // perform set operation
1245                     // }
1246                     
1247                     Type propertyType = baseProperty.PropertyType;
1248
1249                     if (propertyType == typeof(int) ||         // signed integer types
1250                         propertyType == typeof(short) ||
1251                         propertyType == typeof(Int64) ||
1252                         propertyType == typeof(bool) ||        // boolean
1253                         propertyType == typeof(byte) ||         
1254                         propertyType == typeof(UInt32) ||
1255                         propertyType == typeof(UInt64)||
1256                         propertyType == typeof(float) ||
1257                         propertyType == typeof(double) ||
1258                         propertyType.IsEnum)
1259                     {
1260                         generator.Emit(OpCodes.Ldarg_0);
1261                         generator.Emit(OpCodes.Call, baseGetter);
1262                         generator.Emit(OpCodes.Ldarg_1);
1263                         generator.Emit(OpCodes.Beq_S, endOfMethod);
1264                     }
1265                     else if (propertyType == typeof(byte[]))
1266                     {
1267                         // Byte arrays must be compared by value
1268                         generator.Emit(OpCodes.Ldsfld, _compareByteArraysField);
1269                         generator.Emit(OpCodes.Ldarg_0);
1270                         generator.Emit(OpCodes.Call, baseGetter);
1271                         generator.Emit(OpCodes.Ldarg_1);
1272                         generator.Emit(OpCodes.Callvirt, s_Func_object_object_bool_Invoke);
1273                         generator.Emit(OpCodes.Brtrue_S, endOfMethod);
1274                     }
1275                     else
1276                     {
1277                         // Get the specific type's inequality method if it exists
1278                         MethodInfo op_inequality = propertyType.GetMethod("op_Inequality", new Type[] { propertyType, propertyType });
1279                         if (op_inequality != null)
1280                         {
1281                             generator.Emit(OpCodes.Ldarg_0);
1282                             generator.Emit(OpCodes.Call, baseGetter);
1283                             generator.Emit(OpCodes.Ldarg_1);
1284                             generator.Emit(OpCodes.Call, op_inequality);
1285                             generator.Emit(OpCodes.Brfalse_S, endOfMethod);
1286                         }
1287                         else
1288                         {
1289                             // Use object inequality
1290                             generator.Emit(OpCodes.Ldarg_0);
1291                             generator.Emit(OpCodes.Call, baseGetter);
1292                             if (propertyType.IsValueType)
1293                             {
1294                                 generator.Emit(OpCodes.Box, propertyType);
1295                             }
1296                             generator.Emit(OpCodes.Ldarg_1);
1297                             if (propertyType.IsValueType)
1298                             {
1299                                 generator.Emit(OpCodes.Box, propertyType);
1300                             }
1301                             generator.Emit(OpCodes.Call, s_ObjectEquals);
1302                             generator.Emit(OpCodes.Brtrue_S, endOfMethod);
1303                         }
1304                     }
1305                 }
1306             }
1307
1308             // Creates code like this:
1309             //
1310             // try
1311             // {
1312             //     MemberChanging(propertyName);
1313             //     base.Property_set(value);
1314             //     MemberChanged(propertyName);
1315             // }
1316             // finally
1317             // {
1318             //     _resetFKSetterFlagField(this);
1319             // }
1320             //
1321             // Note that the try/finally ensures that even if an exception causes
1322             // the setting of the property to be aborted, we still clear the flag that
1323             // indicates that we are in a property setter.
1324
1325             generator.BeginExceptionBlock();
1326             generator.Emit(OpCodes.Ldarg_0);
1327             generator.Emit(OpCodes.Ldstr, baseProperty.Name);
1328             generator.Emit(OpCodes.Call, _entityMemberChanging);
1329             generator.Emit(OpCodes.Ldarg_0);
1330             generator.Emit(OpCodes.Ldarg_1);
1331             generator.Emit(OpCodes.Call, baseSetter);
1332             generator.Emit(OpCodes.Ldarg_0);
1333             generator.Emit(OpCodes.Ldstr, baseProperty.Name);
1334             generator.Emit(OpCodes.Call, _entityMemberChanged);
1335             generator.BeginFinallyBlock();
1336             generator.Emit(OpCodes.Ldsfld, _resetFKSetterFlagField);
1337             generator.Emit(OpCodes.Ldarg_0);
1338             generator.Emit(OpCodes.Callvirt, s_Action_Invoke);
1339             generator.EndExceptionBlock();
1340             generator.MarkLabel(endOfMethod);
1341             generator.Emit(OpCodes.Ret);
1342             propertyBuilder.SetSetMethod(setterBuilder);
1343         }
1344
1345         private void EmitReferenceProperty(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, PropertyInfo baseProperty, NavigationProperty navProperty)
1346         {
1347             const MethodAttributes methodAttributes = MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;
1348             MethodInfo baseSetter = baseProperty.GetSetMethod(true); ;
1349             MethodAttributes methodAccess = baseSetter.Attributes & MethodAttributes.MemberAccessMask;
1350
1351             MethodInfo specificGetRelatedReference = s_GetRelatedReference.MakeGenericMethod(baseProperty.PropertyType);
1352             MethodInfo specificEntityReferenceSetValue = typeof(EntityReference<>).MakeGenericType(baseProperty.PropertyType).GetMethod("set_Value"); ;
1353
1354             MethodBuilder setterBuilder = typeBuilder.DefineMethod("set_" + baseProperty.Name, methodAccess | methodAttributes, null, new Type[] { baseProperty.PropertyType });
1355             ILGenerator generator = setterBuilder.GetILGenerator();
1356             generator.Emit(OpCodes.Ldarg_0);
1357             generator.Emit(OpCodes.Callvirt, _getRelationshipManager);
1358             generator.Emit(OpCodes.Ldstr, navProperty.RelationshipType.FullName);
1359             generator.Emit(OpCodes.Ldstr, navProperty.ToEndMember.Name);
1360             generator.Emit(OpCodes.Callvirt, specificGetRelatedReference);
1361             generator.Emit(OpCodes.Ldarg_1);
1362             generator.Emit(OpCodes.Callvirt, specificEntityReferenceSetValue);
1363             generator.Emit(OpCodes.Ret);
1364             propertyBuilder.SetSetMethod(setterBuilder);
1365
1366             _referenceProperties.Add(new KeyValuePair<NavigationProperty,PropertyInfo>(navProperty, baseProperty));
1367         }
1368
1369         private void EmitCollectionProperty(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, PropertyInfo baseProperty, NavigationProperty navProperty)
1370         {
1371             const MethodAttributes methodAttributes = MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Virtual;
1372             MethodInfo baseSetter = baseProperty.GetSetMethod(true); ;
1373             MethodAttributes methodAccess = baseSetter.Attributes & MethodAttributes.MemberAccessMask;
1374             
1375             string cannotSetException = System.Data.Entity.Strings.EntityProxyTypeInfo_CannotSetEntityCollectionProperty(propertyBuilder.Name, typeBuilder.Name);
1376             MethodBuilder setterBuilder = typeBuilder.DefineMethod("set_" + baseProperty.Name, methodAccess | methodAttributes, null, new Type[] { baseProperty.PropertyType });
1377             ILGenerator generator = setterBuilder.GetILGenerator();
1378             Label instanceEqual = generator.DefineLabel();
1379             generator.Emit(OpCodes.Ldarg_1);
1380             generator.Emit(OpCodes.Ldarg_0);
1381             generator.Emit(OpCodes.Call, _getRelationshipManager);
1382             generator.Emit(OpCodes.Ldstr, navProperty.RelationshipType.FullName);
1383             generator.Emit(OpCodes.Ldstr, navProperty.ToEndMember.Name);
1384             generator.Emit(OpCodes.Callvirt, s_GetRelatedEnd);
1385             generator.Emit(OpCodes.Beq_S, instanceEqual);
1386             generator.Emit(OpCodes.Ldstr, cannotSetException);
1387             generator.Emit(OpCodes.Newobj, s_InvalidOperationConstructor);
1388             generator.Emit(OpCodes.Throw);
1389             generator.MarkLabel(instanceEqual);
1390             generator.Emit(OpCodes.Ldarg_0);
1391             generator.Emit(OpCodes.Ldarg_1);
1392             generator.Emit(OpCodes.Call, baseProperty.GetSetMethod(true));
1393             generator.Emit(OpCodes.Ret);
1394             propertyBuilder.SetSetMethod(setterBuilder);
1395
1396             _collectionProperties.Add(new KeyValuePair<NavigationProperty, PropertyInfo>(navProperty, baseProperty));
1397         }
1398
1399         #region Interface Implementation
1400
1401         private void ImplementIEntityWithChangeTracker(TypeBuilder typeBuilder, Action<FieldBuilder, bool> registerField)
1402         {
1403             _changeTrackerField = typeBuilder.DefineField("_changeTracker", typeof(IEntityChangeTracker), FieldAttributes.Private);
1404             registerField(_changeTrackerField, false);
1405
1406             // Implement EntityMemberChanging(string propertyName)
1407             _entityMemberChanging = typeBuilder.DefineMethod("EntityMemberChanging", MethodAttributes.Private | MethodAttributes.HideBySig, typeof(void), new Type[] { typeof(string) });
1408             ILGenerator generator = _entityMemberChanging.GetILGenerator();
1409             Label methodEnd = generator.DefineLabel();
1410             generator.Emit(OpCodes.Ldarg_0);
1411             generator.Emit(OpCodes.Ldfld, _changeTrackerField);
1412             generator.Emit(OpCodes.Brfalse_S, methodEnd);
1413             generator.Emit(OpCodes.Ldarg_0);
1414             generator.Emit(OpCodes.Ldfld, _changeTrackerField);
1415             generator.Emit(OpCodes.Ldarg_1);
1416             generator.Emit(OpCodes.Callvirt, s_EntityMemberChanging);
1417             generator.MarkLabel(methodEnd);
1418             generator.Emit(OpCodes.Ret);
1419
1420             // Implement EntityMemberChanged(string propertyName)
1421             _entityMemberChanged = typeBuilder.DefineMethod("EntityMemberChanged", MethodAttributes.Private | MethodAttributes.HideBySig, typeof(void), new Type[] { typeof(string) });
1422             generator = _entityMemberChanged.GetILGenerator();
1423             methodEnd = generator.DefineLabel();
1424             generator.Emit(OpCodes.Ldarg_0);
1425             generator.Emit(OpCodes.Ldfld, _changeTrackerField);
1426             generator.Emit(OpCodes.Brfalse_S, methodEnd);
1427             generator.Emit(OpCodes.Ldarg_0);
1428             generator.Emit(OpCodes.Ldfld, _changeTrackerField);
1429             generator.Emit(OpCodes.Ldarg_1);
1430             generator.Emit(OpCodes.Callvirt, s_EntityMemberChanged);
1431             generator.MarkLabel(methodEnd);
1432             generator.Emit(OpCodes.Ret);
1433
1434             // Implement IEntityWithChangeTracker.SetChangeTracker(IEntityChangeTracker changeTracker)
1435             MethodBuilder setChangeTracker = typeBuilder.DefineMethod("SetChangeTracker", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final,
1436                 typeof(void), new Type[] { typeof(IEntityChangeTracker) });
1437             generator = setChangeTracker.GetILGenerator();
1438             generator.Emit(OpCodes.Ldarg_0);
1439             generator.Emit(OpCodes.Ldarg_1);
1440             generator.Emit(OpCodes.Stfld, _changeTrackerField);
1441             generator.Emit(OpCodes.Ret);
1442         }
1443
1444         private void ImplementIEntityWithRelationships(TypeBuilder typeBuilder, Action<FieldBuilder, bool> registerField)
1445         {
1446             _relationshipManagerField = typeBuilder.DefineField("_relationshipManager", typeof(RelationshipManager), FieldAttributes.Private);
1447             registerField(_relationshipManagerField, true);
1448
1449             PropertyBuilder relationshipManagerProperty = typeBuilder.DefineProperty("RelationshipManager", System.Reflection.PropertyAttributes.None, typeof(RelationshipManager), Type.EmptyTypes);
1450
1451             // Implement IEntityWithRelationships.get_RelationshipManager
1452             _getRelationshipManager = typeBuilder.DefineMethod("get_RelationshipManager", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.SpecialName | MethodAttributes.Virtual | MethodAttributes.Final,
1453                 typeof(RelationshipManager), Type.EmptyTypes);
1454             ILGenerator generator = _getRelationshipManager.GetILGenerator();
1455             Label trueLabel = generator.DefineLabel();
1456             generator.Emit(OpCodes.Ldarg_0);
1457             generator.Emit(OpCodes.Ldfld, _relationshipManagerField);
1458             generator.Emit(OpCodes.Brtrue_S, trueLabel);
1459             generator.Emit(OpCodes.Ldarg_0);
1460             generator.Emit(OpCodes.Ldarg_0);
1461             generator.Emit(OpCodes.Call, s_CreateRelationshipManager);
1462             generator.Emit(OpCodes.Stfld, _relationshipManagerField);
1463             generator.MarkLabel(trueLabel);
1464             generator.Emit(OpCodes.Ldarg_0);
1465             generator.Emit(OpCodes.Ldfld, _relationshipManagerField);
1466             generator.Emit(OpCodes.Ret);
1467             relationshipManagerProperty.SetGetMethod(_getRelationshipManager);
1468         }
1469
1470         #endregion
1471     }
1472
1473     /// <summary>
1474     /// Add a DataContractAttribute to the proxy type, based on one that may have been applied to the base type.
1475     /// </summary>
1476     /// <remarks>
1477     /// <para>
1478     /// From http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractattribute.aspx:
1479     /// 
1480     /// A data contract has two basic requirements: a stable name and a list of members. 
1481     /// The stable name consists of the namespace uniform resource identifier (URI) and the local name of the contract. 
1482     /// By default, when you apply the DataContractAttribute to a class, 
1483     /// it uses the class name as the local name and the class's namespace (prefixed with "http://schemas.datacontract.org/2004/07/") 
1484     /// as the namespace URI. You can override the defaults by setting the Name and Namespace properties. 
1485     /// You can also change the namespace by applying the ContractNamespaceAttribute to the namespace. 
1486     /// Use this capability when you have an existing type that processes data exactly as you require 
1487     /// but has a different namespace and class name from the data contract. 
1488     /// By overriding the default values, you can reuse your existing type and have the serialized data conform to the data contract. 
1489     /// </para>
1490     /// <para>
1491     /// The first attempt at WCF serialization of proxies involved adding a DataContractAttribute to the proxy type in such a way
1492     /// so that the name and namespace of the proxy's data contract matched that of the base class.
1493     /// This worked when serializing proxy objects for the root type of the DataContractSerializer, 
1494     /// but not for proxy objects of types derived from the root type.
1495     /// 
1496     /// Attempting to add the proxy type to the list of known types failed as well, 
1497     /// since the data contract of the proxy type did not match the base type as intended.
1498     /// This was due to the fact that inheritance is captured in the data contract.
1499     /// So while the proxy and base data contracts had the same members, the proxy data contract differed in that is declared itself
1500     /// as an extension of the base data contract.  So the data contracts were technically not equivalent.
1501     /// 
1502     /// The approach used instead is to allow proxy types to have their own DataContract.
1503     /// Users then have at least two options available to them.
1504     /// 
1505     /// The first approach is to add the proxy types to the list of known types.
1506     /// 
1507     /// The second approach is to implement an IDataContractSurrogate that can map a proxy instance to a surrogate that does have a data contract
1508     /// equivalent to the base type (you could use the base type itself for this purpose).  
1509     /// While more complex to implement, it allows services to hide the use of proxies from clients.
1510     /// This can be quite useful in order to maximize potential interoperability.
1511     /// </para>
1512     /// </remarks>
1513     internal sealed class DataContractImplementor
1514     {
1515         private static readonly ConstructorInfo s_DataContractAttributeConstructor = typeof(DataContractAttribute).GetConstructor(Type.EmptyTypes);
1516         private static readonly PropertyInfo[] s_DataContractProperties = new PropertyInfo[] {
1517             typeof(DataContractAttribute).GetProperty("IsReference")
1518         };
1519
1520         private readonly Type _baseClrType;
1521         private readonly DataContractAttribute _dataContract;
1522
1523         internal DataContractImplementor(EntityType ospaceEntityType)
1524         {
1525             _baseClrType = ospaceEntityType.ClrType;
1526
1527             DataContractAttribute[] attributes = (DataContractAttribute[])_baseClrType.GetCustomAttributes(typeof(DataContractAttribute), false);
1528             if (attributes.Length > 0)
1529             {
1530                 _dataContract = attributes[0];
1531             }
1532         }
1533
1534         internal void Implement(TypeBuilder typeBuilder, Action<FieldBuilder, bool> registerField)
1535         {
1536             if (_dataContract != null)
1537             {
1538                 // Use base data contract properties to help determine values of properties the proxy type's data contract.
1539                 object[] propertyValues = new object[] {
1540                     // IsReference
1541                     _dataContract.IsReference
1542                 };
1543
1544                 CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(s_DataContractAttributeConstructor, new object[0], s_DataContractProperties, propertyValues);
1545                 typeBuilder.SetCustomAttribute(attributeBuilder);
1546             }
1547         }
1548     }
1549
1550     /// <summary>
1551     /// This class determines if the proxied type implements ISerializable with the special serialization constructor.
1552     /// If it does, it adds the appropriate members to the proxy type.
1553     /// </summary>
1554     internal sealed class ISerializableImplementor 
1555     {
1556         private readonly Type _baseClrType;
1557         private readonly bool _baseImplementsISerializable;
1558         private readonly bool _canOverride;
1559         private readonly MethodInfo _getObjectDataMethod;
1560         private readonly ConstructorInfo _serializationConstructor;
1561
1562         internal ISerializableImplementor(EntityType ospaceEntityType)
1563         {
1564             _baseClrType = ospaceEntityType.ClrType;
1565             _baseImplementsISerializable = _baseClrType.IsSerializable && typeof(ISerializable).IsAssignableFrom(_baseClrType);
1566
1567             if (_baseImplementsISerializable)
1568             {
1569                 // Determine if interface implementation can be overridden.
1570                 // Fortunately, there's only one method to check.
1571                 InterfaceMapping mapping = _baseClrType.GetInterfaceMap(typeof(ISerializable));
1572                 _getObjectDataMethod = mapping.TargetMethods[0];
1573
1574                 // Members that implement interfaces must be public, unless they are explicitly implemented, in which case they are private and sealed (at least for C#).
1575                 bool canOverrideMethod = (_getObjectDataMethod.IsVirtual && !_getObjectDataMethod.IsFinal) && _getObjectDataMethod.IsPublic;
1576
1577                 if (canOverrideMethod)
1578                 {
1579                     // Determine if proxied type provides the special serialization constructor.
1580                     // In order for the proxy class to properly support ISerializable, this constructor must not be private.
1581                     _serializationConstructor = _baseClrType.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);
1582
1583                     _canOverride = _serializationConstructor != null && (_serializationConstructor.IsPublic || _serializationConstructor.IsFamily || _serializationConstructor.IsFamilyOrAssembly);
1584                 }
1585
1586                 Debug.Assert(!(_canOverride && (_getObjectDataMethod == null || _serializationConstructor == null)), "Both GetObjectData method and Serialization Constructor must be present when proxy overrides ISerializable implementation.");
1587             }
1588         }
1589
1590         internal bool TypeIsSuitable
1591         {
1592             get 
1593             {
1594                 // To be suitable,
1595                 // either proxied type doesn't implement ISerializable,
1596                 // or it does and it can be suitably overridden.
1597                 return !_baseImplementsISerializable || _canOverride;
1598             }
1599         }
1600
1601         internal bool TypeImplementsISerializable
1602         {
1603             get
1604             {
1605                 return _baseImplementsISerializable;
1606             }
1607         }
1608
1609         internal void Implement(TypeBuilder typeBuilder, IEnumerable<FieldBuilder> serializedFields)
1610         {
1611             if (_baseImplementsISerializable && _canOverride)
1612             {
1613                 PermissionSet serializationFormatterPermissions = new PermissionSet(null);
1614                 serializationFormatterPermissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter));
1615
1616                 Type[] parameterTypes = new Type[] { typeof(SerializationInfo), typeof(StreamingContext) };
1617                 MethodInfo getTypeFromHandle = typeof(Type).GetMethod("GetTypeFromHandle", new Type[] { typeof(RuntimeTypeHandle) });
1618                 MethodInfo addValue = typeof(SerializationInfo).GetMethod("AddValue", new Type[] { typeof(string), typeof(object), typeof(Type) });
1619                 MethodInfo getValue = typeof(SerializationInfo).GetMethod("GetValue", new Type[] { typeof(string), typeof(Type) });
1620
1621                 //
1622                 // Define GetObjectData method override
1623                 //
1624                 // [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)]
1625                 // public void GetObjectData(SerializationInfo info, StreamingContext context)
1626                 //
1627                 MethodBuilder proxyGetObjectData = typeBuilder.DefineMethod(_getObjectDataMethod.Name,
1628                                                                             MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
1629                                                                             null,
1630                                                                             parameterTypes);
1631                 proxyGetObjectData.AddDeclarativeSecurity(SecurityAction.Demand, serializationFormatterPermissions);
1632
1633                 {
1634                     ILGenerator generator = proxyGetObjectData.GetILGenerator();
1635
1636                     // Call SerializationInfo.AddValue to serialize each field value
1637                     foreach (FieldBuilder field in serializedFields)
1638                     {
1639                         generator.Emit(OpCodes.Ldarg_1);
1640                         generator.Emit(OpCodes.Ldstr, field.Name);
1641                         generator.Emit(OpCodes.Ldarg_0);
1642                         generator.Emit(OpCodes.Ldfld, field);
1643                         generator.Emit(OpCodes.Ldtoken, field.FieldType);
1644                         generator.Emit(OpCodes.Call, getTypeFromHandle);
1645                         generator.Emit(OpCodes.Callvirt, addValue);
1646                     }
1647
1648                     // Emit call to base method
1649                     generator.Emit(OpCodes.Ldarg_0);
1650                     generator.Emit(OpCodes.Ldarg_1);
1651                     generator.Emit(OpCodes.Ldarg_2);
1652                     generator.Emit(OpCodes.Call, _getObjectDataMethod);
1653                     generator.Emit(OpCodes.Ret);
1654                 }
1655
1656                 //
1657                 // Define serialization constructor
1658                 //
1659                 // [SecurityPermission(SecurityAction.Demand, SerializationFormatter=true)]
1660                 // .ctor(SerializationInfo info, StreamingContext context)
1661                 //
1662                 MethodAttributes constructorAttributes = MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName;
1663                 constructorAttributes |= _serializationConstructor.IsPublic? MethodAttributes.Public : MethodAttributes.Private;
1664
1665                 ConstructorBuilder proxyConstructor = typeBuilder.DefineConstructor(constructorAttributes, CallingConventions.Standard | CallingConventions.HasThis, parameterTypes);
1666                 proxyConstructor.AddDeclarativeSecurity(SecurityAction.Demand, serializationFormatterPermissions);
1667
1668                 {
1669                     //Emit call to base serialization constructor
1670                     ILGenerator generator = proxyConstructor.GetILGenerator();
1671                     generator.Emit(OpCodes.Ldarg_0);
1672                     generator.Emit(OpCodes.Ldarg_1);
1673                     generator.Emit(OpCodes.Ldarg_2);
1674                     generator.Emit(OpCodes.Call, _serializationConstructor);
1675
1676                     // Call SerializationInfo.GetValue to retrieve the value of each field
1677                     foreach (FieldBuilder field in serializedFields)
1678                     {
1679                         generator.Emit(OpCodes.Ldarg_0);
1680                         generator.Emit(OpCodes.Ldarg_1);
1681                         generator.Emit(OpCodes.Ldstr, field.Name);
1682                         generator.Emit(OpCodes.Ldtoken, field.FieldType);
1683                         generator.Emit(OpCodes.Call, getTypeFromHandle);
1684                         generator.Emit(OpCodes.Callvirt, getValue);
1685                         generator.Emit(OpCodes.Castclass, field.FieldType);
1686                         generator.Emit(OpCodes.Stfld, field);
1687                     }
1688
1689                     generator.Emit(OpCodes.Ret);
1690                 }
1691             }
1692         }
1693     }
1694 }