1 //---------------------------------------------------------------------
2 // <copyright file="ComplexTypeMaterializer.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 //---------------------------------------------------------------------
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
17 /// Supports materialization of complex type instances from records. Used
18 /// by the ObjectStateManager.
20 internal class ComplexTypeMaterializer
22 private readonly MetadataWorkspace _workspace;
23 private const int MaxPlanCount = 4;
24 private Plan[] _lastPlans;
25 private int _lastPlanIndex;
27 internal ComplexTypeMaterializer(MetadataWorkspace workspace)
29 _workspace = workspace;
32 internal object CreateComplex(IExtendedDataRecord record, DataRecordInfo recordInfo, object result)
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");
39 Debug.Assert(Helper.IsEntityType(recordInfo.RecordType.EdmType) ||
40 Helper.IsComplexType(recordInfo.RecordType.EdmType),
41 "not EntityType or ComplexType");
43 Plan plan = GetPlan(record, recordInfo);
46 result = ((Func<object>)plan.ClrType)();
48 SetProperties(record, result, plan.Properties);
52 private void SetProperties(IExtendedDataRecord record, object result, PlanEdmProperty[] properties)
54 Debug.Assert(null != record, "null IExtendedDataRecord");
55 Debug.Assert(null != result, "null object");
56 Debug.Assert(null != properties, "null object");
58 for (int i = 0; i < properties.Length; ++i)
60 if (null != properties[i].GetExistingComplex)
62 object existing = properties[i].GetExistingComplex(result);
63 object obj = CreateComplexRecursive(record.GetValue(properties[i].Ordinal), existing);
66 properties[i].ClrProperty(result, obj);
71 properties[i].ClrProperty(result,
74 properties[i].Ordinal)));
79 private static object ConvertDBNull(object value)
81 return ((DBNull.Value != value) ? value : null);
84 private object CreateComplexRecursive(object record, object existing)
86 return ((DBNull.Value != record) ? CreateComplexRecursive((IExtendedDataRecord)record, existing) : existing);
89 private object CreateComplexRecursive(IExtendedDataRecord record, object existing)
91 return CreateComplex(record, record.DataRecordInfo, existing);
94 private Plan GetPlan(IExtendedDataRecord record, DataRecordInfo recordInfo)
96 Debug.Assert(null != record, "null IExtendedDataRecord");
97 Debug.Assert(null != recordInfo, "null DataRecordInfo");
98 Debug.Assert(null != recordInfo.RecordType, "null TypeUsage");
100 Plan[] plans = _lastPlans ?? (_lastPlans = new Plan[MaxPlanCount]);
102 // find an existing plan in circular buffer
103 int index = _lastPlanIndex - 1;
104 for (int i = 0; i < MaxPlanCount; ++i)
106 index = (index + 1) % MaxPlanCount;
107 if (null == plans[index])
111 if (plans[index].Key == recordInfo.RecordType)
113 _lastPlanIndex = index;
117 Debug.Assert(0 <= index, "negative index");
118 Debug.Assert(index != _lastPlanIndex || (null == plans[index]), "index wrapped around");
121 ObjectTypeMapping mapping = System.Data.Common.Internal.Materialization.Util.GetObjectMapping(recordInfo.RecordType.EdmType, _workspace);
122 Debug.Assert(null != mapping, "null ObjectTypeMapping");
124 Debug.Assert(Helper.IsComplexType(recordInfo.RecordType.EdmType),
125 "IExtendedDataRecord is not ComplexType");
127 _lastPlanIndex = index;
128 plans[index] = new Plan(recordInfo.RecordType, mapping, recordInfo.FieldMetadata);
132 private sealed class Plan
134 internal readonly TypeUsage Key;
135 internal readonly Delegate ClrType;
136 internal readonly PlanEdmProperty[] Properties;
138 internal Plan(TypeUsage key, ObjectTypeMapping mapping, System.Collections.ObjectModel.ReadOnlyCollection<FieldMetadata> fields)
140 Debug.Assert(null != mapping, "null ObjectTypeMapping");
141 Debug.Assert(null != fields, "null FieldMetadata");
144 Debug.Assert(!Helper.IsEntityType(mapping.ClrType), "Expecting complex type");
145 ClrType = LightweightCodeGenerator.GetConstructorDelegateForType((ClrComplexType)mapping.ClrType);
146 Properties = new PlanEdmProperty[fields.Count];
148 int lastOrdinal = -1;
149 for (int i = 0; i < Properties.Length; ++i)
151 FieldMetadata field = fields[i];
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;
157 Properties[i] = new PlanEdmProperty(lastOrdinal, mapping.GetPropertyMap(field.FieldType.Name).ClrProperty);
162 private struct PlanEdmProperty
164 internal readonly int Ordinal;
165 internal readonly Func<object, object> GetExistingComplex;
166 internal readonly Action<object, object> ClrProperty;
168 internal PlanEdmProperty(int ordinal, EdmProperty property)
170 Debug.Assert(0 <= ordinal, "negative ordinal");
171 Debug.Assert(null != property, "unsupported shadow state");
173 this.Ordinal = ordinal;
174 this.GetExistingComplex = Helper.IsComplexType(property.TypeUsage.EdmType)
175 ? LightweightCodeGenerator.GetGetterDelegateForProperty(property) : null;
176 this.ClrProperty = LightweightCodeGenerator.GetSetterDelegateForProperty(property);