|
//------------------------------------------------------------------------------
// <copyright file="DataRecord.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">Microsoft</owner>
// <owner current="true" primary="false">Microsoft</owner>
//------------------------------------------------------------------------------
namespace System.Data.Objects
{
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Diagnostics;
using System.ComponentModel;
/// <summary>
/// Instances of this class would be returned to user via Query<T>
/// </summary>
internal sealed class MaterializedDataRecord : DbDataRecord, IExtendedDataRecord, ICustomTypeDescriptor
{
private FieldNameLookup _fieldNameLookup;
private DataRecordInfo _recordInfo;
private readonly MetadataWorkspace _workspace;
private readonly TypeUsage _edmUsage;
private readonly object[] _values;
/// <summary>
///
/// </summary>
internal MaterializedDataRecord(MetadataWorkspace workspace, TypeUsage edmUsage, object[] values)
{
Debug.Assert(null != edmUsage && null != values, "null recordType or values");
_workspace = workspace;
_edmUsage = edmUsage;
#if DEBUG
for (int i = 0; i < values.Length; ++i)
{
Debug.Assert(null != values[i], "should have been DBNull.Value");
}
#endif
_values = values; // take ownership of the array
}
/// <summary>
///
/// </summary>
public DataRecordInfo DataRecordInfo
{
get
{
if (null == _recordInfo)
{ // delay creation of DataRecordInfo until necessary
if (null == _workspace)
{
// When _workspace is null, we are materializing PODR.
// In this case, emdUsage describes a RowType.
Debug.Assert(Helper.IsRowType(_edmUsage.EdmType), "Edm type should be Row Type");
_recordInfo = new DataRecordInfo(_edmUsage);
}
else
{
_recordInfo = new DataRecordInfo(_workspace.GetOSpaceTypeUsage(_edmUsage));
}
Debug.Assert(_values.Length == _recordInfo.FieldMetadata.Count, "wrong values array size");
}
return _recordInfo;
}
}
/// <summary>
///
/// </summary>
public override int FieldCount
{
get
{
return _values.Length;
}
}
/// <summary>
///
/// </summary>
public override object this[int ordinal]
{
get
{
return GetValue(ordinal);
}
}
/// <summary>
///
/// </summary>
public override object this[string name]
{
get
{
return GetValue(GetOrdinal(name));
}
}
/// <summary>
///
/// </summary>
public override bool GetBoolean(int ordinal)
{
return ((bool)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override byte GetByte(int ordinal)
{
return ((byte)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override long GetBytes(int ordinal, long fieldOffset, byte[] buffer, int bufferOffset, int length)
{
int cbytes = 0;
int ndataIndex;
byte[] data = (byte[])_values[ordinal];
cbytes = data.Length;
// since arrays can't handle 64 bit values and this interface doesn't
// allow chunked access to data, a dataIndex outside the rang of Int32
// is invalid
if (fieldOffset > Int32.MaxValue)
{
throw EntityUtil.InvalidSourceBufferIndex(cbytes, fieldOffset, "fieldOffset");
}
ndataIndex = (int)fieldOffset;
// if no buffer is passed in, return the number of characters we have
if (null == buffer)
return cbytes;
try
{
if (ndataIndex < cbytes)
{
// help the user out in the case where there's less data than requested
if ((ndataIndex + length) > cbytes)
cbytes = cbytes - ndataIndex;
else
cbytes = length;
}
Array.Copy(data, ndataIndex, buffer, bufferOffset, cbytes);
}
catch (Exception e)
{
//
if (EntityUtil.IsCatchableExceptionType(e))
{
cbytes = data.Length;
if (length < 0)
throw EntityUtil.InvalidDataLength(length);
// if bad buffer index, throw
if (bufferOffset < 0 || bufferOffset >= buffer.Length)
throw EntityUtil.InvalidDestinationBufferIndex(length, bufferOffset, "bufferOffset");
// if bad data index, throw
if (fieldOffset < 0 || fieldOffset >= cbytes)
throw EntityUtil.InvalidSourceBufferIndex(length, fieldOffset, "fieldOffset");
// if there is not enough room in the buffer for data
if (cbytes + bufferOffset > buffer.Length)
throw EntityUtil.InvalidBufferSizeOrIndex(cbytes, bufferOffset);
}
throw;
}
return cbytes;
}
/// <summary>
///
/// </summary>
public override char GetChar(int ordinal)
{
return ((string)GetValue(ordinal))[0];
}
/// <summary>
///
/// </summary>
public override long GetChars(int ordinal, long fieldOffset, char[] buffer, int bufferOffset, int length)
{
int cchars = 0;
int ndataIndex;
string data = (string)_values[ordinal];
cchars = data.Length;
// since arrays can't handle 64 bit values and this interface doesn't
// allow chunked access to data, a dataIndex outside the rang of Int32
// is invalid
if (fieldOffset > Int32.MaxValue)
{
throw EntityUtil.InvalidSourceBufferIndex(cchars, fieldOffset, "fieldOffset");
}
ndataIndex = (int)fieldOffset;
// if no buffer is passed in, return the number of characters we have
if (null == buffer)
return cchars;
try
{
if (ndataIndex < cchars)
{
// help the user out in the case where there's less data than requested
if ((ndataIndex + length) > cchars)
cchars = cchars - ndataIndex;
else
cchars = length;
}
data.CopyTo(ndataIndex, buffer, bufferOffset, cchars);
}
catch (Exception e)
{
//
if (EntityUtil.IsCatchableExceptionType(e))
{
cchars = data.Length;
if (length < 0)
throw EntityUtil.InvalidDataLength(length);
// if bad buffer index, throw
if (bufferOffset < 0 || bufferOffset >= buffer.Length)
throw EntityUtil.InvalidDestinationBufferIndex(buffer.Length, bufferOffset, "bufferOffset");
// if bad data index, throw
if (fieldOffset < 0 || fieldOffset >= cchars)
throw EntityUtil.InvalidSourceBufferIndex(cchars, fieldOffset, "fieldOffset");
// if there is not enough room in the buffer for data
if (cchars + bufferOffset > buffer.Length)
throw EntityUtil.InvalidBufferSizeOrIndex(cchars, bufferOffset);
}
throw;
}
return cchars;
}
/// <summary>
///
/// </summary>
public DbDataRecord GetDataRecord(int ordinal)
{
return ((DbDataRecord)_values[ordinal]);
}
/// <summary>
/// Used to return a nested result
/// </summary>
public DbDataReader GetDataReader(int i)
{
return this.GetDbDataReader(i);
}
/// <summary>
///
/// </summary>
public override string GetDataTypeName(int ordinal)
{
return GetMember(ordinal).TypeUsage.EdmType.Name;
}
/// <summary>
///
/// </summary>
public override DateTime GetDateTime(int ordinal)
{
return ((DateTime)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override Decimal GetDecimal(int ordinal)
{
return ((Decimal)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override double GetDouble(int ordinal)
{
return ((double)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override Type GetFieldType(int ordinal)
{
EdmType edmMemberType = GetMember(ordinal).TypeUsage.EdmType;
return edmMemberType.ClrType ?? typeof(System.Object);
}
/// <summary>
///
/// </summary>
public override float GetFloat(int ordinal)
{
return ((float)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override Guid GetGuid(int ordinal)
{
return ((Guid)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override Int16 GetInt16(int ordinal)
{
return ((Int16)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override Int32 GetInt32(int ordinal)
{
return ((Int32)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override Int64 GetInt64(int ordinal)
{
return ((Int64)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override string GetName(int ordinal)
{
return GetMember(ordinal).Name;
}
/// <summary>
///
/// </summary>
public override int GetOrdinal(string name)
{
if (null == _fieldNameLookup)
{
_fieldNameLookup = new FieldNameLookup(this, -1);
}
return _fieldNameLookup.GetOrdinal(name);
}
/// <summary>
///
/// </summary>
public override string GetString(int ordinal)
{
return ((string)_values[ordinal]);
}
/// <summary>
///
/// </summary>
public override object GetValue(int ordinal)
{
return _values[ordinal];
}
/// <summary>
///
/// </summary>
public override int GetValues(object[] values)
{
if (null == values)
{
throw EntityUtil.ArgumentNull("values");
}
int copyLen = Math.Min(values.Length, FieldCount);
for (int i = 0; i < copyLen; ++i)
{
values[i] = _values[i];
}
return copyLen;
}
private EdmMember GetMember(int ordinal)
{
return DataRecordInfo.FieldMetadata[ordinal].FieldType;
}
/// <summary>
///
/// </summary>
public override bool IsDBNull(int ordinal)
{
return (DBNull.Value == _values[ordinal]);
}
#region ICustomTypeDescriptor implementation
//[Microsoft] Reference: http://msdn.microsoft.com/msdnmag/issues/05/04/NETMatters/
//Holds all of the PropertyDescriptors for the PrimitiveType objects in _values
private PropertyDescriptorCollection _propertyDescriptors = null;
private FilterCache _filterCache;
//Stores an AttributeCollection for each PrimitiveType object in _values
Dictionary<object, AttributeCollection> _attrCache = null;
//Holds the filtered properties and attributes last used when GetProperties(Attribute[]) was called.
private class FilterCache
{
public Attribute[] Attributes;
public PropertyDescriptorCollection FilteredProperties;
//Verifies that this list of attributes matches the list passed into GetProperties(Attribute[])
public bool IsValid(Attribute[] other)
{
if (other == null || Attributes == null) return false;
if (Attributes.Length != other.Length) return false;
for (int i = 0; i < other.Length; i++)
{
if (!Attributes[i].Match(other[i])) return false;
}
return true;
}
}
AttributeCollection ICustomTypeDescriptor.GetAttributes() { return TypeDescriptor.GetAttributes(this, true); }
string ICustomTypeDescriptor.GetClassName() { return null; }
string ICustomTypeDescriptor.GetComponentName() { return null; }
/// <summary>
/// Initialize the property descriptors for each PrimitiveType attribute.
/// See similar functionality in DataRecordObjectView's ITypedList implementation.
/// </summary>
/// <returns></returns>
private PropertyDescriptorCollection InitializePropertyDescriptors()
{
if (null == _values)
{
return null;
}
if (_propertyDescriptors == null && 0 < _values.Length)
{
// Create a new PropertyDescriptorCollection with read-only properties
_propertyDescriptors = CreatePropertyDescriptorCollection(this.DataRecordInfo.RecordType.EdmType as StructuralType,
typeof(MaterializedDataRecord), true);
}
return _propertyDescriptors;
}
/// <summary>
/// Creates a PropertyDescriptorCollection based on a StructuralType definition
/// Currently this includes a PropertyDescriptor for each primitive type property in the StructuralType
/// </summary>
/// <param name="structuralType">The structural type definition</param>
/// <param name="componentType">The type to use as the component type</param>
/// <param name="isReadOnly">Whether the properties in the collection should be read only or not</param>
/// <returns></returns>
internal static PropertyDescriptorCollection CreatePropertyDescriptorCollection(StructuralType structuralType, Type componentType, bool isReadOnly)
{
List<PropertyDescriptor> pdList = new List<PropertyDescriptor>();
if (structuralType != null)
{
foreach (EdmMember member in structuralType.Members)
{
if (member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
{
EdmProperty edmPropertyMember = (EdmProperty)member;
FieldDescriptor fd = new FieldDescriptor(componentType, isReadOnly, edmPropertyMember);
pdList.Add(fd);
}
}
}
return (new PropertyDescriptorCollection(pdList.ToArray()));
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { return ((ICustomTypeDescriptor)this).GetProperties(null); }
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
bool filtering = (null != attributes && 0 < attributes.Length);
PropertyDescriptorCollection props = InitializePropertyDescriptors();
if (props == null) { return props; }
FilterCache cache = _filterCache;
// Use a cached version if possible
if (filtering && cache != null && cache.IsValid(attributes))
return cache.FilteredProperties;
else if (!filtering && props != null)
return props;
//Build up the attribute cache, since our PropertyDescriptor doesn't store it internally.
// _values is set only during construction.
if (null == _attrCache && null!=attributes && 0<attributes.Length)
{
_attrCache = new Dictionary<object, AttributeCollection>();
foreach (FieldDescriptor pd in _propertyDescriptors)
{
object o = pd.GetValue(this);
object[] atts = o.GetType().GetCustomAttributes(/*inherit*/false); //atts will not be null (atts.Length==0)
Attribute[] attrArray = new Attribute[atts.Length];
atts.CopyTo(attrArray, 0);
_attrCache.Add(pd, new AttributeCollection(attrArray));
}
}
//Create the filter based on the attributes.
props = new PropertyDescriptorCollection(null);
foreach (PropertyDescriptor pd in _propertyDescriptors)
{
if (_attrCache[pd].Matches(attributes))
{
props.Add(pd);
}
}
// Store the computed properties
if (filtering)
{
cache = new FilterCache();
cache.Attributes = attributes;
cache.FilteredProperties = props;
_filterCache = cache;
}
return props;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { return this; }
#endregion
}
}
|