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