// created on 09/07/2003 at 20:20
// Npgsql.NpgsqlParameterCollection.cs
//
// Author:
// Brar Piening (brar@gmx.de)
//
// Rewritten from the scratch to derive from MarshalByRefObject instead of ArrayList.
// Recycled some parts of the original NpgsqlParameterCollection.cs
// by Francisco Jr. (fxjrlists@yahoo.com.br)
//
// Copyright (C) 2002 The Npgsql Development Team
// npgsql-general@gborg.postgresql.org
// http://gborg.postgresql.org/project/npgsql/projdisplay.php
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose, without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph and the following two paragraphs appear in all copies.
//
// IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
// INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
// DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
//
// THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
// ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS
// TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Common;
using System.Reflection;
using System.Resources;
using NpgsqlTypes;
#if WITHDESIGN
#endif
namespace Npgsql
{
///
/// Represents a collection of parameters relevant to a NpgsqlCommand
/// as well as their respective mappings to columns in a DataSet.
/// This class cannot be inherited.
///
#if WITHDESIGN
[ListBindable(false)]
[Editor(typeof(NpgsqlParametersEditor), typeof(System.Drawing.Design.UITypeEditor))]
#endif
public sealed class NpgsqlParameterCollection : DbParameterCollection
{
private readonly List InternalList = new List();
// Dictionary lookups for GetValue to improve performance
private Dictionary lookup;
private Dictionary lookupIgnoreCase;
// Logging related value
private static readonly String CLASSNAME = MethodBase.GetCurrentMethod().DeclaringType.Name;
// Our resource manager
private static readonly ResourceManager resman = new ResourceManager(MethodBase.GetCurrentMethod().DeclaringType);
///
/// Initializes a new instance of the NpgsqlParameterCollection class.
///
internal NpgsqlParameterCollection()
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, CLASSNAME);
InvalidateHashLookups();
}
///
/// Invalidate the hash lookup tables. This should be done any time a change
/// may throw the lookups out of sync with the list.
///
internal void InvalidateHashLookups()
{
lookup = null;
lookupIgnoreCase = null;
}
#region NpgsqlParameterCollection Member
///
/// Gets the NpgsqlParameter with the specified name.
///
/// The name of the NpgsqlParameter to retrieve.
/// The NpgsqlParameter with the specified name, or a null reference if the parameter is not found.
#if WITHDESIGN
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endif
public new NpgsqlParameter this[string parameterName]
{
get
{
NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, parameterName);
int index = IndexOf(parameterName);
if (index == -1)
{
throw new IndexOutOfRangeException("Parameter not found");
}
return this.InternalList[index];
}
set
{
NpgsqlEventLog.LogIndexerSet(LogLevel.Debug, CLASSNAME, parameterName, value);
int index = IndexOf(parameterName);
if (index == -1)
{
throw new IndexOutOfRangeException("Parameter not found");
}
NpgsqlParameter oldValue = this.InternalList[index];
if (value.CleanName != oldValue.CleanName)
{
InvalidateHashLookups();
}
this.InternalList[index] = value;
}
}
///
/// Gets the NpgsqlParameter at the specified index.
///
/// The zero-based index of the NpgsqlParameter to retrieve.
/// The NpgsqlParameter at the specified index.
#if WITHDESIGN
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endif
public new NpgsqlParameter this[int index]
{
get
{
NpgsqlEventLog.LogIndexerGet(LogLevel.Debug, CLASSNAME, index);
return this.InternalList[index];
}
set
{
NpgsqlEventLog.LogIndexerSet(LogLevel.Debug, CLASSNAME, index, value);
NpgsqlParameter oldValue = this.InternalList[index];
if (oldValue == value)
{
// Reasigning the same value is a non-op.
return;
}
if (value.Collection != null)
{
throw new InvalidOperationException("The parameter already belongs to a collection");
}
if (value.CleanName != oldValue.CleanName)
{
InvalidateHashLookups();
}
this.InternalList[index] = value;
value.Collection = this;
oldValue.Collection = null;
}
}
///
/// Adds the specified NpgsqlParameter object to the NpgsqlParameterCollection.
///
/// The NpgsqlParameter to add to the collection.
/// The index of the new NpgsqlParameter object.
public NpgsqlParameter Add(NpgsqlParameter value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", value);
// Do not allow parameters without name.
if (value.Collection != null)
{
throw new InvalidOperationException("The parameter already belongs to a collection");
}
this.InternalList.Add(value);
value.Collection = this;
this.InvalidateHashLookups();
// Check if there is a name. If not, add a name based in the index of parameter.
if (value.ParameterName.Trim() == String.Empty || (value.ParameterName.Length == 1 && value.ParameterName[0] == ':'))
{
value.ParameterName = ":" + "Parameter" + (IndexOf(value) + 1);
}
return value;
}
///
/// Obsolete. Use AddWithValue instead.
///
///
/// Use caution when using this overload of the
/// Add method to specify integer parameter values.
/// Because this overload takes a value of type Object,
/// you must convert the integral value to an Object
/// type when the value is zero, as the following C# example demonstrates.
/// parameters.Add(":pname", Convert.ToInt32(0));
/// If you do not perform this conversion, the compiler will assume you
/// are attempting to call the NpgsqlParameterCollection.Add(string, DbType) overload.
///
[Obsolete("Do not call this method. Use AddWithValue instead.")]
public NpgsqlParameter Add(string parameterName, object value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, value);
return this.AddWithValue(parameterName, value);
}
///
/// Adds a NpgsqlParameter to the NpgsqlParameterCollection given the specified parameter name and value.
///
/// The name of the NpgsqlParameter.
/// The Value of the NpgsqlParameter to add to the collection.
/// The paramater that was added.
public NpgsqlParameter AddWithValue(string parameterName, object value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "AddWithValue", parameterName, value);
return this.Add(new NpgsqlParameter(parameterName, value));
}
///
/// Adds a NpgsqlParameter to the NpgsqlParameterCollection given the specified parameter name and value.
///
/// The name of the NpgsqlParameter.
/// The Value of the NpgsqlParameter to add to the collection.
/// One of the NpgsqlDbType values.
/// The paramater that was added.
public NpgsqlParameter AddWithValue(string parameterName, NpgsqlDbType parameterType, object value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "AddWithValue", parameterName, parameterType, value);
NpgsqlParameter param = new NpgsqlParameter(parameterName, parameterType);
param.Value = value;
return this.Add(param);
}
///
/// Adds a NpgsqlParameter to the NpgsqlParameterCollection given the specified parameter name and value.
///
/// The name of the NpgsqlParameter.
/// The Value of the NpgsqlParameter to add to the collection.
/// One of the NpgsqlDbType values.
/// The length of the column.
/// The paramater that was added.
public NpgsqlParameter AddWithValue(string parameterName, NpgsqlDbType parameterType, int size, object value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "AddWithValue", parameterName, parameterType, size, value);
NpgsqlParameter param = new NpgsqlParameter(parameterName, parameterType, size);
param.Value = value;
return this.Add(param);
}
///
/// Adds a NpgsqlParameter to the NpgsqlParameterCollection given the specified parameter name and value.
///
/// The name of the NpgsqlParameter.
/// The Value of the NpgsqlParameter to add to the collection.
/// One of the NpgsqlDbType values.
/// The length of the column.
/// The name of the source column.
/// The paramater that was added.
public NpgsqlParameter AddWithValue(string parameterName, NpgsqlDbType parameterType, int size, string sourceColumn, object value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "AddWithValue", parameterName, parameterType, size, sourceColumn, value);
NpgsqlParameter param = new NpgsqlParameter(parameterName, parameterType, size, sourceColumn);
param.Value = value;
return this.Add(param);
}
///
/// Adds a NpgsqlParameter to the NpgsqlParameterCollection given the parameter name and the data type.
///
/// The name of the parameter.
/// One of the DbType values.
/// The index of the new NpgsqlParameter object.
public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType);
return this.Add(new NpgsqlParameter(parameterName, parameterType));
}
///
/// Adds a NpgsqlParameter to the NpgsqlParameterCollection with the parameter name, the data type, and the column length.
///
/// The name of the parameter.
/// One of the DbType values.
/// The length of the column.
/// The index of the new NpgsqlParameter object.
public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType, int size)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType, size);
return this.Add(new NpgsqlParameter(parameterName, parameterType, size));
}
///
/// Adds a NpgsqlParameter to the NpgsqlParameterCollection with the parameter name, the data type, the column length, and the source column name.
///
/// The name of the parameter.
/// One of the DbType values.
/// The length of the column.
/// The name of the source column.
/// The index of the new NpgsqlParameter object.
public NpgsqlParameter Add(string parameterName, NpgsqlDbType parameterType, int size, string sourceColumn)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", parameterName, parameterType, size, sourceColumn);
return this.Add(new NpgsqlParameter(parameterName, parameterType, size, sourceColumn));
}
#endregion
#region IDataParameterCollection Member
///
/// Removes the specified NpgsqlParameter from the collection using the parameter name.
///
/// The name of the NpgsqlParameter object to retrieve.
public override void RemoveAt(string parameterName)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "RemoveAt", parameterName);
int index = IndexOf(parameterName);
NpgsqlParameter existing = InternalList[index];
this.InternalList.RemoveAt(index);
existing.Collection = null;
this.InvalidateHashLookups();
}
///
/// Gets a value indicating whether a NpgsqlParameter with the specified parameter name exists in the collection.
///
/// The name of the NpgsqlParameter object to find.
/// true if the collection contains the parameter; otherwise, false.
public override bool Contains(string parameterName)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Contains", parameterName);
return (IndexOf(parameterName) != -1);
}
///
/// Gets the location of the NpgsqlParameter in the collection with a specific parameter name.
///
/// The name of the NpgsqlParameter object to find.
/// The zero-based location of the NpgsqlParameter in the collection.
public override int IndexOf(string parameterName)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IndexOf", parameterName);
int retIndex;
int scanIndex;
if ((parameterName[0] == ':') || (parameterName[0] == '@'))
{
parameterName = parameterName.Remove(0, 1);
}
// Using a dictionary is much faster for 5 or more items
if (this.InternalList.Count >= 5)
{
if (this.lookup == null)
{
this.lookup = new Dictionary();
for (scanIndex = 0 ; scanIndex < this.InternalList.Count ; scanIndex++)
{
var item = this.InternalList[scanIndex];
// Store only the first of each distinct value
if (! this.lookup.ContainsKey(item.CleanName))
{
this.lookup.Add(item.CleanName, scanIndex);
}
}
}
// Try to access the case sensitive parameter name first
if (this.lookup.TryGetValue(parameterName, out retIndex))
{
return retIndex;
}
// Case sensitive lookup failed, generate a case insensitive lookup
if (this.lookupIgnoreCase == null)
{
this.lookupIgnoreCase = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
for (scanIndex = 0 ; scanIndex < this.InternalList.Count ; scanIndex++)
{
var item = this.InternalList[scanIndex];
// Store only the first of each distinct value
if (! this.lookupIgnoreCase.ContainsKey(item.CleanName))
{
this.lookupIgnoreCase.Add(item.CleanName, scanIndex);
}
}
}
// Then try to access the case insensitive parameter name
if (this.lookupIgnoreCase.TryGetValue(parameterName, out retIndex))
{
return retIndex;
}
return -1;
}
retIndex = -1;
// Scan until a case insensitive match is found, and save its index for possible return.
// Items that don't match loosely cannot possibly match exactly.
for (scanIndex = 0 ; scanIndex < this.InternalList.Count ; scanIndex++)
{
var item = this.InternalList[scanIndex];
if (string.Compare(parameterName, item.CleanName, StringComparison.InvariantCultureIgnoreCase) == 0)
{
retIndex = scanIndex;
break;
}
}
// Then continue the scan until a case sensitive match is found, and return it.
// If a case insensitive match was found, it will be re-checked for an exact match.
for ( ; scanIndex < this.InternalList.Count ; scanIndex++)
{
var item = this.InternalList[scanIndex];
if(item.CleanName == parameterName)
{
return scanIndex;
}
}
// If a case insensitive match was found, it will be returned here.
return retIndex;
}
#endregion
#region IList Member
public override bool IsReadOnly
{
get
{
NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsReadOnly");
return false;
}
}
///
/// Removes the specified NpgsqlParameter from the collection using a specific index.
///
/// The zero-based index of the parameter.
public override void RemoveAt(int index)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "RemoveAt", index);
if (InternalList.Count - 1 < index)
{
throw new IndexOutOfRangeException();
}
InternalList[index].Collection = null;
InternalList.RemoveAt(index);
InvalidateHashLookups();
}
///
/// Inserts a NpgsqlParameter into the collection at the specified index.
///
/// The zero-based index where the parameter is to be inserted within the collection.
/// The NpgsqlParameter to add to the collection.
public override void Insert(int index, object oValue)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Insert", index, oValue);
CheckType(oValue);
NpgsqlParameter value = oValue as NpgsqlParameter;
if (value.Collection != null)
{
throw new InvalidOperationException("The parameter already belongs to a collection");
}
value.Collection = this;
this.InternalList.Insert(index, value);
this.InvalidateHashLookups();
}
///
/// Removes the specified NpgsqlParameter from the collection.
///
/// The name of the NpgsqlParameter to remove from the collection.
public void Remove(string parameterName)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Remove", parameterName);
int index;
index = IndexOf(parameterName);
if (index < 0)
{
throw new InvalidOperationException("No parameter with the specified name exists in the collection");
}
this.InternalList[index].Collection = null;
this.InternalList.RemoveAt(index);
this.InvalidateHashLookups();
}
///
/// Removes the specified NpgsqlParameter from the collection.
///
/// The NpgsqlParameter to remove from the collection.
public override void Remove(object oValue)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Remove", oValue);
CheckType(oValue);
NpgsqlParameter value = oValue as NpgsqlParameter;
if (value.Collection != this)
{
throw new InvalidOperationException("The item does not belong to this collection");
}
value.Collection = null;
this.InternalList.Remove(value);
this.InvalidateHashLookups();
}
///
/// Gets a value indicating whether a NpgsqlParameter exists in the collection.
///
/// The value of the NpgsqlParameter object to find.
/// true if the collection contains the NpgsqlParameter object; otherwise, false.
public override bool Contains(object value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Contains", value);
if (!(value is NpgsqlParameter))
{
return false;
}
return this.InternalList.Contains((NpgsqlParameter) value);
}
///
/// Gets a value indicating whether a NpgsqlParameter with the specified parameter name exists in the collection.
///
/// The name of the NpgsqlParameter object to find.
/// A reference to the requested parameter is returned in this out param if it is found in the list. This value is null if the parameter is not found.
/// true if the collection contains the parameter and param will contain the parameter; otherwise, false.
public bool TryGetValue(string parameterName, out NpgsqlParameter parameter)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "TryGetValue", parameterName);
int index = IndexOf(parameterName);
if (index != -1)
{
parameter = InternalList[index];
return true;
}
else
{
parameter = null;
return false;
}
}
///
/// Removes all items from the collection.
///
public override void Clear()
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Clear");
this.InternalList.Clear();
this.InvalidateHashLookups();
}
///
/// Gets the location of a NpgsqlParameter in the collection.
///
/// The value of the NpgsqlParameter object to find.
/// The zero-based index of the NpgsqlParameter object in the collection.
public override int IndexOf(object value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "IndexOf", value);
CheckType(value);
return this.InternalList.IndexOf((NpgsqlParameter) value);
}
///
/// Adds the specified NpgsqlParameter object to the NpgsqlParameterCollection.
///
/// The NpgsqlParameter to add to the collection.
/// The zero-based index of the new NpgsqlParameter object.
public override int Add(object value)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "Add", value);
CheckType(value);
this.Add((NpgsqlParameter) value);
return Count - 1;
}
public override bool IsFixedSize
{
get
{
NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsFixedSize");
return false;
}
}
#endregion
#region ICollection Member
public override bool IsSynchronized
{
get
{
NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "IsSynchronized");
return (InternalList as ICollection).IsSynchronized;
}
}
///
/// Gets the number of NpgsqlParameter objects in the collection.
///
/// The number of NpgsqlParameter objects in the collection.
#if WITHDESIGN
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
#endif
public override int Count
{
get
{
NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "Count");
return this.InternalList.Count;
}
}
///
/// Copies NpgsqlParameter objects from the NpgsqlParameterCollection to the specified array.
///
/// An Array to which to copy the NpgsqlParameter objects in the collection.
/// The starting index of the array.
public override void CopyTo(Array array, int index)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CopyTo", array, index);
(InternalList as ICollection).CopyTo(array, index);
IRaiseItemChangedEvents x = InternalList as IRaiseItemChangedEvents;
}
public override object SyncRoot
{
get
{
NpgsqlEventLog.LogPropertyGet(LogLevel.Debug, CLASSNAME, "SyncRoot");
return (InternalList as ICollection).SyncRoot;
}
}
#endregion
#region IEnumerable Member
///
/// Returns an enumerator that can iterate through the collection.
///
/// An IEnumerator that can be used to iterate through the collection.
public override IEnumerator GetEnumerator()
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "GetEnumerator");
return this.InternalList.GetEnumerator();
}
#endregion
public override void AddRange(Array values)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "AddRange", values);
foreach (NpgsqlParameter parameter in values)
{
Add(parameter);
}
}
protected override DbParameter GetParameter(string parameterName)
{
return this[parameterName];
}
protected override DbParameter GetParameter(int index)
{
return this[index];
}
protected override void SetParameter(string parameterName, DbParameter value)
{
this[parameterName] = (NpgsqlParameter) value;
}
protected override void SetParameter(int index, DbParameter value)
{
this[index] = (NpgsqlParameter) value;
}
///
/// In methods taking an object as argument this method is used to verify
/// that the argument has the type NpgsqlParameter
///
/// The object to verify
private void CheckType(object Object)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CheckType", Object);
if (!(Object is NpgsqlParameter))
{
throw new InvalidCastException(
String.Format(resman.GetString("Exception_WrongType"), Object.GetType()));
}
}
/*
///
/// In methods taking an array as argument this method is used to verify
/// that the argument has the type NpgsqlParameter[]
///
/// The array to verify
private void CheckType(Array array)
{
NpgsqlEventLog.LogMethodEnter(LogLevel.Debug, CLASSNAME, "CheckType", array);
if (array.GetType() != typeof (NpgsqlParameter[]))
{
throw new InvalidCastException(
String.Format(this.resman.GetString("Exception_WrongType"), array.GetType().ToString()));
}
}
*/
public int IndexOf(NpgsqlParameter item)
{
return InternalList.IndexOf(item);
}
public void Insert(int index, NpgsqlParameter item)
{
if (item.Collection != null)
{
throw new Exception("The parameter already belongs to a collection");
}
InternalList.Insert(index, item);
item.Collection = this;
this.InvalidateHashLookups();
}
public bool Contains(NpgsqlParameter item)
{
return InternalList.Contains(item);
}
public bool Remove(NpgsqlParameter item)
{
if (InternalList.Remove(item))
{
item.Collection = null;
this.InvalidateHashLookups();
return true;
}
return false;
}
public void CopyTo(NpgsqlParameter[] array, int arrayIndex)
{
InternalList.CopyTo(array, arrayIndex);
}
public NpgsqlParameter[] ToArray()
{
return InternalList.ToArray();
}
}
}