1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
//------------------------------------------------------------------------------
// <copyright file="EntityClientCacheKey.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//------------------------------------------------------------------------------
namespace System.Data.Common.QueryCache
{
using System;
using System.Collections.Generic;
using System.Data.Common.Internal;
using System.Data.EntityClient;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Globalization;
using System.Text;
/// <summary>
/// Represents EntityCommand Cache key context
/// </summary>
internal sealed class EntityClientCacheKey : QueryCacheKey
{
/// <summary>
/// Stored procedure or command text?
/// </summary>
readonly CommandType _commandType;
/// <summary>
/// Entity Sql statement
/// </summary>
readonly string _eSqlStatement;
/// <summary>
/// parameter collection token
/// </summary>
readonly string _parametersToken;
/// <summary>
/// number of parameters
/// </summary>
readonly int _parameterCount;
/// <summary>
/// Combined Hashcode based on field hashcodes
/// </summary>
readonly int _hashCode;
/// <summary>
/// Creates a new instance of EntityClientCacheKey given a entityCommand instance
/// </summary>
/// <param name="entityCommand"></param>
internal EntityClientCacheKey(EntityCommand entityCommand)
: base()
{
// Command Type
_commandType = entityCommand.CommandType;
// Statement
_eSqlStatement = entityCommand.CommandText;
// Parameters
_parametersToken = GetParametersToken(entityCommand);
_parameterCount = entityCommand.Parameters.Count;
// Hashcode
_hashCode = _commandType.GetHashCode() ^
_eSqlStatement.GetHashCode() ^
_parametersToken.GetHashCode();
}
/// <summary>
/// determines equality of two cache keys based on cache context values
/// </summary>
/// <param name="otherObject"></param>
/// <returns></returns>
public override bool Equals( object otherObject )
{
Debug.Assert(null != otherObject, "otherObject must not be null");
if (typeof(EntityClientCacheKey) != otherObject.GetType())
{
return false;
}
EntityClientCacheKey otherEntityClientCacheKey = (EntityClientCacheKey)otherObject;
return (_commandType == otherEntityClientCacheKey._commandType &&
_parameterCount == otherEntityClientCacheKey._parameterCount) &&
Equals(otherEntityClientCacheKey._eSqlStatement, _eSqlStatement) &&
Equals(otherEntityClientCacheKey._parametersToken, _parametersToken);
}
/// <summary>
/// Returns Context Hash Code
/// </summary>
/// <returns></returns>
public override int GetHashCode()
{
return _hashCode;
}
private static string GetTypeUsageToken(TypeUsage type)
{
string result = null;
// Dev10#537010: EntityCommand false positive cache hits caused by insufficient parameter type information in cache key
// Ensure String types are correctly differentiated.
if (object.ReferenceEquals(type, DbTypeMap.AnsiString))
{
result = "AnsiString";
}
else if (object.ReferenceEquals(type, DbTypeMap.AnsiStringFixedLength))
{
result = "AnsiStringFixedLength";
}
else if (object.ReferenceEquals(type, DbTypeMap.String))
{
result = "String";
}
else if (object.ReferenceEquals(type, DbTypeMap.StringFixedLength))
{
result = "StringFixedLength";
}
else if (object.ReferenceEquals(type, DbTypeMap.Xml))
{
// Xml is currently mapped to (unicode, variable-length) string, so the TypeUsage
// given to the provider is actually a String TypeUsage.
Debug.Assert(TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String), "Update GetTypeUsageToken to return 'Xml' for Xml parameters");
result = "String";
}
else if (TypeSemantics.IsEnumerationType(type))
{
result = type.EdmType.FullName;
}
else
{
// String/Xml TypeUsages are the only DbType-derived TypeUsages that carry meaningful facets.
// Otherwise, the primitive type name is a sufficient token (note that full name is not required
// since model types always have the 'Edm' namespace).
Debug.Assert(TypeSemantics.IsPrimitiveType(type), "EntityParameter TypeUsage not a primitive type?");
Debug.Assert(!TypeSemantics.IsPrimitiveType(type, PrimitiveTypeKind.String), "String TypeUsage not derived from DbType.AnsiString, AnsiString, String, StringFixedLength or Xml?");
result = type.EdmType.Name;
}
return result;
}
/// <summary>
/// Returns a string representation of the parameter list
/// </summary>
/// <param name="entityCommand"></param>
/// <returns></returns>
private static string GetParametersToken(EntityCommand entityCommand)
{
if (null == entityCommand.Parameters || 0 == entityCommand.Parameters.Count)
{
//
// means no parameters
//
return "@@0";
}
// Ensure that parameter DbTypes are valid and there are no duplicate names
Dictionary<string, TypeUsage> paramTypeUsage = entityCommand.GetParameterTypeUsage();
Debug.Assert(paramTypeUsage.Count == entityCommand.Parameters.Count, "entityParameter collection and query parameter collection must have the same number of entries");
if (1 == paramTypeUsage.Count)
{
// if its one parameter only, there is no need to use stringbuilder
return "@@1:" +
entityCommand.Parameters[0].ParameterName + ":" +
GetTypeUsageToken(paramTypeUsage[entityCommand.Parameters[0].ParameterName]);
}
else
{
StringBuilder sb = new StringBuilder(entityCommand.Parameters.Count * EstimatedParameterStringSize);
Debug.Assert(paramTypeUsage.Count == entityCommand.Parameters.Count, "entityParameter collection and query parameter collection must have the same number of entries");
sb.Append("@@");
sb.Append(entityCommand.Parameters.Count);
sb.Append(":");
string separator = "";
foreach (KeyValuePair<string, TypeUsage> param in paramTypeUsage)
{
sb.Append(separator);
sb.Append(param.Key);
sb.Append(":");
sb.Append(GetTypeUsageToken(param.Value));
separator = ";";
}
return sb.ToString();
}
}
/// <summary>
/// returns the composed cache key
/// </summary>
/// <returns></returns>
public override string ToString()
{
return String.Join("|", new string[] { Enum.GetName(typeof(CommandType), _commandType), _eSqlStatement, _parametersToken });
}
}
}
|