1 //---------------------------------------------------------------------
2 // <copyright file="ObjectParameterCollection.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupowner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Objects
13 using System.Collections;
14 using System.Collections.Generic;
16 using System.Data.Entity;
17 using System.Data.Metadata.Edm;
21 /// This class represents a collection of query parameters at the object layer.
23 public sealed class ObjectParameterCollection : ICollection<ObjectParameter>
25 // Note: There are NO public constructors for this class - it is for internal
26 // ObjectQuery<T> use only, but must be public so that an instance thereof can be
27 // a public property on ObjectQuery<T>.
29 #region Internal Constructors
31 // ---------------------
32 // Internal Constructors
33 // ---------------------
35 #region ObjectParameterCollection (ClrPerspective)
38 /// This internal constructor creates a new query parameter collection and
39 /// initializes the internal parameter storage.
41 internal ObjectParameterCollection(ClrPerspective perspective)
43 EntityUtil.CheckArgumentNull(perspective, "perspective");
45 // The perspective is required to do type-checking on parameters as they
46 // are added to the collection.
47 this._perspective = perspective;
49 // Create a new list to store the parameters.
50 _parameters = new List<ObjectParameter>();
57 #region Private Fields
64 /// Can parameters be added or removed from this collection?
69 /// The internal storage for the query parameters in the collection.
71 private List<ObjectParameter> _parameters;
74 /// A CLR perspective necessary to do type-checking on parameters as they
75 /// are added to the collection.
77 private ClrPerspective _perspective;
80 /// A string that can be used to represent the current state of this parameter collection in an ObjectQuery cache key.
82 private string _cacheKey;
86 #region Public Properties
93 /// The number of parameters currently in the collection.
99 return this._parameters.Count;
104 /// This collection is read-write - parameters may be added, removed
105 /// and [somewhat] modified at will (value only) - provided that the
106 /// implementation the collection belongs to has not locked its parameters
107 /// because it's command definition has been prepared.
109 bool ICollection<ObjectParameter>.IsReadOnly
113 return (this._locked);
119 #region Public Indexers
126 /// This indexer allows callers to retrieve parameters by name. If no
127 /// parameter by the given name exists, an exception is thrown. For
128 /// safe existence-checking, use the Contains method instead.
130 /// <param name="name">
131 /// The name of the parameter to find.
134 /// The parameter object with the specified name.
136 /// <exception cref="ArgumentOutOfRangeException">
137 /// If no parameter with the specified name is found in the collection.
139 public ObjectParameter this[string name]
143 int index = this.IndexOf(name);
147 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.ObjectParameterCollection_ParameterNameNotFound(name), "name");
150 return this._parameters[index];
156 #region Public Methods
165 /// This method adds the specified parameter object to the collection. If
166 /// the parameter object already exists in the collection, an exception is
169 /// <param name="parameter">
170 /// The parameter object to add to the collection.
172 /// <returns></returns>
173 /// <exception cref="ArgumentNullException">
174 /// If the value of the parameter argument is null.
176 /// <exception cref="ArgumentException">
177 /// If the parameter argument already exists in the collection. This
178 /// behavior differs from that of most collections which allow duplicate
181 /// <exception cref="ArgumentException">
182 /// If another parameter with the same name as the parameter argument
183 /// already exists in the collection. Note that the lookup is case-
184 /// insensitive. This behavior differs from that of most collections,
185 /// and is more like that of a Dictionary.
187 /// <exception cref="ArgumentOutOfRangeException">
188 /// If the type of the specified parameter is invalid.
190 public void Add (ObjectParameter parameter)
192 EntityUtil.CheckArgumentNull(parameter, "parameter");
195 if (this.Contains(parameter))
197 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectParameterCollection_ParameterAlreadyExists(parameter.Name), "parameter");
200 if (this.Contains(parameter.Name))
202 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectParameterCollection_DuplicateParameterName(parameter.Name), "parameter");
205 if (!parameter.ValidateParameterType(this._perspective))
207 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.ObjectParameter_InvalidParameterType(parameter.ParameterType.FullName), "parameter");
210 this._parameters.Add(parameter);
211 this._cacheKey = null;
219 /// This method empties the entire parameter collection.
221 /// <returns></returns>
225 this._parameters.Clear();
226 this._cacheKey = null;
231 #region Contains (ObjectParameter)
234 /// This methods checks for the existence of a given parameter object in the
235 /// collection by reference.
237 /// <param name="parameter">
238 /// The parameter object to look for in the collection.
241 /// True if the parameter object was found in the collection, false otherwise.
242 /// Note that this is a reference-based lookup, which means that if the para-
243 /// meter argument has the same name as a parameter object in the collection,
244 /// this method will only return true if it's the same object.
246 /// <exception cref="ArgumentNullException">
247 /// If the value of the parameter argument is null.
249 public bool Contains (ObjectParameter parameter)
251 EntityUtil.CheckArgumentNull(parameter, "parameter");
253 return this._parameters.Contains(parameter);
258 #region Contains (string)
261 /// This method checks for the existence of a given parameter in the collection
264 /// <param name="name">
265 /// The name of the parameter to look for in the collection.
268 /// True if a parameter with the specified name was found in the collection,
269 /// false otherwise. Note that the lookup is case-insensitive.
271 /// <exception cref="ArgumentNullException">
272 /// If the value of the parameter argument is null.
274 public bool Contains (string name)
276 EntityUtil.CheckArgumentNull(name, "name");
278 if (this.IndexOf(name) != -1)
291 /// This method allows the parameters in the collection to be copied into a
292 /// supplied array, beginning at the specified index therein.
294 /// <param name="array">
295 /// The array into which to copy the parameters.
297 /// <param name="index">
298 /// The index in the array at which to start copying the parameters.
300 /// <returns></returns>
301 public void CopyTo (ObjectParameter[] array, int index)
303 this._parameters.CopyTo(array, index);
311 /// This method removes an instance of a parameter from the collection by
312 /// reference if it exists in the collection. To remove a parameter by name,
313 /// first use the Contains(name) method or this[name] indexer to retrieve
314 /// the parameter instance, then remove it using this method.
316 /// <param name="parameter">
317 /// The parameter object to remove from the collection.
320 /// True if the parameter object was found and removed from the collection,
321 /// false otherwise. Note that this is a reference-based lookup, which means
322 /// that if the parameter argument has the same name as a parameter object
323 /// in the collection, this method will remove it only if it's the same object.
325 /// <exception cref="ArgumentNullException">
326 /// If the value of the parameter argument is null.
328 public bool Remove (ObjectParameter parameter)
330 EntityUtil.CheckArgumentNull(parameter, "parameter");
333 bool removed = this._parameters.Remove(parameter);
335 // If the specified parameter was found in the collection and removed,
336 // clear out the cached string representation of this parameter collection
337 // so that the next call to GetCacheKey (if any) will regenerate it based on
338 // the new state of this collection.
341 this._cacheKey = null;
349 #region GetEnumerator
352 /// These methods return enumerator instances, which allow the collection to
353 /// be iterated through and traversed.
355 IEnumerator<ObjectParameter> IEnumerable<ObjectParameter>.GetEnumerator()
357 return ((System.Collections.Generic.ICollection<ObjectParameter>)this._parameters).GetEnumerator();
360 IEnumerator IEnumerable.GetEnumerator()
362 return ((System.Collections.ICollection)this._parameters).GetEnumerator();
369 #region Internal Methods
376 /// Retrieves a string that may be used to represent this parameter collection in an ObjectQuery cache key.
377 /// If this collection has not changed since the last call to this method, the same string instance is returned.
378 /// Note that this string is used by various ObjectQueryImplementations to version the parameter collection.
380 /// <returns>A string that may be used to represent this parameter collection in an ObjectQuery cache key.</returns>
381 internal string GetCacheKey()
383 if (null == this._cacheKey)
385 if(this._parameters.Count > 0)
387 // Future Enhancement: If the separate branch for a single parameter does not have a measurable perf advantage, remove it.
388 if (1 == this._parameters.Count)
390 // if its one parameter only, there is no need to use stringbuilder
391 ObjectParameter theParam = this._parameters[0];
392 this._cacheKey = "@@1" + theParam.Name + ":" + theParam.ParameterType.FullName;
396 // Future Enhancement: Investigate whether precalculating the required size of the string builder is a better time/space tradeoff.
397 StringBuilder keyBuilder = new StringBuilder(this._parameters.Count * 20);
398 keyBuilder.Append("@@");
399 keyBuilder.Append(this._parameters.Count);
400 for (int idx = 0; idx < this._parameters.Count; idx++)
407 keyBuilder.Append(";");
410 ObjectParameter thisParam = this._parameters[idx];
411 keyBuilder.Append(thisParam.Name);
412 keyBuilder.Append(":");
413 keyBuilder.Append(thisParam.ParameterType.FullName);
416 this._cacheKey = keyBuilder.ToString();
421 return this._cacheKey;
425 /// Locks or unlocks this parameter collection, allowing its contents to be added to, removed from, or cleared.
426 /// Calling this method consecutively with the same value has no effect but does not throw an exception.
428 /// <param name="isReadOnly">If <c>true</c>, this parameter collection is now locked; otherwise it is unlocked</param>
429 internal void SetReadOnly(bool isReadOnly) { this._locked = isReadOnly; }
432 /// Creates a new copy of the specified parameter collection containing copies of its element <see cref="ObjectParameter"/>s.
433 /// If the specified argument is <c>null</c>, then <c>null</c> is returned.
435 /// <param name="copyParams">The parameter collection to copy</param>
436 /// <returns>The new collection containing copies of <paramref name="copyParams"/> parameters, if <paramref name="copyParams"/> is non-null; otherwise <c>null</c>.</returns>
437 internal static ObjectParameterCollection DeepCopy(ObjectParameterCollection copyParams)
439 if (null == copyParams)
444 ObjectParameterCollection retParams = new ObjectParameterCollection(copyParams._perspective);
445 foreach (ObjectParameter param in copyParams)
447 retParams.Add(param.ShallowCopy());
455 #region Private Methods
462 /// This private method checks for the existence of a given parameter object
463 /// by name by iterating through the list and comparing each parameter name
464 /// to the specified name. This is a case-insensitive lookup.
466 private int IndexOf (string name)
470 foreach (ObjectParameter parameter in this._parameters)
472 if (0 == String.Compare(name, parameter.Name, StringComparison.OrdinalIgnoreCase))
484 /// This method successfully returns only if the parameter collection is not considered 'locked';
485 /// otherwise an <see cref="InvalidOperationException"/> is thrown.
487 private void CheckUnlocked()
491 throw EntityUtil.InvalidOperation(Strings.ObjectParameterCollection_ParametersLocked);