1 //------------------------------------------------------------------------------
2 // <copyright file="DataRecord.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System.Data.Objects
11 using System.Collections.Generic;
13 using System.Data.Common;
14 using System.Data.Metadata.Edm;
15 using System.Data.Objects;
16 using System.Diagnostics;
17 using System.ComponentModel;
20 /// Instances of this class would be returned to user via Query<T>
22 internal sealed class MaterializedDataRecord : DbDataRecord, IExtendedDataRecord, ICustomTypeDescriptor
24 private FieldNameLookup _fieldNameLookup;
25 private DataRecordInfo _recordInfo;
26 private readonly MetadataWorkspace _workspace;
27 private readonly TypeUsage _edmUsage;
28 private readonly object[] _values;
33 internal MaterializedDataRecord(MetadataWorkspace workspace, TypeUsage edmUsage, object[] values)
35 Debug.Assert(null != edmUsage && null != values, "null recordType or values");
36 _workspace = workspace;
39 for (int i = 0; i < values.Length; ++i)
41 Debug.Assert(null != values[i], "should have been DBNull.Value");
44 _values = values; // take ownership of the array
50 public DataRecordInfo DataRecordInfo
54 if (null == _recordInfo)
55 { // delay creation of DataRecordInfo until necessary
56 if (null == _workspace)
58 // When _workspace is null, we are materializing PODR.
59 // In this case, emdUsage describes a RowType.
60 Debug.Assert(Helper.IsRowType(_edmUsage.EdmType), "Edm type should be Row Type");
61 _recordInfo = new DataRecordInfo(_edmUsage);
65 _recordInfo = new DataRecordInfo(_workspace.GetOSpaceTypeUsage(_edmUsage));
67 Debug.Assert(_values.Length == _recordInfo.FieldMetadata.Count, "wrong values array size");
76 public override int FieldCount
80 return _values.Length;
87 public override object this[int ordinal]
91 return GetValue(ordinal);
98 public override object this[string name]
102 return GetValue(GetOrdinal(name));
109 public override bool GetBoolean(int ordinal)
111 return ((bool)_values[ordinal]);
117 public override byte GetByte(int ordinal)
119 return ((byte)_values[ordinal]);
125 public override long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length)
130 byte[] data = (byte[])_values[ordinal];
132 cbytes = data.Length;
134 // since arrays can't handle 64 bit values and this interface doesn't
135 // allow chunked access to data, a dataIndex outside the rang of Int32
137 if (fieldOffset > Int32.MaxValue)
139 throw EntityUtil.InvalidSourceBufferIndex(cbytes, fieldOffset, "fieldOffset");
142 ndataIndex = (int)fieldOffset;
144 // if no buffer is passed in, return the number of characters we have
150 if (ndataIndex < cbytes)
152 // help the user out in the case where there's less data than requested
153 if ((ndataIndex + length) > cbytes)
154 cbytes = cbytes - ndataIndex;
159 Array.Copy(data, ndataIndex, buffer, bufferOffset, cbytes);
164 if (EntityUtil.IsCatchableExceptionType(e))
166 cbytes = data.Length;
169 throw EntityUtil.InvalidDataLength(length);
171 // if bad buffer index, throw
172 if (bufferOffset < 0 || bufferOffset >= buffer.Length)
173 throw EntityUtil.InvalidDestinationBufferIndex(length, bufferOffset, "bufferOffset");
175 // if bad data index, throw
176 if (fieldOffset < 0 || fieldOffset >= cbytes)
177 throw EntityUtil.InvalidSourceBufferIndex(length, fieldOffset, "fieldOffset");
179 // if there is not enough room in the buffer for data
180 if (cbytes + bufferOffset > buffer.Length)
181 throw EntityUtil.InvalidBufferSizeOrIndex(cbytes, bufferOffset);
193 public override char GetChar(int ordinal)
195 return ((string)GetValue(ordinal))[0];
201 public override long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length)
205 string data = (string)_values[ordinal];
207 cchars = data.Length;
209 // since arrays can't handle 64 bit values and this interface doesn't
210 // allow chunked access to data, a dataIndex outside the rang of Int32
212 if (fieldOffset > Int32.MaxValue)
214 throw EntityUtil.InvalidSourceBufferIndex(cchars, fieldOffset, "fieldOffset");
217 ndataIndex = (int)fieldOffset;
219 // if no buffer is passed in, return the number of characters we have
225 if (ndataIndex < cchars)
227 // help the user out in the case where there's less data than requested
228 if ((ndataIndex + length) > cchars)
229 cchars = cchars - ndataIndex;
233 data.CopyTo(ndataIndex, buffer, bufferOffset, cchars);
238 if (EntityUtil.IsCatchableExceptionType(e))
240 cchars = data.Length;
243 throw EntityUtil.InvalidDataLength(length);
245 // if bad buffer index, throw
246 if (bufferOffset < 0 || bufferOffset >= buffer.Length)
247 throw EntityUtil.InvalidDestinationBufferIndex(buffer.Length, bufferOffset, "bufferOffset");
249 // if bad data index, throw
250 if (fieldOffset < 0 || fieldOffset >= cchars)
251 throw EntityUtil.InvalidSourceBufferIndex(cchars, fieldOffset, "fieldOffset");
253 // if there is not enough room in the buffer for data
254 if (cchars + bufferOffset > buffer.Length)
255 throw EntityUtil.InvalidBufferSizeOrIndex(cchars, bufferOffset);
267 public DbDataRecord GetDataRecord(int ordinal)
269 return ((DbDataRecord)_values[ordinal]);
273 /// Used to return a nested result
275 public DbDataReader GetDataReader(int i)
277 return this.GetDbDataReader(i);
283 public override string GetDataTypeName(int ordinal)
285 return GetMember(ordinal).TypeUsage.EdmType.Name;
291 public override DateTime GetDateTime(int ordinal)
293 return ((DateTime)_values[ordinal]);
299 public override Decimal GetDecimal(int ordinal)
301 return ((Decimal)_values[ordinal]);
307 public override double GetDouble(int ordinal)
309 return ((double)_values[ordinal]);
315 public override Type GetFieldType(int ordinal)
317 EdmType edmMemberType = GetMember(ordinal).TypeUsage.EdmType;
318 return edmMemberType.ClrType ?? typeof(System.Object);
324 public override float GetFloat(int ordinal)
326 return ((float)_values[ordinal]);
332 public override Guid GetGuid(int ordinal)
334 return ((Guid)_values[ordinal]);
340 public override Int16 GetInt16(int ordinal)
342 return ((Int16)_values[ordinal]);
348 public override Int32 GetInt32(int ordinal)
350 return ((Int32)_values[ordinal]);
356 public override Int64 GetInt64(int ordinal)
358 return ((Int64)_values[ordinal]);
364 public override string GetName(int ordinal)
366 return GetMember(ordinal).Name;
372 public override int GetOrdinal(string name)
374 if (null == _fieldNameLookup)
376 _fieldNameLookup = new FieldNameLookup(this, -1);
378 return _fieldNameLookup.GetOrdinal(name);
384 public override string GetString(int ordinal)
386 return ((string)_values[ordinal]);
392 public override object GetValue(int ordinal)
394 return _values[ordinal];
400 public override int GetValues(object[] values)
404 throw EntityUtil.ArgumentNull("values");
407 int copyLen = Math.Min(values.Length, FieldCount);
408 for (int i = 0; i < copyLen; ++i)
410 values[i] = _values[i];
415 private EdmMember GetMember(int ordinal)
417 return DataRecordInfo.FieldMetadata[ordinal].FieldType;
423 public override bool IsDBNull(int ordinal)
425 return (DBNull.Value == _values[ordinal]);
428 #region ICustomTypeDescriptor implementation
429 //[Microsoft] Reference: http://msdn.microsoft.com/msdnmag/issues/05/04/NETMatters/
430 //Holds all of the PropertyDescriptors for the PrimitiveType objects in _values
431 private PropertyDescriptorCollection _propertyDescriptors = null;
432 private FilterCache _filterCache;
433 //Stores an AttributeCollection for each PrimitiveType object in _values
434 Dictionary<object, AttributeCollection> _attrCache = null;
436 //Holds the filtered properties and attributes last used when GetProperties(Attribute[]) was called.
437 private class FilterCache
439 public Attribute[] Attributes;
440 public PropertyDescriptorCollection FilteredProperties;
441 //Verifies that this list of attributes matches the list passed into GetProperties(Attribute[])
442 public bool IsValid(Attribute[] other)
444 if (other == null || Attributes == null) return false;
446 if (Attributes.Length != other.Length) return false;
448 for (int i = 0; i < other.Length; i++)
450 if (!Attributes[i].Match(other[i])) return false;
457 AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); }
458 string ICustomTypeDescriptor.GetClassName() { return null; }
459 string ICustomTypeDescriptor.GetComponentName() { return null; }
461 /// Initialize the property descriptors for each PrimitiveType attribute.
462 /// See similar functionality in DataRecordObjectView's ITypedList implementation.
464 /// <returns></returns>
465 private PropertyDescriptorCollection InitializePropertyDescriptors()
472 if (_propertyDescriptors == null && 0 < _values.Length)
474 // Create a new PropertyDescriptorCollection with read-only properties
475 _propertyDescriptors = CreatePropertyDescriptorCollection(this.DataRecordInfo.RecordType.EdmType as StructuralType,
476 typeof(MaterializedDataRecord), true);
479 return _propertyDescriptors;
483 /// Creates a PropertyDescriptorCollection based on a StructuralType definition
484 /// Currently this includes a PropertyDescriptor for each primitive type property in the StructuralType
486 /// <param name="structuralType">The structural type definition</param>
487 /// <param name="componentType">The type to use as the component type</param>
488 /// <param name="isReadOnly">Whether the properties in the collection should be read only or not</param>
489 /// <returns></returns>
490 internal static PropertyDescriptorCollection CreatePropertyDescriptorCollection(StructuralType structuralType, Type componentType, bool isReadOnly)
492 List<PropertyDescriptor> pdList = new List<PropertyDescriptor>();
493 if (structuralType != null)
495 foreach (EdmMember member in structuralType.Members)
497 if (member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
499 EdmProperty edmPropertyMember = (EdmProperty)member;
501 FieldDescriptor fd = new FieldDescriptor(componentType, isReadOnly, edmPropertyMember);
506 return (new PropertyDescriptorCollection(pdList.ToArray()));
508 PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return ((ICustomTypeDescriptor)this).GetProperties(null); }
509 PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
511 bool filtering = (null != attributes && 0 < attributes.Length);
513 PropertyDescriptorCollection props = InitializePropertyDescriptors();
514 if (props == null) { return props; }
516 FilterCache cache = _filterCache;
518 // Use a cached version if possible
519 if (filtering && cache != null && cache.IsValid(attributes))
520 return cache.FilteredProperties;
521 else if (!filtering && props != null)
524 //Build up the attribute cache, since our PropertyDescriptor doesn't store it internally.
525 // _values is set only during construction.
526 if (null == _attrCache && null!=attributes && 0<attributes.Length)
528 _attrCache = new Dictionary<object, AttributeCollection>();
529 foreach (FieldDescriptor pd in _propertyDescriptors)
531 object o = pd.GetValue(this);
532 object[] atts = o.GetType().GetCustomAttributes(/*inherit*/false); //atts will not be null (atts.Length==0)
533 Attribute[] attrArray = new Attribute[atts.Length];
534 atts.CopyTo(attrArray, 0);
535 _attrCache.Add(pd, new AttributeCollection(attrArray));
539 //Create the filter based on the attributes.
540 props = new PropertyDescriptorCollection(null);
541 foreach (PropertyDescriptor pd in _propertyDescriptors)
543 if (_attrCache[pd].Matches(attributes))
549 // Store the computed properties
552 cache = new FilterCache();
553 cache.Attributes = attributes;
554 cache.FilteredProperties = props;
555 _filterCache = cache;
561 object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { return this; }