Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / DataRecord.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="DataRecord.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.Objects
10 {
11     using System.Collections.Generic;
12     using System.Data;
13     using System.Data.Common;
14     using System.Data.Metadata.Edm;
15     using System.Data.Objects;
16     using System.Diagnostics;
17     using System.ComponentModel;
18
19     /// <summary>
20     /// Instances of this class would be returned to user via Query&lt;T&gt;
21     /// </summary>
22     internal sealed class MaterializedDataRecord : DbDataRecord, IExtendedDataRecord, ICustomTypeDescriptor
23     {
24         private FieldNameLookup _fieldNameLookup;
25         private DataRecordInfo _recordInfo;
26         private readonly MetadataWorkspace _workspace;
27         private readonly TypeUsage _edmUsage;
28         private readonly object[] _values;
29
30         /// <summary>
31         ///
32         /// </summary>
33         internal MaterializedDataRecord(MetadataWorkspace workspace, TypeUsage edmUsage, object[] values)
34         {
35             Debug.Assert(null != edmUsage && null != values, "null recordType or values");
36             _workspace = workspace;
37             _edmUsage = edmUsage;
38 #if DEBUG
39             for (int i = 0; i < values.Length; ++i)
40             {
41                 Debug.Assert(null != values[i], "should have been DBNull.Value");
42             }
43 #endif
44             _values = values; // take ownership of the array
45         }
46
47         /// <summary>
48         ///
49         /// </summary>
50         public DataRecordInfo DataRecordInfo
51         {
52             get
53             {
54                 if (null == _recordInfo)
55                 {   // delay creation of DataRecordInfo until necessary
56                     if (null == _workspace)
57                     {
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);
62                     }
63                     else
64                     {
65                         _recordInfo = new DataRecordInfo(_workspace.GetOSpaceTypeUsage(_edmUsage));
66                     }
67                     Debug.Assert(_values.Length == _recordInfo.FieldMetadata.Count, "wrong values array size");
68                 }
69                 return _recordInfo;
70             }
71         }
72
73         /// <summary>
74         ///
75         /// </summary>
76         public override int FieldCount
77         {
78             get
79             {
80                 return _values.Length;
81             }
82         }
83
84         /// <summary>
85         ///
86         /// </summary>
87         public override object this[int ordinal]
88         {
89             get
90             {
91                 return GetValue(ordinal);
92             }
93         }
94
95         /// <summary>
96         ///
97         /// </summary>
98         public override object this[string name]
99         {
100             get
101             {
102                 return GetValue(GetOrdinal(name));
103             }
104         }
105
106         /// <summary>
107         ///
108         /// </summary>
109         public override bool GetBoolean(int ordinal)
110         {
111             return ((bool)_values[ordinal]);
112         }
113
114         /// <summary>
115         ///
116         /// </summary>
117         public override byte GetByte(int ordinal)
118         {
119             return ((byte)_values[ordinal]);
120         }
121
122         /// <summary>
123         ///
124         /// </summary>
125         public override long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length)
126         {
127             int cbytes = 0;
128             int ndataIndex;
129
130             byte[] data = (byte[])_values[ordinal];
131
132             cbytes = data.Length;
133
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
136             // is invalid
137             if (fieldOffset > Int32.MaxValue)
138             {
139                 throw EntityUtil.InvalidSourceBufferIndex(cbytes, fieldOffset, "fieldOffset");
140             }
141
142             ndataIndex = (int)fieldOffset;
143
144             // if no buffer is passed in, return the number of characters we have
145             if (null == buffer)
146                 return cbytes;
147
148             try
149             {
150                 if (ndataIndex < cbytes)
151                 {
152                     // help the user out in the case where there's less data than requested
153                     if ((ndataIndex + length) > cbytes)
154                         cbytes = cbytes - ndataIndex;
155                     else
156                         cbytes = length;
157                 }
158
159                 Array.Copy(data, ndataIndex, buffer, bufferOffset, cbytes);
160             }
161             catch (Exception e)
162             {
163                 // 
164                 if (EntityUtil.IsCatchableExceptionType(e))
165                 {
166                     cbytes = data.Length;
167
168                     if (length < 0)
169                         throw EntityUtil.InvalidDataLength(length);
170
171                     // if bad buffer index, throw
172                     if (bufferOffset < 0 || bufferOffset >= buffer.Length)
173                         throw EntityUtil.InvalidDestinationBufferIndex(length, bufferOffset, "bufferOffset");
174
175                     // if bad data index, throw
176                     if (fieldOffset < 0 || fieldOffset >= cbytes)
177                         throw EntityUtil.InvalidSourceBufferIndex(length, fieldOffset, "fieldOffset");
178
179                     // if there is not enough room in the buffer for data
180                     if (cbytes + bufferOffset > buffer.Length)
181                         throw EntityUtil.InvalidBufferSizeOrIndex(cbytes, bufferOffset);
182                 }
183
184                 throw;
185             }
186
187             return cbytes;
188         }
189
190         /// <summary>
191         ///
192         /// </summary>
193         public override char GetChar(int ordinal)
194         {
195             return ((string)GetValue(ordinal))[0];
196         }
197
198         /// <summary>
199         ///
200         /// </summary>
201         public override long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length)
202         {
203             int cchars = 0;
204             int ndataIndex;
205             string data = (string)_values[ordinal];
206
207             cchars = data.Length;
208
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
211             // is invalid
212             if (fieldOffset > Int32.MaxValue)
213             {
214                 throw EntityUtil.InvalidSourceBufferIndex(cchars, fieldOffset, "fieldOffset");
215             }
216
217             ndataIndex = (int)fieldOffset;
218
219             // if no buffer is passed in, return the number of characters we have
220             if (null == buffer)
221                 return cchars;
222
223             try
224             {
225                 if (ndataIndex < cchars)
226                 {
227                     // help the user out in the case where there's less data than requested
228                     if ((ndataIndex + length) > cchars)
229                         cchars = cchars - ndataIndex;
230                     else
231                         cchars = length;
232                 }
233                 data.CopyTo(ndataIndex, buffer, bufferOffset, cchars);
234             }
235             catch (Exception e)
236             {
237                 // 
238                 if (EntityUtil.IsCatchableExceptionType(e))
239                 {
240                     cchars = data.Length;
241
242                     if (length < 0)
243                         throw EntityUtil.InvalidDataLength(length);
244
245                     // if bad buffer index, throw
246                     if (bufferOffset < 0 || bufferOffset >= buffer.Length)
247                         throw EntityUtil.InvalidDestinationBufferIndex(buffer.Length, bufferOffset, "bufferOffset");
248
249                     // if bad data index, throw
250                     if (fieldOffset < 0 || fieldOffset >= cchars)
251                         throw EntityUtil.InvalidSourceBufferIndex(cchars, fieldOffset, "fieldOffset");
252
253                     // if there is not enough room in the buffer for data
254                     if (cchars + bufferOffset > buffer.Length)
255                         throw EntityUtil.InvalidBufferSizeOrIndex(cchars, bufferOffset);
256                 }
257
258                 throw;
259             }
260
261             return cchars;
262         }
263
264         /// <summary>
265         ///
266         /// </summary>
267         public DbDataRecord GetDataRecord(int ordinal)
268         {
269             return ((DbDataRecord)_values[ordinal]);
270         }
271
272         /// <summary>
273         /// Used to return a nested result
274         /// </summary>
275         public DbDataReader GetDataReader(int i)
276         {
277             return this.GetDbDataReader(i);
278         }
279
280         /// <summary>
281         ///
282         /// </summary>
283         public override string GetDataTypeName(int ordinal)
284         {
285             return GetMember(ordinal).TypeUsage.EdmType.Name;
286         }
287
288         /// <summary>
289         ///
290         /// </summary>
291         public override DateTime GetDateTime(int ordinal)
292         {
293             return ((DateTime)_values[ordinal]);
294         }
295
296         /// <summary>
297         ///
298         /// </summary>
299         public override Decimal GetDecimal(int ordinal)
300         {
301             return ((Decimal)_values[ordinal]);
302         }
303
304         /// <summary>
305         ///
306         /// </summary>
307         public override double GetDouble(int ordinal)
308         {
309             return ((double)_values[ordinal]);
310         }
311
312         /// <summary>
313         ///
314         /// </summary>
315         public override Type GetFieldType(int ordinal)
316         {
317             EdmType edmMemberType = GetMember(ordinal).TypeUsage.EdmType;
318             return edmMemberType.ClrType ?? typeof(System.Object);
319         }
320
321         /// <summary>
322         ///
323         /// </summary>
324         public override float GetFloat(int ordinal)
325         {
326             return ((float)_values[ordinal]);
327         }
328
329         /// <summary>
330         ///
331         /// </summary>
332         public override Guid GetGuid(int ordinal)
333         {
334             return ((Guid)_values[ordinal]);
335         }
336
337         /// <summary>
338         ///
339         /// </summary>
340         public override Int16 GetInt16(int ordinal)
341         {
342             return ((Int16)_values[ordinal]);
343         }
344
345         /// <summary>
346         ///
347         /// </summary>
348         public override Int32 GetInt32(int ordinal)
349         {
350             return ((Int32)_values[ordinal]);
351         }
352
353         /// <summary>
354         ///
355         /// </summary>
356         public override Int64 GetInt64(int ordinal)
357         {
358             return ((Int64)_values[ordinal]);
359         }
360
361         /// <summary>
362         ///
363         /// </summary>
364         public override string GetName(int ordinal)
365         {
366             return GetMember(ordinal).Name;
367         }
368
369         /// <summary>
370         ///
371         /// </summary>
372         public override int GetOrdinal(string name)
373         {
374             if (null == _fieldNameLookup)
375             {
376                 _fieldNameLookup = new FieldNameLookup(this, -1);
377             }
378             return _fieldNameLookup.GetOrdinal(name);
379         }
380
381         /// <summary>
382         ///
383         /// </summary>
384         public override string GetString(int ordinal)
385         {
386             return ((string)_values[ordinal]);
387         }
388
389         /// <summary>
390         ///
391         /// </summary>
392         public override object GetValue(int ordinal)
393         {
394             return _values[ordinal];
395         }
396
397         /// <summary>
398         ///
399         /// </summary>
400         public override int GetValues(object[] values)
401         {
402             if (null == values)
403             {
404                 throw EntityUtil.ArgumentNull("values");
405             }
406
407             int copyLen = Math.Min(values.Length, FieldCount);
408             for (int i = 0; i < copyLen; ++i)
409             {
410                 values[i] = _values[i];
411             }
412             return copyLen;
413         }
414
415         private EdmMember GetMember(int ordinal)
416         {
417             return DataRecordInfo.FieldMetadata[ordinal].FieldType;
418         }
419
420         /// <summary>
421         ///
422         /// </summary>
423         public override bool IsDBNull(int ordinal)
424         {
425             return (DBNull.Value == _values[ordinal]);
426         }
427
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;
435
436         //Holds the filtered properties and attributes last used when GetProperties(Attribute[]) was called.
437         private class FilterCache
438         {
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)
443             {
444                 if (other == null || Attributes == null) return false;
445
446                 if (Attributes.Length != other.Length) return false;
447
448                 for (int i = 0; i < other.Length; i++)
449                 {
450                     if (!Attributes[i].Match(other[i])) return false;
451                 }
452
453                 return true;
454             }
455         }
456
457         AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); }
458         string ICustomTypeDescriptor.GetClassName() { return null; }
459         string ICustomTypeDescriptor.GetComponentName() { return null; }
460         /// <summary>
461         /// Initialize the property descriptors for each PrimitiveType attribute.
462         /// See similar functionality in DataRecordObjectView's ITypedList implementation.
463         /// </summary>
464         /// <returns></returns>
465         private PropertyDescriptorCollection InitializePropertyDescriptors()
466         {
467             if (null == _values)
468             {
469                 return null;
470             }
471
472             if (_propertyDescriptors == null && 0 < _values.Length)
473             {
474                 // Create a new PropertyDescriptorCollection with read-only properties
475                 _propertyDescriptors = CreatePropertyDescriptorCollection(this.DataRecordInfo.RecordType.EdmType as StructuralType,
476                                                                           typeof(MaterializedDataRecord), true);
477             }
478
479             return _propertyDescriptors;
480         }
481
482         /// <summary>
483         /// Creates a PropertyDescriptorCollection based on a StructuralType definition
484         /// Currently this includes a PropertyDescriptor for each primitive type property in the StructuralType
485         /// </summary>
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)
491         {
492             List<PropertyDescriptor> pdList = new List<PropertyDescriptor>();
493             if (structuralType != null)
494             {
495                 foreach (EdmMember member in structuralType.Members)
496                 {
497                     if (member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
498                     {
499                         EdmProperty edmPropertyMember = (EdmProperty)member;
500
501                         FieldDescriptor fd = new FieldDescriptor(componentType, isReadOnly, edmPropertyMember);
502                         pdList.Add(fd);
503                     }
504                 }
505             }
506             return (new PropertyDescriptorCollection(pdList.ToArray()));
507         }
508         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return ((ICustomTypeDescriptor)this).GetProperties(null); }
509         PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
510         {
511             bool filtering = (null != attributes && 0 < attributes.Length);
512
513             PropertyDescriptorCollection props = InitializePropertyDescriptors();
514             if (props == null) { return props; }
515
516             FilterCache cache = _filterCache;
517
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)
522                 return props;
523
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)
527             {
528                 _attrCache = new Dictionary<object, AttributeCollection>();
529                 foreach (FieldDescriptor pd in _propertyDescriptors)
530                 {
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));
536                 }
537             }
538
539             //Create the filter based on the attributes.
540             props = new PropertyDescriptorCollection(null);
541             foreach (PropertyDescriptor pd in _propertyDescriptors)
542             {
543                 if (_attrCache[pd].Matches(attributes))
544                 {
545                     props.Add(pd);
546                 }
547             }
548
549             // Store the computed properties
550             if (filtering)
551             {
552                 cache = new FilterCache();
553                 cache.Attributes = attributes;
554                 cache.FilteredProperties = props;
555                 _filterCache = cache;
556             }
557
558             return props;
559         }
560
561         object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { return this; }
562
563         #endregion
564     }
565 }