2016-08-03 10:59:49 +00:00
//---------------------------------------------------------------------
// <copyright file="ObjectParameterCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
2017-08-21 15:34:15 +00:00
// @owner Microsoft
// @backupowner Microsoft
2016-08-03 10:59:49 +00:00
//---------------------------------------------------------------------
namespace System.Data.Objects
{
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Data ;
using System.Data.Entity ;
using System.Data.Metadata.Edm ;
using System.Text ;
/// <summary>
/// This class represents a collection of query parameters at the object layer.
/// </summary>
public sealed class ObjectParameterCollection : ICollection < ObjectParameter >
{
// Note: There are NO public constructors for this class - it is for internal
// ObjectQuery<T> use only, but must be public so that an instance thereof can be
// a public property on ObjectQuery<T>.
#region Internal Constructors
// ---------------------
// Internal Constructors
// ---------------------
#region ObjectParameterCollection ( ClrPerspective )
/// <summary>
/// This internal constructor creates a new query parameter collection and
/// initializes the internal parameter storage.
/// </summary>
internal ObjectParameterCollection ( ClrPerspective perspective )
{
EntityUtil . CheckArgumentNull ( perspective , "perspective" ) ;
// The perspective is required to do type-checking on parameters as they
// are added to the collection.
this . _perspective = perspective ;
// Create a new list to store the parameters.
_parameters = new List < ObjectParameter > ( ) ;
}
#endregion
#endregion
#region Private Fields
// --------------
// Private Fields
// --------------
/// <summary>
/// Can parameters be added or removed from this collection?
/// </summary>
private bool _locked ;
/// <summary>
/// The internal storage for the query parameters in the collection.
/// </summary>
private List < ObjectParameter > _parameters ;
/// <summary>
/// A CLR perspective necessary to do type-checking on parameters as they
/// are added to the collection.
/// </summary>
private ClrPerspective _perspective ;
/// <summary>
/// A string that can be used to represent the current state of this parameter collection in an ObjectQuery cache key.
/// </summary>
private string _cacheKey ;
#endregion
#region Public Properties
// -----------------
// Public Properties
// -----------------
/// <summary>
/// The number of parameters currently in the collection.
/// </summary>
public int Count
{
get
{
return this . _parameters . Count ;
}
}
/// <summary>
/// This collection is read-write - parameters may be added, removed
/// and [somewhat] modified at will (value only) - provided that the
/// implementation the collection belongs to has not locked its parameters
/// because it's command definition has been prepared.
/// </summary>
bool ICollection < ObjectParameter > . IsReadOnly
{
get
{
return ( this . _locked ) ;
}
}
#endregion
#region Public Indexers
// ---------------
// Public Indexers
// ---------------
/// <summary>
/// This indexer allows callers to retrieve parameters by name. If no
/// parameter by the given name exists, an exception is thrown. For
/// safe existence-checking, use the Contains method instead.
/// </summary>
/// <param name="name">
/// The name of the parameter to find.
/// </param>
/// <returns>
/// The parameter object with the specified name.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// If no parameter with the specified name is found in the collection.
/// </exception>
public ObjectParameter this [ string name ]
{
get
{
int index = this . IndexOf ( name ) ;
if ( index = = - 1 )
{
throw EntityUtil . ArgumentOutOfRange ( System . Data . Entity . Strings . ObjectParameterCollection_ParameterNameNotFound ( name ) , "name" ) ;
}
return this . _parameters [ index ] ;
}
}
#endregion
#region Public Methods
// --------------
// Public Methods
// --------------
#region Add
/// <summary>
/// This method adds the specified parameter object to the collection. If
/// the parameter object already exists in the collection, an exception is
/// thrown.
/// </summary>
/// <param name="parameter">
/// The parameter object to add to the collection.
/// </param>
/// <returns></returns>
/// <exception cref="ArgumentNullException">
/// If the value of the parameter argument is null.
/// </exception>
/// <exception cref="ArgumentException">
/// If the parameter argument already exists in the collection. This
/// behavior differs from that of most collections which allow duplicate
/// entries.
/// </exception>
/// <exception cref="ArgumentException">
/// If another parameter with the same name as the parameter argument
/// already exists in the collection. Note that the lookup is case-
/// insensitive. This behavior differs from that of most collections,
/// and is more like that of a Dictionary.
/// </exception>
/// <exception cref="ArgumentOutOfRangeException">
/// If the type of the specified parameter is invalid.
/// </exception>
public void Add ( ObjectParameter parameter )
{
EntityUtil . CheckArgumentNull ( parameter , "parameter" ) ;
CheckUnlocked ( ) ;
if ( this . Contains ( parameter ) )
{
throw EntityUtil . Argument ( System . Data . Entity . Strings . ObjectParameterCollection_ParameterAlreadyExists ( parameter . Name ) , "parameter" ) ;
}
if ( this . Contains ( parameter . Name ) )
{
throw EntityUtil . Argument ( System . Data . Entity . Strings . ObjectParameterCollection_DuplicateParameterName ( parameter . Name ) , "parameter" ) ;
}
if ( ! parameter . ValidateParameterType ( this . _perspective ) )
{
throw EntityUtil . ArgumentOutOfRange ( System . Data . Entity . Strings . ObjectParameter_InvalidParameterType ( parameter . ParameterType . FullName ) , "parameter" ) ;
}
this . _parameters . Add ( parameter ) ;
this . _cacheKey = null ;
}
#endregion
#region Clear
/// <summary>
/// This method empties the entire parameter collection.
/// </summary>
/// <returns></returns>
public void Clear ( )
{
CheckUnlocked ( ) ;
this . _parameters . Clear ( ) ;
this . _cacheKey = null ;
}
#endregion
#region Contains ( ObjectParameter )
/// <summary>
/// This methods checks for the existence of a given parameter object in the
/// collection by reference.
/// </summary>
/// <param name="parameter">
/// The parameter object to look for in the collection.
/// </param>
/// <returns>
/// True if the parameter object was found in the collection, false otherwise.
/// Note that this is a reference-based lookup, which means that if the para-
/// meter argument has the same name as a parameter object in the collection,
/// this method will only return true if it's the same object.
/// </returns>
/// <exception cref="ArgumentNullException">
/// If the value of the parameter argument is null.
/// </exception>
public bool Contains ( ObjectParameter parameter )
{
EntityUtil . CheckArgumentNull ( parameter , "parameter" ) ;
return this . _parameters . Contains ( parameter ) ;
}
#endregion
#region Contains ( string )
/// <summary>
/// This method checks for the existence of a given parameter in the collection
/// by name.
/// </summary>
/// <param name="name">
/// The name of the parameter to look for in the collection.
/// </param>
/// <returns>
/// True if a parameter with the specified name was found in the collection,
/// false otherwise. Note that the lookup is case-insensitive.
/// </returns>
/// <exception cref="ArgumentNullException">
/// If the value of the parameter argument is null.
/// </exception>
public bool Contains ( string name )
{
EntityUtil . CheckArgumentNull ( name , "name" ) ;
if ( this . IndexOf ( name ) ! = - 1 )
{
return true ;
}
return false ;
}
#endregion
#region CopyTo
/// <summary>
/// This method allows the parameters in the collection to be copied into a
/// supplied array, beginning at the specified index therein.
/// </summary>
/// <param name="array">
/// The array into which to copy the parameters.
/// </param>
/// <param name="index">
/// The index in the array at which to start copying the parameters.
/// </param>
/// <returns></returns>
public void CopyTo ( ObjectParameter [ ] array , int index )
{
this . _parameters . CopyTo ( array , index ) ;
}
#endregion
#region Remove
/// <summary>
/// This method removes an instance of a parameter from the collection by
/// reference if it exists in the collection. To remove a parameter by name,
/// first use the Contains(name) method or this[name] indexer to retrieve
/// the parameter instance, then remove it using this method.
/// </summary>
/// <param name="parameter">
/// The parameter object to remove from the collection.
/// </param>
/// <returns>
/// True if the parameter object was found and removed from the collection,
/// false otherwise. Note that this is a reference-based lookup, which means
/// that if the parameter argument has the same name as a parameter object
/// in the collection, this method will remove it only if it's the same object.
/// </returns>
/// <exception cref="ArgumentNullException">
/// If the value of the parameter argument is null.
/// </exception>
public bool Remove ( ObjectParameter parameter )
{
EntityUtil . CheckArgumentNull ( parameter , "parameter" ) ;
CheckUnlocked ( ) ;
bool removed = this . _parameters . Remove ( parameter ) ;
// If the specified parameter was found in the collection and removed,
// clear out the cached string representation of this parameter collection
// so that the next call to GetCacheKey (if any) will regenerate it based on
// the new state of this collection.
if ( removed )
{
this . _cacheKey = null ;
}
return removed ;
}
#endregion
#region GetEnumerator
/// <summary>
/// These methods return enumerator instances, which allow the collection to
/// be iterated through and traversed.
/// </summary>
IEnumerator < ObjectParameter > IEnumerable < ObjectParameter > . GetEnumerator ( )
{
return ( ( System . Collections . Generic . ICollection < ObjectParameter > ) this . _parameters ) . GetEnumerator ( ) ;
}
IEnumerator IEnumerable . GetEnumerator ( )
{
return ( ( System . Collections . ICollection ) this . _parameters ) . GetEnumerator ( ) ;
}
#endregion
#endregion
#region Internal Methods
// ---------------
// Internal Methods
// ---------------
/// <summary>
/// Retrieves a string that may be used to represent this parameter collection in an ObjectQuery cache key.
/// If this collection has not changed since the last call to this method, the same string instance is returned.
/// Note that this string is used by various ObjectQueryImplementations to version the parameter collection.
/// </summary>
/// <returns>A string that may be used to represent this parameter collection in an ObjectQuery cache key.</returns>
internal string GetCacheKey ( )
{
if ( null = = this . _cacheKey )
{
if ( this . _parameters . Count > 0 )
{
// Future Enhancement: If the separate branch for a single parameter does not have a measurable perf advantage, remove it.
if ( 1 = = this . _parameters . Count )
{
// if its one parameter only, there is no need to use stringbuilder
ObjectParameter theParam = this . _parameters [ 0 ] ;
this . _cacheKey = "@@1" + theParam . Name + ":" + theParam . ParameterType . FullName ;
}
else
{
// Future Enhancement: Investigate whether precalculating the required size of the string builder is a better time/space tradeoff.
StringBuilder keyBuilder = new StringBuilder ( this . _parameters . Count * 20 ) ;
keyBuilder . Append ( "@@" ) ;
keyBuilder . Append ( this . _parameters . Count ) ;
for ( int idx = 0 ; idx < this . _parameters . Count ; idx + + )
{
//
//
if ( idx > 0 )
{
keyBuilder . Append ( ";" ) ;
}
ObjectParameter thisParam = this . _parameters [ idx ] ;
keyBuilder . Append ( thisParam . Name ) ;
keyBuilder . Append ( ":" ) ;
keyBuilder . Append ( thisParam . ParameterType . FullName ) ;
}
this . _cacheKey = keyBuilder . ToString ( ) ;
}
}
}
return this . _cacheKey ;
}
/// <summary>
/// Locks or unlocks this parameter collection, allowing its contents to be added to, removed from, or cleared.
/// Calling this method consecutively with the same value has no effect but does not throw an exception.
/// </summary>
/// <param name="isReadOnly">If <c>true</c>, this parameter collection is now locked; otherwise it is unlocked</param>
internal void SetReadOnly ( bool isReadOnly ) { this . _locked = isReadOnly ; }
/// <summary>
/// Creates a new copy of the specified parameter collection containing copies of its element <see cref="ObjectParameter"/>s.
/// If the specified argument is <c>null</c>, then <c>null</c> is returned.
/// </summary>
/// <param name="copyParams">The parameter collection to copy</param>
/// <returns>The new collection containing copies of <paramref name="copyParams"/> parameters, if <paramref name="copyParams"/> is non-null; otherwise <c>null</c>.</returns>
internal static ObjectParameterCollection DeepCopy ( ObjectParameterCollection copyParams )
{
if ( null = = copyParams )
{
return null ;
}
ObjectParameterCollection retParams = new ObjectParameterCollection ( copyParams . _perspective ) ;
foreach ( ObjectParameter param in copyParams )
{
retParams . Add ( param . ShallowCopy ( ) ) ;
}
return retParams ;
}
#endregion
#region Private Methods
// ---------------
// Private Methods
// ---------------
/// <summary>
/// This private method checks for the existence of a given parameter object
/// by name by iterating through the list and comparing each parameter name
/// to the specified name. This is a case-insensitive lookup.
/// </summary>
private int IndexOf ( string name )
{
int index = 0 ;
foreach ( ObjectParameter parameter in this . _parameters )
{
if ( 0 = = String . Compare ( name , parameter . Name , StringComparison . OrdinalIgnoreCase ) )
{
return index ;
}
index + + ;
}
return - 1 ;
}
/// <summary>
/// This method successfully returns only if the parameter collection is not considered 'locked';
/// otherwise an <see cref="InvalidOperationException"/> is thrown.
/// </summary>
private void CheckUnlocked ( )
{
if ( this . _locked )
{
throw EntityUtil . InvalidOperation ( Strings . ObjectParameterCollection_ParametersLocked ) ;
}
}
#endregion
}
}