1 //---------------------------------------------------------------------
2 // <copyright file="ComplexTypeMaterializer.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner       Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9 
10 using System.Data.Metadata.Edm;
11 using System.Data.Common;
12 using System.Diagnostics;
13 using System.Data.Mapping;
14 namespace System.Data.Objects.Internal
15 {
16     /// <summary>
17     /// Supports materialization of complex type instances from records. Used
18     /// by the ObjectStateManager.
19     /// </summary>
20     internal class ComplexTypeMaterializer
21     {
22         private readonly MetadataWorkspace _workspace;
23         private const int MaxPlanCount = 4;
24         private Plan[] _lastPlans;
25         private int _lastPlanIndex;
26 
ComplexTypeMaterializer(MetadataWorkspace workspace)27         internal ComplexTypeMaterializer(MetadataWorkspace workspace)
28         {
29             _workspace = workspace;
30         }
31 
CreateComplex(IExtendedDataRecord record, DataRecordInfo recordInfo, object result)32         internal object CreateComplex(IExtendedDataRecord record, DataRecordInfo recordInfo, object result)
33         {
34             Debug.Assert(null != record, "null IExtendedDataRecord");
35             Debug.Assert(null != recordInfo, "null DataRecordInfo");
36             Debug.Assert(null != recordInfo.RecordType, "null TypeUsage");
37             Debug.Assert(null != recordInfo.RecordType.EdmType, "null EdmType");
38 
39             Debug.Assert(Helper.IsEntityType(recordInfo.RecordType.EdmType) ||
40                          Helper.IsComplexType(recordInfo.RecordType.EdmType),
41                          "not EntityType or ComplexType");
42 
43             Plan plan = GetPlan(record, recordInfo);
44             if (null == result)
45             {
46                 result = ((Func<object>)plan.ClrType)();
47             }
48             SetProperties(record, result, plan.Properties);
49             return result;
50         }
51 
SetProperties(IExtendedDataRecord record, object result, PlanEdmProperty[] properties)52         private void SetProperties(IExtendedDataRecord record, object result, PlanEdmProperty[] properties)
53         {
54             Debug.Assert(null != record, "null IExtendedDataRecord");
55             Debug.Assert(null != result, "null object");
56             Debug.Assert(null != properties, "null object");
57 
58             for (int i = 0; i < properties.Length; ++i)
59             {
60                 if (null != properties[i].GetExistingComplex)
61                 {
62                     object existing = properties[i].GetExistingComplex(result);
63                     object obj = CreateComplexRecursive(record.GetValue(properties[i].Ordinal), existing);
64                     if (null == existing)
65                     {
66                         properties[i].ClrProperty(result, obj);
67                     }
68                 }
69                 else
70                 {
71                     properties[i].ClrProperty(result,
72                         ConvertDBNull(
73                             record.GetValue(
74                                 properties[i].Ordinal)));
75                 }
76             }
77         }
78 
ConvertDBNull(object value)79         private static object ConvertDBNull(object value)
80         {
81             return ((DBNull.Value != value) ? value : null);
82         }
83 
CreateComplexRecursive(object record, object existing)84         private object CreateComplexRecursive(object record, object existing)
85         {
86             return ((DBNull.Value != record) ? CreateComplexRecursive((IExtendedDataRecord)record, existing) : existing);
87         }
88 
CreateComplexRecursive(IExtendedDataRecord record, object existing)89         private object CreateComplexRecursive(IExtendedDataRecord record, object existing)
90         {
91             return CreateComplex(record, record.DataRecordInfo, existing);
92         }
93 
GetPlan(IExtendedDataRecord record, DataRecordInfo recordInfo)94         private Plan GetPlan(IExtendedDataRecord record, DataRecordInfo recordInfo)
95         {
96             Debug.Assert(null != record, "null IExtendedDataRecord");
97             Debug.Assert(null != recordInfo, "null DataRecordInfo");
98             Debug.Assert(null != recordInfo.RecordType, "null TypeUsage");
99 
100             Plan[] plans = _lastPlans ?? (_lastPlans = new Plan[MaxPlanCount]);
101 
102             // find an existing plan in circular buffer
103             int index = _lastPlanIndex - 1;
104             for (int i = 0; i < MaxPlanCount; ++i)
105             {
106                 index = (index + 1) % MaxPlanCount;
107                 if (null == plans[index])
108                 {
109                     break;
110                 }
111                 if (plans[index].Key == recordInfo.RecordType)
112                 {
113                     _lastPlanIndex = index;
114                     return plans[index];
115                 }
116             }
117             Debug.Assert(0 <= index, "negative index");
118             Debug.Assert(index != _lastPlanIndex || (null == plans[index]), "index wrapped around");
119 
120             // create a new plan
121             ObjectTypeMapping mapping = System.Data.Common.Internal.Materialization.Util.GetObjectMapping(recordInfo.RecordType.EdmType, _workspace);
122             Debug.Assert(null != mapping, "null ObjectTypeMapping");
123 
124             Debug.Assert(Helper.IsComplexType(recordInfo.RecordType.EdmType),
125                          "IExtendedDataRecord is not ComplexType");
126 
127             _lastPlanIndex = index;
128             plans[index] = new Plan(recordInfo.RecordType, mapping, recordInfo.FieldMetadata);
129             return plans[index];
130         }
131 
132         private sealed class Plan
133         {
134             internal readonly TypeUsage Key;
135             internal readonly Delegate ClrType;
136             internal readonly PlanEdmProperty[] Properties;
137 
Plan(TypeUsage key, ObjectTypeMapping mapping, System.Collections.ObjectModel.ReadOnlyCollection<FieldMetadata> fields)138             internal Plan(TypeUsage key, ObjectTypeMapping mapping, System.Collections.ObjectModel.ReadOnlyCollection<FieldMetadata> fields)
139             {
140                 Debug.Assert(null != mapping, "null ObjectTypeMapping");
141                 Debug.Assert(null != fields, "null FieldMetadata");
142 
143                 Key = key;
144                 Debug.Assert(!Helper.IsEntityType(mapping.ClrType), "Expecting complex type");
145                 ClrType = LightweightCodeGenerator.GetConstructorDelegateForType((ClrComplexType)mapping.ClrType);
146                 Properties = new PlanEdmProperty[fields.Count];
147 
148                 int lastOrdinal = -1;
149                 for (int i = 0; i < Properties.Length; ++i)
150                 {
151                     FieldMetadata field = fields[i];
152 
153                     Debug.Assert(unchecked((uint)field.Ordinal) < unchecked((uint)fields.Count), "FieldMetadata.Ordinal out of range of Fields.Count");
154                     Debug.Assert(lastOrdinal < field.Ordinal, "FieldMetadata.Ordinal is not increasing");
155                     lastOrdinal = field.Ordinal;
156 
157                     Properties[i] = new PlanEdmProperty(lastOrdinal, mapping.GetPropertyMap(field.FieldType.Name).ClrProperty);
158                 }
159             }
160         }
161 
162         private struct PlanEdmProperty
163         {
164             internal readonly int Ordinal;
165             internal readonly Func<object, object> GetExistingComplex;
166             internal readonly Action<object, object> ClrProperty;
167 
PlanEdmPropertySystem.Data.Objects.Internal.ComplexTypeMaterializer.PlanEdmProperty168             internal PlanEdmProperty(int ordinal, EdmProperty property)
169             {
170                 Debug.Assert(0 <= ordinal, "negative ordinal");
171                 Debug.Assert(null != property, "unsupported shadow state");
172 
173                 this.Ordinal = ordinal;
174                 this.GetExistingComplex = Helper.IsComplexType(property.TypeUsage.EdmType)
175                     ? LightweightCodeGenerator.GetGetterDelegateForProperty(property) : null;
176                 this.ClrProperty = LightweightCodeGenerator.GetSetterDelegateForProperty(property);
177             }
178         }
179     }
180 }
181