1 //---------------------------------------------------------------------
2 // <copyright file="InitializerFacet.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner  Microsoft
7 //---------------------------------------------------------------------
8 
9 namespace System.Data.Objects.ELinq
10 {
11     using System.Collections.Generic;
12     using System.Data.Common;
13     using System.Data.Common.Internal.Materialization;
14     using System.Data.Metadata.Edm;
15     using System.Data.Objects.DataClasses;
16     using System.Data.Objects.Internal;
17     using System.Diagnostics;
18     using System.Globalization;
19     using System.Linq;
20     using System.Linq.Expressions;
21     using System.Reflection;
22     using System.Security;
23     using System.Security.Permissions;
24     using System.Threading;
25 
26     /// <summary>
27     /// Facet encapsulating information necessary to initialize a LINQ projection
28     /// result.
29     /// </summary>
30     internal abstract class InitializerMetadata : IEquatable<InitializerMetadata>
31     {
32         internal readonly Type ClrType;
33         internal static readonly MethodInfo UserExpressionMarker = typeof(InitializerMetadata).GetMethod("MarkAsUserExpression", BindingFlags.NonPublic | BindingFlags.Static);
34         private static long s_identifier;
35         internal readonly string Identity;
36         private static readonly string s_identifierPrefix = typeof(InitializerMetadata).Name;
37 
InitializerMetadata(Type clrType)38         private InitializerMetadata(Type clrType)
39         {
40             Debug.Assert(null != clrType);
41             ClrType = clrType;
42             Identity = s_identifierPrefix + Interlocked.Increment(ref s_identifier).ToString(CultureInfo.InvariantCulture);
43         }
44 
45         // Gets the kind of this initializer (grouping, row, etc.)
46         internal abstract InitializerMetadataKind Kind { get; }
47 
48         // Attempts to retrieve the initializer facet from a type usage
TryGetInitializerMetadata(TypeUsage typeUsage, out InitializerMetadata initializerMetadata)49         internal static bool TryGetInitializerMetadata(TypeUsage typeUsage, out InitializerMetadata initializerMetadata)
50         {
51             initializerMetadata = null;
52             if (BuiltInTypeKind.RowType == typeUsage.EdmType.BuiltInTypeKind)
53             {
54                 initializerMetadata = ((RowType)typeUsage.EdmType).InitializerMetadata;
55             }
56             return null != initializerMetadata;
57         }
58 
59         // Initializes an initializer for an IGrouping return type
60         // Requires: resultType is IGrouping<T, K> instance.
CreateGroupingInitializer(EdmItemCollection itemCollection, Type resultType)61         internal static InitializerMetadata CreateGroupingInitializer(EdmItemCollection itemCollection, Type resultType)
62         {
63             return itemCollection.GetCanonicalInitializerMetadata(new GroupingInitializerMetadata(resultType));
64         }
65 
66         // Initializes an initializer for a MemberInit expression
CreateProjectionInitializer(EdmItemCollection itemCollection, MemberInitExpression initExpression, MemberInfo[] members)67         internal static InitializerMetadata CreateProjectionInitializer(EdmItemCollection itemCollection, MemberInitExpression initExpression,
68             MemberInfo[] members)
69         {
70             return itemCollection.GetCanonicalInitializerMetadata(new ProjectionInitializerMetadata(initExpression, members));
71         }
72 
73         // Initializes an initializer for a New expression
CreateProjectionInitializer(EdmItemCollection itemCollection, NewExpression newExpression)74         internal static InitializerMetadata CreateProjectionInitializer(EdmItemCollection itemCollection, NewExpression newExpression)
75         {
76             return itemCollection.GetCanonicalInitializerMetadata(new ProjectionNewMetadata(newExpression));
77         }
78 
79         // Initializes an initializer for a New expression with no properties
CreateEmptyProjectionInitializer(EdmItemCollection itemCollection, NewExpression newExpression)80         internal static InitializerMetadata CreateEmptyProjectionInitializer(EdmItemCollection itemCollection, NewExpression newExpression)
81         {
82             return itemCollection.GetCanonicalInitializerMetadata(new EmptyProjectionNewMetadata(newExpression));
83         }
84 
85         // Creates metadata for entity collection materialization
CreateEntityCollectionInitializer(EdmItemCollection itemCollection, Type type, NavigationProperty navigationProperty)86         internal static InitializerMetadata CreateEntityCollectionInitializer(EdmItemCollection itemCollection, Type type, NavigationProperty navigationProperty)
87         {
88             return itemCollection.GetCanonicalInitializerMetadata(new EntityCollectionInitializerMetadata(type, navigationProperty));
89         }
90 
MarkAsUserExpression(T value)91         private static T MarkAsUserExpression<T>(T value)
92         {
93             // No op. This is used as a marker inside of an expression tree to indicate
94             // that the input expression is not trusted.
95             return value;
96         }
97 
AppendColumnMapKey(ColumnMapKeyBuilder builder)98         internal virtual void AppendColumnMapKey(ColumnMapKeyBuilder builder)
99         {
100             // by default, the type is sufficient (more information is needed for EntityCollection and initializers)
101             builder.Append("CLR-", this.ClrType);
102         }
103 
Equals(object obj)104         public override bool Equals(object obj)
105         {
106             Debug.Fail("use typed Equals method only");
107             return Equals(obj as InitializerMetadata);
108         }
109 
Equals(InitializerMetadata other)110         public bool Equals(InitializerMetadata other)
111         {
112             Debug.Assert(null != other, "must not use a null key");
113             if (object.ReferenceEquals(this, other)) { return true; }
114             if (this.Kind != other.Kind) { return false; }
115             if (!this.ClrType.Equals(other.ClrType)) { return false; }
116             return IsStructurallyEquivalent(other);
117         }
118 
119         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2303", Justification="ClrType is not expected to be an Embedded Interop Type.")]
GetHashCode()120         public override int GetHashCode()
121         {
122             return ClrType.GetHashCode();
123         }
124 
125         /// <summary>
126         /// Requires: other has the same type as this and refers to the same CLR type
127         /// Determine whether this Metadata is compatible with the other based on record layout.
128         /// </summary>
IsStructurallyEquivalent(InitializerMetadata other)129         protected virtual bool IsStructurallyEquivalent(InitializerMetadata other)
130         {
131             return true;
132         }
133 
134         /// <summary>
135         /// Produces an expression initializing an instance of ClrType (given emitters for input
136         /// columns)
137         /// </summary>
Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)138         internal abstract Expression Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults);
139 
140         /// <summary>
141         /// Yields expected types for input columns. Null values are returned for children
142         /// whose type is irrelevant to the initializer.
143         /// </summary>
GetChildTypes()144         internal abstract IEnumerable<Type> GetChildTypes();
145 
146         /// <summary>
147         /// return a list of propertyReader expressions from an array of translator results.
148         /// </summary>
149         /// <param name="propertyTranslatorResults"></param>
150         /// <returns></returns>
GetPropertyReaders(List<TranslatorResult> propertyTranslatorResults)151         protected static List<Expression> GetPropertyReaders(List<TranslatorResult> propertyTranslatorResults)
152         {
153             List<Expression> propertyReaders = propertyTranslatorResults.Select(s => s.UnwrappedExpression).ToList();
154             return propertyReaders;
155         }
156 
157         /// <summary>
158         /// Implementation of IGrouping that can be initialized using the standard
159         /// initializer pattern supported by ELinq
160         /// </summary>
161         /// <typeparam name="K">Type of key</typeparam>
162         /// <typeparam name="T">Type of record</typeparam>
163         private class Grouping<K, T> : IGrouping<K, T>
164         {
Grouping(K key, IEnumerable<T> group)165             public Grouping(K key, IEnumerable<T> group)
166             {
167                 _key = key;
168                 _group = group;
169             }
170 
171             private readonly K _key;
172             private readonly IEnumerable<T> _group;
173 
174             public K Key
175             {
176                 get { return _key; }
177             }
178 
179             public IEnumerable<T> Group
180             {
181                 get { return _group; }
182             }
183 
GetEnumerator()184             IEnumerator<T> IEnumerable<T>.GetEnumerator()
185             {
186                 if (null == _group)
187                 {
188                     yield break;
189                 }
190                 foreach (T member in _group)
191                 {
192                     yield return member;
193                 }
194             }
195 
System.Collections.IEnumerable.GetEnumerator()196             System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
197             {
198                 return ((IEnumerable<T>)this).GetEnumerator();
199             }
200         }
201 
202         /// <summary>
203         /// Metadata for grouping initializer.
204         /// </summary>
205         private class GroupingInitializerMetadata : InitializerMetadata
206         {
GroupingInitializerMetadata(Type type)207             internal GroupingInitializerMetadata(Type type)
208                 : base(type)
209             {
210             }
211 
212             internal override InitializerMetadataKind Kind { get { return InitializerMetadataKind.Grouping; } }
213 
Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)214             internal override Expression Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)
215             {
216                 // Create expression of the form:
217                 // new Grouping<K, T>(children[0], children[1])
218 
219                 // Collect information...
220                 Debug.Assert(ClrType.IsGenericType &&
221                     typeof(IGrouping<,>).Equals(ClrType.GetGenericTypeDefinition()));
222                 Debug.Assert(propertyTranslatorResults.Count == 2);
223                 Type keyType = this.ClrType.GetGenericArguments()[0];
224                 Type groupElementType = this.ClrType.GetGenericArguments()[1];
225                 Type groupType = typeof(Grouping<,>).MakeGenericType(keyType, groupElementType);
226                 ConstructorInfo constructor = groupType.GetConstructors().Single();
227 
228                 // new Grouping<K, T>(children[0], children[1])
229                 Expression newGrouping = Expression.Convert(Expression.New(constructor, GetPropertyReaders(propertyTranslatorResults)), this.ClrType);
230 
231                 return newGrouping;
232             }
233 
GetChildTypes()234             internal override IEnumerable<Type> GetChildTypes()
235             {
236                 // Collect information...
237                 Debug.Assert(ClrType.IsGenericType &&
238                     typeof(IGrouping<,>).Equals(ClrType.GetGenericTypeDefinition()));
239                 Type keyType = this.ClrType.GetGenericArguments()[0];
240                 Type groupElementType = this.ClrType.GetGenericArguments()[1];
241 
242                 // key
243                 yield return keyType;
244                 // group
245                 yield return typeof(IEnumerable<>).MakeGenericType(groupElementType);
246             }
247         }
248 
249         /// <summary>
250         /// Metadata for anonymous type materialization.
251         /// </summary>
252         private class ProjectionNewMetadata : InitializerMetadata
253         {
ProjectionNewMetadata(NewExpression newExpression)254             internal ProjectionNewMetadata(NewExpression newExpression)
255                 : base(newExpression.Type)
256             {
257                 Debug.Assert(null != newExpression);
258                 _newExpression = newExpression;
259             }
260 
261             private readonly NewExpression _newExpression;
262 
263             internal override InitializerMetadataKind Kind { get { return InitializerMetadataKind.ProjectionNew; } }
264 
IsStructurallyEquivalent(InitializerMetadata other)265             protected override bool IsStructurallyEquivalent(InitializerMetadata other)
266             {
267                 // caller must ensure the type matches
268                 ProjectionNewMetadata otherProjection = (ProjectionNewMetadata)other;
269                 if (this._newExpression.Members == null && otherProjection._newExpression.Members == null)
270                 {
271                     return true;
272                 }
273 
274                 if (this._newExpression.Members == null || otherProjection._newExpression.Members == null)
275                 {
276                     return false;
277                 }
278 
279                 if (this._newExpression.Members.Count != otherProjection._newExpression.Members.Count)
280                 {
281                     return false;
282                 }
283 
284                 for (int i = 0; i < this._newExpression.Members.Count; i++)
285                 {
286                     MemberInfo thisMember = this._newExpression.Members[i];
287                     MemberInfo otherMember = otherProjection._newExpression.Members[i];
288                     if (!thisMember.Equals(otherMember))
289                     {
290                         return false;
291                     }
292                 }
293 
294                 return true;
295             }
296 
Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)297             internal override Expression Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)
298             {
299                 // Create expression of the form:
300                 // _newExpression(children)
301 
302                 // (ClrType)null
303                 Expression nullProjection = Expression.Constant(null, this.ClrType);
304 
305                 // _newExpression with members rebound
306                 Expression newProjection = Expression.New(_newExpression.Constructor, GetPropertyReaders(propertyTranslatorResults));
307 
308                 // Indicate that this expression is provided by the user and should not be trusted.
309                 return Expression.Call(UserExpressionMarker.MakeGenericMethod(newProjection.Type), newProjection);
310             }
311 
GetChildTypes()312             internal override IEnumerable<Type> GetChildTypes()
313             {
314                 // return all argument types
315                 return _newExpression.Arguments.Select(arg => arg.Type);
316             }
317 
AppendColumnMapKey(ColumnMapKeyBuilder builder)318             internal override void AppendColumnMapKey(ColumnMapKeyBuilder builder)
319             {
320                 base.AppendColumnMapKey(builder);
321                 builder.Append(_newExpression.Constructor.ToString());
322                 foreach (var member in _newExpression.Members ?? Enumerable.Empty<MemberInfo>())
323                 {
324                     builder.Append("DT", member.DeclaringType);
325                     builder.Append("." + member.Name);
326                 }
327             }
328         }
329 
330         private class EmptyProjectionNewMetadata : ProjectionNewMetadata
331         {
EmptyProjectionNewMetadata(NewExpression newExpression)332             internal EmptyProjectionNewMetadata(NewExpression newExpression)
333                 : base(newExpression)
334             {
335             }
Emit(Translator translator, List<TranslatorResult> propertyReaders)336             internal override Expression Emit(Translator translator, List<TranslatorResult> propertyReaders)
337             {
338                 // ignore sentinel column
339                 return base.Emit(translator, new List<TranslatorResult>());
340             }
GetChildTypes()341             internal override IEnumerable<Type> GetChildTypes()
342             {
343                 // ignore sentinel column
344                 yield return null;
345             }
346         }
347 
348         /// <summary>
349         /// Metadata for standard projection initializers.
350         /// </summary>
351         private class ProjectionInitializerMetadata : InitializerMetadata
352         {
ProjectionInitializerMetadata(MemberInitExpression initExpression, MemberInfo[] members)353             internal ProjectionInitializerMetadata(MemberInitExpression initExpression, MemberInfo[] members)
354                 : base(initExpression.Type)
355             {
356                 Debug.Assert(null != initExpression);
357                 Debug.Assert(null != members);
358                 _initExpression = initExpression;
359                 _members = members;
360             }
361 
362             private readonly MemberInitExpression _initExpression;
363             private readonly MemberInfo[] _members;
364 
365             internal override InitializerMetadataKind Kind { get { return InitializerMetadataKind.ProjectionInitializer; } }
366 
IsStructurallyEquivalent(InitializerMetadata other)367             protected override bool IsStructurallyEquivalent(InitializerMetadata other)
368             {
369                 // caller must ensure the type matches
370                 ProjectionInitializerMetadata otherProjection = (ProjectionInitializerMetadata)other;
371                 if (this._initExpression.Bindings.Count != otherProjection._initExpression.Bindings.Count)
372                 {
373                     return false;
374                 }
375 
376                 for (int i = 0; i < this._initExpression.Bindings.Count; i++)
377                 {
378                     MemberBinding thisBinding = this._initExpression.Bindings[i];
379                     MemberBinding otherBinding = otherProjection._initExpression.Bindings[i];
380                     if (!thisBinding.Member.Equals(otherBinding.Member))
381                     {
382                         return false;
383                     }
384                 }
385 
386                 return true;
387             }
388 
Emit(Translator translator, List<TranslatorResult> propertyReaders)389             internal override Expression Emit(Translator translator, List<TranslatorResult> propertyReaders)
390             {
391                 // Create expression of the form:
392                 // _initExpression(children)
393 
394                 // create member bindings (where values are taken from children)
395                 MemberBinding[] memberBindings = new MemberBinding[_initExpression.Bindings.Count];
396                 MemberBinding[] constantMemberBindings = new MemberBinding[memberBindings.Length];
397                 for (int i = 0; i < memberBindings.Length; i++)
398                 {
399                     MemberBinding originalBinding = _initExpression.Bindings[i];
400                     Expression value = propertyReaders[i].UnwrappedExpression;
401                     MemberBinding newBinding = Expression.Bind(originalBinding.Member, value);
402                     MemberBinding constantBinding = Expression.Bind(originalBinding.Member, Expression.Constant(
403                         TypeSystem.GetDefaultValue(value.Type), value.Type));
404                     memberBindings[i] = newBinding;
405                     constantMemberBindings[i] = constantBinding;
406                 }
407 
408                 Expression newProjection = Expression.MemberInit(_initExpression.NewExpression, memberBindings);
409 
410                 // Indicate that this expression is provided by the user and should not be trusted.
411                 return Expression.Call(UserExpressionMarker.MakeGenericMethod(newProjection.Type), newProjection);
412             }
413 
GetChildTypes()414             internal override IEnumerable<Type> GetChildTypes()
415             {
416                 // return all argument types
417                 foreach (var binding in _initExpression.Bindings)
418                 {
419                     // determine member type
420                     Type memberType;
421                     string name;
422                     TypeSystem.PropertyOrField(binding.Member, out name, out memberType);
423                     yield return memberType;
424                 }
425             }
426 
AppendColumnMapKey(ColumnMapKeyBuilder builder)427             internal override void AppendColumnMapKey(ColumnMapKeyBuilder builder)
428             {
429                 base.AppendColumnMapKey(builder);
430                 foreach (var binding in _initExpression.Bindings)
431                 {
432                     builder.Append(",", binding.Member.DeclaringType);
433                     builder.Append("." + binding.Member.Name);
434                 }
435             }
436         }
437 
438         /// <summary>
439         /// Metadata for entity collection initializer.
440         /// </summary>
441         private class EntityCollectionInitializerMetadata : InitializerMetadata
442         {
EntityCollectionInitializerMetadata(Type type, NavigationProperty navigationProperty)443             internal EntityCollectionInitializerMetadata(Type type, NavigationProperty navigationProperty)
444                 : base(type)
445             {
446                 Debug.Assert(null != navigationProperty);
447                 _navigationProperty = navigationProperty;
448             }
449 
450             private readonly NavigationProperty _navigationProperty;
451 
452             internal override InitializerMetadataKind Kind { get { return InitializerMetadataKind.EntityCollection; } }
453 
454             /// <summary>
455             /// Make sure the other metadata instance generates the same property
456             /// (otherwise, we get incorrect behavior where multiple nav props return
457             /// the same type)
458             /// </summary>
IsStructurallyEquivalent(InitializerMetadata other)459             protected override bool IsStructurallyEquivalent(InitializerMetadata other)
460             {
461                 // caller must ensure the type matches
462                 EntityCollectionInitializerMetadata otherInitializer = (EntityCollectionInitializerMetadata)other;
463                 return this._navigationProperty.Equals(otherInitializer._navigationProperty);
464             }
465 
466             private static readonly MethodInfo s_createEntityCollectionMethod = typeof(EntityCollectionInitializerMetadata).GetMethod("CreateEntityCollection",
467                 BindingFlags.Static | BindingFlags.Public);
468 
Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)469             internal override Expression Emit(Translator translator, List<TranslatorResult> propertyTranslatorResults)
470             {
471                 Debug.Assert(propertyTranslatorResults.Count > 1, "no properties?");
472                 Debug.Assert(propertyTranslatorResults[1] is CollectionTranslatorResult, "not a collection?");
473 
474                 Type elementType = GetElementType();
475                 MethodInfo createEntityCollectionMethod = s_createEntityCollectionMethod.MakeGenericMethod(elementType);
476 
477                 Expression shaper = Translator.Shaper_Parameter;
478                 Expression owner = propertyTranslatorResults[0].Expression;
479 
480                 CollectionTranslatorResult collectionResult = propertyTranslatorResults[1] as CollectionTranslatorResult;
481 
482                 Expression coordinator = collectionResult.ExpressionToGetCoordinator;
483 
484                 // CreateEntityCollection(shaper, owner, elements, relationshipName, targetRoleName)
485                 Expression result = Expression.Call(createEntityCollectionMethod,
486                     shaper, owner, coordinator, Expression.Constant(_navigationProperty.RelationshipType.FullName), Expression.Constant(_navigationProperty.ToEndMember.Name));
487 
488                 return result;
489             }
490 
491             public static EntityCollection<T> CreateEntityCollection<T>(Shaper state, IEntityWrapper wrappedOwner, Coordinator<T> coordinator, string relationshipName, string targetRoleName)
492                 where T : class
493             {
494                 if (null == wrappedOwner.Entity)
495                 {
496                     return null;
497                 }
498                 else
499                 {
500                     EntityCollection<T> result = wrappedOwner.RelationshipManager.GetRelatedCollection<T>(relationshipName, targetRoleName);
501                     // register a handler for deferred loading (when the nested result has been consumed)
502                     coordinator.RegisterCloseHandler((readerState, elements) => result.Load(elements, readerState.MergeOption));
503                     return result;
504                 }
505             }
506 
GetChildTypes()507             internal override IEnumerable<Type> GetChildTypes()
508             {
509                 Type elementType = GetElementType();
510                 yield return null; // defer in determining entity type...
511                 yield return typeof(IEnumerable<>).MakeGenericType(elementType);
512             }
513 
514 
AppendColumnMapKey(ColumnMapKeyBuilder builder)515             internal override void AppendColumnMapKey(ColumnMapKeyBuilder builder)
516             {
517                 base.AppendColumnMapKey(builder);
518                 builder.Append(",NP" + _navigationProperty.Name);
519                 builder.Append(",AT", _navigationProperty.DeclaringType);
520             }
521 
GetElementType()522             private Type GetElementType()
523             {
524                 // POCO support requires that we allow ICollection<T> collections.  This allows a POCO collection
525                 // to be projected in a LINQ query.
526                 Type elementType;
527                 if (!EntityUtil.TryGetICollectionElementType(ClrType, out elementType))
528                 {
529                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.ELinq_UnexpectedTypeForNavigationProperty(
530                         _navigationProperty,
531                         typeof(EntityCollection<>), typeof(ICollection<>),
532                         ClrType));
533                 }
534                 return elementType;
535             }
536         }
537     }
538 
539     internal enum InitializerMetadataKind
540     {
541         Grouping,
542         ProjectionNew,
543         ProjectionInitializer,
544         EntityCollection,
545     }
546 }
547