513 lines
18 KiB
C#
513 lines
18 KiB
C#
|
//---------------------------------------------------------------------
|
||
|
// <copyright file="EntityParameter.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
//
|
||
|
// @owner [....]
|
||
|
// @backupOwner [....]
|
||
|
//---------------------------------------------------------------------
|
||
|
namespace System.Data.EntityClient
|
||
|
{
|
||
|
using System.Data;
|
||
|
using System.Data.Common;
|
||
|
using System.Data.Common.CommandTrees;
|
||
|
using System.Data.Common.Internal;
|
||
|
using System.Data.Metadata.Edm;
|
||
|
using System.Diagnostics;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Class representing a parameter used in EntityCommand
|
||
|
/// </summary>
|
||
|
public sealed partial class EntityParameter : DbParameter, IDbDataParameter
|
||
|
{
|
||
|
private string _parameterName;
|
||
|
private DbType? _dbType;
|
||
|
private EdmType _edmType;
|
||
|
private byte? _precision;
|
||
|
private byte? _scale;
|
||
|
private bool _isDirty;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructs the EntityParameter object
|
||
|
/// </summary>
|
||
|
public EntityParameter()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructs the EntityParameter object with the given parameter name and the type of the parameter
|
||
|
/// </summary>
|
||
|
/// <param name="parameterName">The name of the parameter</param>
|
||
|
/// <param name="dbType">The type of the parameter</param>
|
||
|
public EntityParameter(string parameterName, DbType dbType)
|
||
|
{
|
||
|
SetParameterNameWithValidation(parameterName, "parameterName");
|
||
|
this.DbType = dbType;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructs the EntityParameter object with the given parameter name, the type of the parameter, and the size of the
|
||
|
/// parameter
|
||
|
/// </summary>
|
||
|
/// <param name="parameterName">The name of the parameter</param>
|
||
|
/// <param name="dbType">The type of the parameter</param>
|
||
|
/// <param name="size">The size of the parameter</param>
|
||
|
public EntityParameter(string parameterName, DbType dbType, int size)
|
||
|
{
|
||
|
SetParameterNameWithValidation(parameterName, "parameterName");
|
||
|
this.DbType = dbType;
|
||
|
this.Size = size;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructs the EntityParameter object with the given parameter name, the type of the parameter, the size of the
|
||
|
/// parameter, and the name of the source column
|
||
|
/// </summary>
|
||
|
/// <param name="parameterName">The name of the parameter</param>
|
||
|
/// <param name="dbType">The type of the parameter</param>
|
||
|
/// <param name="size">The size of the parameter</param>
|
||
|
/// <param name="sourceColumn">The name of the source column mapped to the data set, used for loading the parameter value</param>
|
||
|
public EntityParameter(string parameterName, DbType dbType, int size, string sourceColumn)
|
||
|
{
|
||
|
SetParameterNameWithValidation(parameterName, "parameterName");
|
||
|
this.DbType = dbType;
|
||
|
this.Size = size;
|
||
|
this.SourceColumn = sourceColumn;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructs the EntityParameter object with the given parameter name, the type of the parameter, the size of the
|
||
|
/// parameter, and the name of the source column
|
||
|
/// </summary>
|
||
|
/// <param name="parameterName">The name of the parameter</param>
|
||
|
/// <param name="dbType">The type of the parameter</param>
|
||
|
/// <param name="size">The size of the parameter</param>
|
||
|
/// <param name="direction">The direction of the parameter, whether it's input/output/both/return value</param>
|
||
|
/// <param name="isNullable">If the parameter is nullable</param>
|
||
|
/// <param name="precision">The floating point precision of the parameter, valid only if the parameter type is a floating point type</param>
|
||
|
/// <param name="scale">The scale of the parameter, valid only if the parameter type is a floating point type</param>
|
||
|
/// <param name="sourceColumn">The name of the source column mapped to the data set, used for loading the parameter value</param>
|
||
|
/// <param name="sourceVersion">The data row version to use when loading the parameter value</param>
|
||
|
/// <param name="value">The value of the parameter</param>
|
||
|
public EntityParameter(string parameterName,
|
||
|
DbType dbType,
|
||
|
int size,
|
||
|
ParameterDirection direction,
|
||
|
bool isNullable,
|
||
|
byte precision,
|
||
|
byte scale,
|
||
|
string sourceColumn,
|
||
|
DataRowVersion sourceVersion,
|
||
|
object value)
|
||
|
{
|
||
|
SetParameterNameWithValidation(parameterName, "parameterName");
|
||
|
this.DbType = dbType;
|
||
|
this.Size = size;
|
||
|
this.Direction = direction;
|
||
|
this.IsNullable = isNullable;
|
||
|
this.Precision = precision;
|
||
|
this.Scale = scale;
|
||
|
this.SourceColumn = sourceColumn;
|
||
|
this.SourceVersion = sourceVersion;
|
||
|
this.Value = value;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The name of the parameter
|
||
|
/// </summary>
|
||
|
public override string ParameterName
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._parameterName ?? "";
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
SetParameterNameWithValidation(value, "value");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Helper method to validate the parameter name; Ideally we'd only call this once, but
|
||
|
/// we have to put an argumentName on the Argument exception, and the property setter would
|
||
|
/// need "value" which confuses folks when they call the constructor that takes the value
|
||
|
/// of the parameter. c'est la vie.
|
||
|
/// </summary>
|
||
|
/// <param name="parameterName"></param>
|
||
|
/// <param name="argumentName"></param>
|
||
|
private void SetParameterNameWithValidation(string parameterName, string argumentName)
|
||
|
{
|
||
|
if (!string.IsNullOrEmpty(parameterName) && !DbCommandTree.IsValidParameterName(parameterName))
|
||
|
{
|
||
|
throw EntityUtil.Argument(System.Data.Entity.Strings.EntityClient_InvalidParameterName(parameterName), argumentName);
|
||
|
}
|
||
|
PropertyChanging();
|
||
|
this._parameterName = parameterName;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The type of the parameter, EdmType may also be set, and may provide more detailed information.
|
||
|
/// </summary>
|
||
|
public override DbType DbType
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
// if the user has not set the dbType but has set the dbType, use the edmType to try to deduce a dbType
|
||
|
if (!this._dbType.HasValue)
|
||
|
{
|
||
|
if (this._edmType != null)
|
||
|
{
|
||
|
return GetDbTypeFromEdm(_edmType);
|
||
|
}
|
||
|
// If the user has set neither the DbType nor the EdmType,
|
||
|
// then we attempt to deduce it from the value, but we won't set it in the
|
||
|
// member field as that's used to keep track of what the user set explicitly
|
||
|
else
|
||
|
{
|
||
|
// If we can't deduce the type because there are no values, we still have to return something, just assume it's string type
|
||
|
if (_value == null)
|
||
|
return DbType.String;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
return TypeHelpers.ConvertClrTypeToDbType(_value.GetType());
|
||
|
}
|
||
|
catch (ArgumentException e)
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotDeduceDbType, e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (DbType)this._dbType;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
PropertyChanging();
|
||
|
this._dbType = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The type of the parameter, expressed as an EdmType.
|
||
|
/// May be null (which is what it will be if unset). This means
|
||
|
/// that the DbType contains all the type information.
|
||
|
/// Non-null values must not contradict DbType (only restate or specialize).
|
||
|
/// </summary>
|
||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Edm")]
|
||
|
public EdmType EdmType
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._edmType;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != null && !Helper.IsScalarType(value))
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_EntityParameterEdmTypeNotScalar(value.FullName));
|
||
|
}
|
||
|
PropertyChanging();
|
||
|
this._edmType = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The precision of the parameter if the parameter is a floating point type
|
||
|
/// </summary>
|
||
|
public new byte Precision
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
byte result = this._precision.HasValue ? this._precision.Value : (byte)0;
|
||
|
return result;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
PropertyChanging();
|
||
|
this._precision = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The scale of the parameter if the parameter is a floating point type
|
||
|
/// </summary>
|
||
|
public new byte Scale
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
byte result = this._scale.HasValue ? this._scale.Value : (byte)0;
|
||
|
return result;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
PropertyChanging();
|
||
|
this._scale = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// The value of the parameter
|
||
|
/// </summary>
|
||
|
public override object Value
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._value;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
// If the user hasn't set the DbType, then we have to figure out if the DbType will change as a result
|
||
|
// of the change in the value. What we want to achieve is that changes to the value will not cause
|
||
|
// it to be dirty, but changes to the value that causes the apparent DbType to change, then should be
|
||
|
// dirty.
|
||
|
if (!this._dbType.HasValue && this._edmType == null)
|
||
|
{
|
||
|
// If the value is null, then we assume it's string type
|
||
|
DbType oldDbType = DbType.String;
|
||
|
if (_value != null)
|
||
|
{
|
||
|
oldDbType = TypeHelpers.ConvertClrTypeToDbType(_value.GetType());
|
||
|
}
|
||
|
|
||
|
// If the value is null, then we assume it's string type
|
||
|
DbType newDbType = DbType.String;
|
||
|
if (value != null)
|
||
|
{
|
||
|
newDbType = TypeHelpers.ConvertClrTypeToDbType(value.GetType());
|
||
|
}
|
||
|
|
||
|
if (oldDbType != newDbType)
|
||
|
{
|
||
|
PropertyChanging();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._value = value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets whether this collection has been changes since the last reset
|
||
|
/// </summary>
|
||
|
internal bool IsDirty
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _isDirty;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Indicates whether the DbType property has been set by the user;
|
||
|
/// </summary>
|
||
|
internal bool IsDbTypeSpecified
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._dbType.HasValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Indicates whether the Direction property has been set by the user;
|
||
|
/// </summary>
|
||
|
internal bool IsDirectionSpecified
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._direction != 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Indicates whether the IsNullable property has been set by the user;
|
||
|
/// </summary>
|
||
|
internal bool IsIsNullableSpecified
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._isNullable.HasValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Indicates whether the Precision property has been set by the user;
|
||
|
/// </summary>
|
||
|
internal bool IsPrecisionSpecified
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._precision.HasValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Indicates whether the Scale property has been set by the user;
|
||
|
/// </summary>
|
||
|
internal bool IsScaleSpecified
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._scale.HasValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Indicates whether the Size property has been set by the user;
|
||
|
/// </summary>
|
||
|
internal bool IsSizeSpecified
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return this._size.HasValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Resets the DbType property to its original settings
|
||
|
/// </summary>
|
||
|
public override void ResetDbType()
|
||
|
{
|
||
|
if (_dbType != null || _edmType != null)
|
||
|
{
|
||
|
PropertyChanging();
|
||
|
}
|
||
|
|
||
|
_edmType = null;
|
||
|
_dbType = null;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Clones this parameter object
|
||
|
/// </summary>
|
||
|
/// <returns>The new cloned object</returns>
|
||
|
internal EntityParameter Clone()
|
||
|
{
|
||
|
return new EntityParameter(this);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Clones this parameter object
|
||
|
/// </summary>
|
||
|
/// <returns>The new cloned object</returns>
|
||
|
private void CloneHelper(EntityParameter destination)
|
||
|
{
|
||
|
CloneHelperCore(destination);
|
||
|
|
||
|
destination._parameterName = _parameterName;
|
||
|
destination._dbType = _dbType;
|
||
|
destination._edmType = _edmType;
|
||
|
destination._precision = _precision;
|
||
|
destination._scale = _scale;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Marks that this parameter has been changed
|
||
|
/// </summary>
|
||
|
private void PropertyChanging()
|
||
|
{
|
||
|
_isDirty = true;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Determines the size of the given object
|
||
|
/// </summary>
|
||
|
/// <param name="value"></param>
|
||
|
/// <returns></returns>
|
||
|
private int ValueSize(object value)
|
||
|
{
|
||
|
return ValueSizeCore(value);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Get the type usage for this parameter in model terms.
|
||
|
/// </summary>
|
||
|
/// <returns>The type usage for this parameter</returns>
|
||
|
//NOTE: Because GetTypeUsage throws CommandValidationExceptions, it should only be called from EntityCommand during command execution
|
||
|
internal TypeUsage GetTypeUsage()
|
||
|
{
|
||
|
TypeUsage typeUsage;
|
||
|
if (!this.IsTypeConsistent)
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_EntityParameterInconsistentEdmType(
|
||
|
_edmType.FullName, _parameterName));
|
||
|
}
|
||
|
|
||
|
if (this._edmType != null)
|
||
|
{
|
||
|
typeUsage = TypeUsage.Create(this._edmType);
|
||
|
}
|
||
|
else if (!DbTypeMap.TryGetModelTypeUsage(this.DbType, out typeUsage))
|
||
|
{
|
||
|
// Spatial types have only DbType 'Object', and cannot be represented in the static type map.
|
||
|
PrimitiveType primitiveParameterType;
|
||
|
if (this.DbType == DbType.Object &&
|
||
|
this.Value != null &&
|
||
|
ClrProviderManifest.Instance.TryGetPrimitiveType(this.Value.GetType(), out primitiveParameterType) &&
|
||
|
Helper.IsSpatialType(primitiveParameterType))
|
||
|
{
|
||
|
typeUsage = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(primitiveParameterType.PrimitiveTypeKind);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnsupportedDbType(this.DbType.ToString(), ParameterName));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Debug.Assert(typeUsage != null, "DbType.TryGetModelTypeUsage returned true for null TypeUsage?");
|
||
|
return typeUsage;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Reset the dirty flag on the collection
|
||
|
/// </summary>
|
||
|
internal void ResetIsDirty()
|
||
|
{
|
||
|
_isDirty = false;
|
||
|
}
|
||
|
|
||
|
private bool IsTypeConsistent
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (this._edmType != null && this._dbType.HasValue)
|
||
|
{
|
||
|
DbType dbType = GetDbTypeFromEdm(_edmType);
|
||
|
if (dbType == DbType.String)
|
||
|
{
|
||
|
// would need facets to distinguish the various sorts of string,
|
||
|
// a generic string EdmType is consistent with any string DbType.
|
||
|
return _dbType == DbType.String || _dbType == DbType.AnsiString
|
||
|
|| dbType == DbType.AnsiStringFixedLength || dbType == DbType.StringFixedLength;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return _dbType == dbType;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static DbType GetDbTypeFromEdm(EdmType edmType)
|
||
|
{
|
||
|
PrimitiveType primitiveType = Helper.AsPrimitive(edmType);
|
||
|
DbType dbType;
|
||
|
if (Helper.IsSpatialType(primitiveType))
|
||
|
{
|
||
|
return DbType.Object;
|
||
|
}
|
||
|
else if (DbCommandDefinition.TryGetDbTypeFromPrimitiveType(primitiveType, out dbType))
|
||
|
{
|
||
|
return dbType;
|
||
|
}
|
||
|
|
||
|
// we shouldn't ever get here. Assert in a debug build, and pick a type.
|
||
|
Debug.Assert(false, "The provided edmType is of an unknown primitive type.");
|
||
|
return default(DbType);
|
||
|
}
|
||
|
}
|
||
|
}
|