diff --git a/src/Nemo/DataReaderExtensions.cs b/src/Nemo/DataReaderExtensions.cs index 4556536..2c1ea73 100644 --- a/src/Nemo/DataReaderExtensions.cs +++ b/src/Nemo/DataReaderExtensions.cs @@ -8,8 +8,12 @@ internal static class DataReaderExtensions { internal static ISet GetColumns(this IDataRecord record) { - var columns = new HashSet(StringComparer.OrdinalIgnoreCase); int count = record.FieldCount; +#if NETSTANDARD2_0 || NET472 + var columns = new HashSet(StringComparer.OrdinalIgnoreCase); +#else + var columns = new HashSet(count, StringComparer.OrdinalIgnoreCase); +#endif for (var i = 0; i < count; i++) { columns.Add(record.GetName(i)); diff --git a/src/Nemo/ObjectFactory.Retrieve.cs b/src/Nemo/ObjectFactory.Retrieve.cs index ff51c24..66ab433 100644 --- a/src/Nemo/ObjectFactory.Retrieve.cs +++ b/src/Nemo/ObjectFactory.Retrieve.cs @@ -58,9 +58,7 @@ private static IEnumerable RetrieveImplemenation(string operat { config ??= ConfigurationFactory.Get(); - queryKey = GetQueryKey(operation, parameters ?? new Param[] { }, returnType); - - Log.CaptureBegin($"Retrieving from L1 cache: {queryKey}", config); + queryKey = GetQueryKey(operation, parameters ?? Array.Empty(), returnType); if (returnType == OperationReturnType.MultiResult) { @@ -74,8 +72,6 @@ private static IEnumerable RetrieveImplemenation(string operat if (result != null) { - Log.Capture($"Found in L1 cache: {queryKey}", config); - if (returnType == OperationReturnType.MultiResult) { ((IMultiResult)result).Reset(); @@ -84,8 +80,6 @@ private static IEnumerable RetrieveImplemenation(string operat Log.CaptureEnd(config); return result; } - Log.Capture($"Not found in L1 cache: {queryKey}", config); - Log.CaptureEnd(config); } result = RetrieveItems(operation, parameters, operationType, returnType, connectionName, connection, types, map, schema, config, identityMap); @@ -153,12 +147,25 @@ private static IEnumerable RetrieveItems(string operation, IList pa return result; } - private static string GetQueryKey(string operation, IEnumerable parameters, OperationReturnType returnType) + private static string GetQueryKey(string operation, IList parameters, OperationReturnType returnType) { - var combined = new StringBuilder(); - combined.Append(returnType.ToString()).Append("/").Append(operation).Append("/").Append(parameters.OrderBy(p => p.Name).Select(p => $"{p.Name}={p.Value}").ToDelimitedString(",")); - var hash = Hash.Compute(Encoding.UTF8.GetBytes(combined.ToString())); - return typeof(T).FullName + "/" + hash; + var sb = new StringBuilder(); + sb.Append(returnType.ToString()).Append("/").Append(operation); + if (parameters.Count > 0) + { + sb.Append("/"); + var sorted = parameters.Count > 1; + IEnumerable paramSource = sorted ? parameters.OrderBy(p => p.Name) : parameters; + var first = true; + foreach (var p in paramSource) + { + if (!first) sb.Append(","); + sb.Append(p.Name).Append("=").Append(p.Value); + first = false; + } + } + var hash = Hash.Compute(Encoding.UTF8.GetBytes(sb.ToString())); + return string.Concat(typeof(T).FullName, "/", hash.ToString()); } /// @@ -344,9 +351,7 @@ private static async Task> RetrieveImplemenationAsync(); - queryKey = GetQueryKey(operation, parameters ?? new Param[] { }, returnType); - - Log.CaptureBegin($"Retrieving from L1 cache: {queryKey}", config); + queryKey = GetQueryKey(operation, parameters ?? Array.Empty(), returnType); if (returnType == OperationReturnType.MultiResult) { @@ -360,8 +365,6 @@ private static async Task> RetrieveImplemenationAsync> RetrieveImplemenationAsync ConvertDataReader(IDataReader reader, Func 1 && useMapper) + { + secondaryMappers = new Mapper.PropertyMapper[typeCount]; + for (var i = 1; i < typeCount; i++) + { + secondaryMappers[i] = Mapper.CreateDelegate(typeof(IDataRecord), types[i], true, autoTypeCoercion); + } + } while (reader.Read()) { if (isSimpleType) @@ -1085,20 +1105,23 @@ private static IEnumerable ConvertDataReader(IDataReader reader, Func(isInterface); var record = (IDataRecord)new WrappedRecord(reader, columns); - Map(record, item, config.AutoTypeCoercion); + if (autoTypeCoercion) + FastIndexerMapperWithTypeCoercion.Map(record, item); + else + FastIndexerMapper.Map(record, item); TrySetObjectState(item); - if (map != null) + if (hasMap) { - var args = new object[types.Count]; args[0] = item; - for (var i = 1; i < types.Count; i++) + for (var i = 1; i < typeCount; i++) { var identity = CreateIdentity(types[i], reader); if (!references.TryGetValue(identity, out var reference)) { - reference = Map((object)record, types[i], config.AutoTypeCoercion); + reference = Create(types[i]); + secondaryMappers[i](record, reference); TrySetObjectState(reference); references.Add(identity, reference); } @@ -1126,11 +1149,10 @@ private static IEnumerable ConvertDataReader(IDataReader reader, Func ConvertDataReader(IDataReader reader, Func CreateDynamicItem(IDataReader reader, return bag; } + private static readonly ConcurrentDictionary PrimaryKeyColumnCache = new ConcurrentDictionary(); + + private static string[] GetPrimaryKeyColumnNames(Type objectType) + { + return PrimaryKeyColumnCache.GetOrAdd(objectType, type => + { + var nameMap = Reflector.GetPropertyNameMap(type); + return nameMap.Values.Where(p => p.IsPrimaryKey) + .Select(p => p.MappedColumnName ?? p.PropertyName) + .OrderBy(_ => _) + .ToArray(); + }); + } + private static Tuple CreateIdentity(Type objectType, IDataRecord record) { - var nameMap = Reflector.GetPropertyNameMap(objectType); - var identity = Tuple.Create(objectType, string.Join(",", nameMap.Values.Where(p => p.IsPrimaryKey) - .Select(p => p.MappedColumnName ?? p.PropertyName) - .OrderBy(_ => _) - .Select(n => Convert.ToString(record.GetValue(record.GetOrdinal(n)))))); - return identity; + var pkColumns = GetPrimaryKeyColumnNames(objectType); + var sb = new StringBuilder(); + for (var i = 0; i < pkColumns.Length; i++) + { + if (i > 0) sb.Append(','); + sb.Append(Convert.ToString(record.GetValue(record.GetOrdinal(pkColumns[i])))); + } + return Tuple.Create(objectType, sb.ToString()); } private static IEnumerable ConvertDataReaderMultiResult(IDataReader reader, IList types, INemoConfiguration config) @@ -1205,7 +1243,13 @@ void changeSkipNext() skipNext = true; } - var reflectedTypes = types.Select(t => Reflector.GetReflectedType(t)).ToList(); + var reflectedTypes = new ReflectedType[types.Count]; + for (var i = 0; i < types.Count; i++) + { + reflectedTypes[i] = Reflector.GetReflectedType(types[i]); + } + + var autoTypeCoercion = config.AutoTypeCoercion; try { @@ -1217,30 +1261,37 @@ void changeSkipNext() var isSimpleType = reflectedTypes[resultIndex].IsSimpleType; var useMapper = !isInterface || config.DefaultMaterializationMode == MaterializationMode.Exact; var columns = !isSimpleType ? reader.GetColumns() : null; + var currentType = types[resultIndex]; + Mapper.PropertyMapper resultMapper = null; + if (useMapper && !isSimpleType && !isAnonymous) + { + resultMapper = Mapper.CreateDelegate(typeof(IDataRecord), currentType, true, autoTypeCoercion); + } while (reader.Read()) { if (isSimpleType) { var item = reader.GetValue(0); - yield return new MultiResultItem { Item = Reflector.ChangeType(item, types[resultIndex]), ItemType = types[resultIndex], ItemTypeIndex = resultIndex, SkipNextCallback = changeSkipNext }; + yield return new MultiResultItem { Item = Reflector.ChangeType(item, currentType), ItemType = currentType, ItemTypeIndex = resultIndex, SkipNextCallback = changeSkipNext }; } else if (isAnonymous) { var item = CreateDynamicItem(reader, columns, true); - yield return new MultiResultItem { Item = item, ItemType = types[resultIndex], ItemTypeIndex = resultIndex, SkipNextCallback = changeSkipNext }; + yield return new MultiResultItem { Item = item, ItemType = currentType, ItemTypeIndex = resultIndex, SkipNextCallback = changeSkipNext }; } else if (useMapper) { - var item = Map((object)new WrappedReader(reader, columns), types[resultIndex], config.AutoTypeCoercion); - TrySetObjectState(item); - yield return new MultiResultItem { Item = item, ItemType = types[resultIndex], ItemTypeIndex = resultIndex, SkipNextCallback = changeSkipNext }; + var target = Create(currentType); + resultMapper(new WrappedReader(reader, columns), target); + TrySetObjectState(target); + yield return new MultiResultItem { Item = target, ItemType = currentType, ItemTypeIndex = resultIndex, SkipNextCallback = changeSkipNext }; } else { var bag = CreateDynamicItem(reader, columns, false); - var item = Wrap(bag, types[resultIndex]); + var item = Wrap(bag, currentType); TrySetObjectState(item); - yield return new MultiResultItem { Item = item, ItemType = types[resultIndex], ItemTypeIndex = resultIndex, SkipNextCallback = changeSkipNext }; + yield return new MultiResultItem { Item = item, ItemType = currentType, ItemTypeIndex = resultIndex, SkipNextCallback = changeSkipNext }; } if (skipNext) diff --git a/src/Nemo/Utilities/Hash.cs b/src/Nemo/Utilities/Hash.cs index 0fd37f5..b489feb 100644 --- a/src/Nemo/Utilities/Hash.cs +++ b/src/Nemo/Utilities/Hash.cs @@ -2,70 +2,65 @@ { public class Hash { - private static uint _a; - private static uint _b; - private static uint _c; - - private static void Mix() + private static void Mix(ref uint a, ref uint b, ref uint c) { - _a -= _b; _a -= _c; _a ^= (_c >> 13); - _b -= _c; _b -= _a; _b ^= (_a << 8); - _c -= _a; _c -= _b; _c ^= (_b >> 13); - _a -= _b; _a -= _c; _a ^= (_c >> 12); - _b -= _c; _b -= _a; _b ^= (_a << 16); - _c -= _a; _c -= _b; _c ^= (_b >> 5); - _a -= _b; _a -= _c; _a ^= (_c >> 3); - _b -= _c; _b -= _a; _b ^= (_a << 10); - _c -= _a; _c -= _b; _c ^= (_b >> 15); + a -= b; a -= c; a ^= (c >> 13); + b -= c; b -= a; b ^= (a << 8); + c -= a; c -= b; c ^= (b >> 13); + a -= b; a -= c; a ^= (c >> 12); + b -= c; b -= a; b ^= (a << 16); + c -= a; c -= b; c ^= (b >> 5); + a -= b; a -= c; a ^= (c >> 3); + b -= c; b -= a; b ^= (a << 10); + c -= a; c -= b; c ^= (b >> 15); } public static uint Compute(byte[] data) { var len = data.Length; - _a = _b = 0x9e3779b9; - _c = 0; + uint a = 0x9e3779b9, b = 0x9e3779b9, c = 0; var i = 0; while (i + 12 <= len) { - _a += data[i++] | + a += data[i++] | ((uint)data[i++] << 8) | ((uint)data[i++] << 16) | ((uint)data[i++] << 24); - _b += data[i++] | + b += data[i++] | ((uint)data[i++] << 8) | ((uint)data[i++] << 16) | ((uint)data[i++] << 24); - _c += data[i++] | + c += data[i++] | ((uint)data[i++] << 8) | ((uint)data[i++] << 16) | ((uint)data[i++] << 24); - Mix(); + Mix(ref a, ref b, ref c); } - _c += (uint)len; + c += (uint)len; if (i < len) - _a += data[i++]; + a += data[i++]; if (i < len) - _a += (uint)data[i++] << 8; + a += (uint)data[i++] << 8; if (i < len) - _a += (uint)data[i++] << 16; + a += (uint)data[i++] << 16; if (i < len) - _a += (uint)data[i++] << 24; + a += (uint)data[i++] << 24; if (i < len) - _b += data[i++]; + b += data[i++]; if (i < len) - _b += (uint)data[i++] << 8; + b += (uint)data[i++] << 8; if (i < len) - _b += (uint)data[i++] << 16; + b += (uint)data[i++] << 16; if (i < len) - _b += (uint)data[i++] << 24; + b += (uint)data[i++] << 24; if (i < len) - _c += (uint)data[i++] << 8; + c += (uint)data[i++] << 8; if (i < len) - _c += (uint)data[i++] << 16; + c += (uint)data[i++] << 16; if (i < len) - _c += (uint)data[i++] << 24; - Mix(); - return _c; + c += (uint)data[i++] << 24; + Mix(ref a, ref b, ref c); + return c; } } }