Skip to content

Commit f7df775

Browse files
Final caching update
1 parent c900ded commit f7df775

File tree

3 files changed

+87
-55
lines changed

3 files changed

+87
-55
lines changed

Magic.IndexedDb/Helpers/PropertyMappingCache.cs

Lines changed: 71 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313

1414
namespace Magic.IndexedDb.Helpers
1515
{
16+
1617
public struct SearchPropEntry
1718
{
18-
public SearchPropEntry(Dictionary<string, MagicPropertyEntry> _propertyEntries,
19-
ConstructorInfo? constructor, ParameterInfo[]? constructorParams)
19+
public SearchPropEntry(Type type, Dictionary<string, MagicPropertyEntry> _propertyEntries, ConstructorInfo[] constructors)
2020
{
2121
propertyEntries = _propertyEntries;
2222
jsNameToCsName = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -27,37 +27,62 @@ public SearchPropEntry(Dictionary<string, MagicPropertyEntry> _propertyEntries,
2727
jsNameToCsName[entry.Value.JsPropertyName] = entry.Value.CsharpPropertyName;
2828
}
2929

30-
// Store constructor parameters and their indexes
31-
if (constructor != null && constructorParams != null)
30+
// 🔥 Pick the best constructor: Prefer a parameterized one, else fallback to parameterless
31+
var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
32+
Constructor = constructor; // ✅ Assign to instance variable
33+
HasConstructorParameters = constructor != null && constructor.GetParameters().Length > 0;
34+
35+
// 🔥 Cache constructor parameter mappings
36+
if (HasConstructorParameters)
3237
{
33-
for (int i = 0; i < constructorParams.Length; i++)
38+
var parameters = constructor.GetParameters();
39+
for (int i = 0; i < parameters.Length; i++)
3440
{
35-
ConstructorParameterMappings[constructorParams[i].Name!] = i;
41+
ConstructorParameterMappings[parameters[i].Name!] = i;
3642
}
43+
}
44+
45+
// 🔥 Store constructor in a local variable before using in lambda (Fix for struct issue)
46+
var localConstructor = constructor;
3747

38-
// Compile a fast instance creator delegate if there is a parameterized constructor
39-
InstanceCreator = (args) => constructor.Invoke(args);
48+
// 🔥 Cache fast instance creator
49+
if (localConstructor != null)
50+
{
51+
InstanceCreator = (args) => localConstructor.Invoke(args);
4052
}
4153
else
4254
{
43-
// If there's no constructor, use parameterless instance creation
44-
InstanceCreator = (_) => Activator.CreateInstance(_propertyEntries.First().Value.Property.DeclaringType!);
55+
// 🚀 Use default constructor if no valid parameterized constructor is found
56+
InstanceCreator = (_) => IsInstantiable(type) ? Activator.CreateInstance(type) : throw new InvalidOperationException($"Cannot instantiate abstract/interface type {type.FullName}");
4557
}
4658
}
4759

60+
public ConstructorInfo? Constructor { get; } // ✅ Stores the most relevant constructor
61+
public bool HasConstructorParameters { get; } // ✅ Cached flag to avoid checking length
62+
public Func<object?[], object?> InstanceCreator { get; } // ✅ Cached instance creator
63+
4864
public Dictionary<string, MagicPropertyEntry> propertyEntries { get; }
4965
public Dictionary<string, string> jsNameToCsName { get; }
50-
5166
public Dictionary<string, int> ConstructorParameterMappings { get; } // ✅ Stores constructor parameter indexes
52-
public Func<object?[], object?> InstanceCreator { get; } // ✅ Cached constructor invocation
67+
68+
/// <summary>
69+
/// Determines whether a type can be instantiated.
70+
/// </summary>
71+
private static bool IsInstantiable(Type type)
72+
{
73+
return !(type.IsAbstract || type.IsInterface || type.IsGenericTypeDefinition);
74+
}
5375
}
5476

5577

5678

5779

80+
81+
82+
5883
public static class PropertyMappingCache
5984
{
60-
internal static readonly ConcurrentDictionary<string, SearchPropEntry> _propertyCache = new();
85+
internal static readonly ConcurrentDictionary<Type, SearchPropEntry> _propertyCache = new();
6186

6287

6388
public static MagicPropertyEntry GetPrimaryKeyOfType(Type type)
@@ -75,42 +100,38 @@ public static MagicPropertyEntry GetPrimaryKeyOfType(Type type)
75100
public static SearchPropEntry GetTypeOfTProperties(Type type)
76101
{
77102
EnsureTypeIsCached(type);
78-
if (_propertyCache.TryGetValue(type.FullName!, out var properties))
103+
if (_propertyCache.TryGetValue(type!, out var properties))
79104
{
80105
return properties;
81106
}
82107
throw new Exception("Something went very wrong getting GetTypeOfTProperties");
83108
}
84109

85-
private static readonly ConcurrentDictionary<Type, bool> _simpleTypeCache = new();
110+
private static readonly HashSet<Type> _simpleTypes = new()
111+
{
112+
typeof(string), typeof(decimal), typeof(DateTime), typeof(DateTimeOffset),
113+
typeof(Guid), typeof(Uri), typeof(TimeSpan)
114+
};
86115

87116
public static bool IsSimpleType(Type type)
88117
{
89-
return _simpleTypeCache.GetOrAdd(type, t =>
90-
t.IsPrimitive ||
91-
t.IsEnum ||
92-
t == typeof(string) ||
93-
t == typeof(decimal) ||
94-
t == typeof(DateTime) ||
95-
t == typeof(DateTimeOffset) ||
96-
t == typeof(Guid) ||
97-
t == typeof(Uri) ||
98-
t == typeof(TimeSpan));
118+
return type.IsPrimitive || type.IsEnum || _simpleTypes.Contains(type);
99119
}
100120

101-
/*
102-
public static bool IsSimpleType(Type type)
103-
{
104-
return type.IsPrimitive ||
105-
type.IsEnum ||
106-
type == typeof(string) ||
107-
type == typeof(decimal) ||
108-
type == typeof(DateTime) ||
109-
type == typeof(DateTimeOffset) ||
110-
type == typeof(Guid) ||
111-
type == typeof(Uri) ||
112-
type == typeof(TimeSpan);
113-
}*/
121+
122+
/*
123+
public static bool IsSimpleType(Type type)
124+
{
125+
return type.IsPrimitive ||
126+
type.IsEnum ||
127+
type == typeof(string) ||
128+
type == typeof(decimal) ||
129+
type == typeof(DateTime) ||
130+
type == typeof(DateTimeOffset) ||
131+
type == typeof(Guid) ||
132+
type == typeof(Uri) ||
133+
type == typeof(TimeSpan);
134+
}*/
114135

115136

116137
public static IEnumerable<Type> GetAllNestedComplexTypes(IEnumerable<PropertyInfo> properties)
@@ -147,16 +168,16 @@ public static IEnumerable<Type> GetAllNestedComplexTypes(IEnumerable<PropertyInf
147168
return complexTypes;
148169
}
149170

150-
/*public static bool IsComplexType(Type type)
171+
public static bool IsComplexType(Type type)
151172
{
152173
return !(IsSimpleType(type)
153174
|| type == typeof(string)
154175
|| typeof(IEnumerable).IsAssignableFrom(type) // Non-generic IEnumerable
155176
|| (type.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(type.GetGenericTypeDefinition())) // Generic IEnumerable<T>
156177
|| type.IsArray); // Arrays are collections too
157-
}*/
178+
}
158179

159-
private static readonly ConcurrentDictionary<Type, bool> _complexTypeCache = new();
180+
/*private static readonly ConcurrentDictionary<Type, bool> _complexTypeCache = new();
160181
161182
public static bool IsComplexType(Type type)
162183
{
@@ -180,7 +201,7 @@ public static bool IsComplexType(Type type)
180201
181202
return true;
182203
});
183-
}
204+
}*/
184205

185206

186207
/*public static bool IsComplexType(Type type)
@@ -231,7 +252,7 @@ public static string GetCsharpPropertyName(string jsPropertyName, Type type)
231252

232253
try
233254
{
234-
if (_propertyCache.TryGetValue(typeKey, out var search))
255+
if (_propertyCache.TryGetValue(type, out var search))
235256
{
236257
return search.GetCsharpPropertyName(jsPropertyName);
237258
}
@@ -291,7 +312,7 @@ public static string GetJsPropertyName(string csharpPropertyName, Type type)
291312

292313
try
293314
{
294-
if (_propertyCache.TryGetValue(typeKey, out var properties) &&
315+
if (_propertyCache.TryGetValue(type, out var properties) &&
295316
properties.propertyEntries.TryGetValue(csharpPropertyName, out var entry))
296317
{
297318
return entry.JsPropertyName;
@@ -323,7 +344,7 @@ public static MagicPropertyEntry GetPropertyEntry(string propertyName, Type type
323344

324345
try
325346
{
326-
if (_propertyCache.TryGetValue(typeKey, out var properties) &&
347+
if (_propertyCache.TryGetValue(type, out var properties) &&
327348
properties.propertyEntries.TryGetValue(propertyName, out var entry))
328349
{
329350
return entry;
@@ -364,10 +385,10 @@ internal static void EnsureTypeIsCached<T>()
364385

365386
internal static void EnsureTypeIsCached(Type type)
366387
{
367-
string typeKey = type.FullName!;
388+
//string typeKey = type.FullName!;
368389

369390
// Avoid re-registering types if the typeKey already exists
370-
if (_propertyCache.ContainsKey(typeKey))
391+
if (_propertyCache.ContainsKey(type))
371392
return;
372393

373394
// Ensure schema metadata is cached
@@ -405,12 +426,11 @@ internal static void EnsureTypeIsCached(Type type)
405426
}
406427

407428
// 🔥 Extract constructor metadata
408-
var constructor = type.GetConstructors().FirstOrDefault();
409-
var constructorParams = constructor?.GetParameters();
429+
var constructors = type.GetConstructors();
410430

411431
// Cache the properties for this type
412-
_propertyCache[typeKey] = new SearchPropEntry(propertyEntries,
413-
constructor, constructorParams ?? Array.Empty<ParameterInfo>());
432+
_propertyCache[type] = new SearchPropEntry(type, propertyEntries,
433+
constructors);
414434

415435
var complexTypes = GetAllNestedComplexTypes(newMagicPropertyEntry.Select(x => x.Property));
416436
if (complexTypes != null && complexTypes.Any())

Magic.IndexedDb/Models/MagicContractResolver.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,11 @@ private bool IsSimpleJsonNull(JsonElement element)
159159
throw new JsonException("Unexpected end of JSON while reading an object.");
160160
}
161161

162-
private object? GetDefaultValue(Type type)
162+
private static readonly ConcurrentDictionary<Type, object?> _defaultValues = new();
163+
164+
public static object? GetDefaultValue(Type type)
163165
{
164-
return type.IsValueType ? Activator.CreateInstance(type) : null;
166+
return _defaultValues.GetOrAdd(type, t => t.IsValueType ? Activator.CreateInstance(t) : null);
165167
}
166168

167169

@@ -182,7 +184,7 @@ private bool IsSimpleJsonNull(JsonElement element)
182184
return ReadIEnumerable(ref reader, propertyType, options);
183185
}
184186

185-
if (PropertyMappingCache.IsComplexType(propertyType))
187+
if (mpe.IsComplexType)
186188
{
187189
return ReadComplexObject(ref reader, propertyType, options);
188190
}

Magic.IndexedDb/Models/Structs/MagicPropertyEntry.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Magic.IndexedDb.Helpers;
22
using Magic.IndexedDb.Interfaces;
33
using System;
4+
using System.Collections;
45
using System.Collections.Generic;
56
using System.Diagnostics.Metrics;
67
using System.Linq;
@@ -26,7 +27,7 @@ public MagicPropertyEntry(PropertyInfo property, IColumnNamed? columnNamedAttrib
2627
PrimaryKey = primaryKey;
2728
NotMapped = notMapped;
2829

29-
IsComplexType = PropertyMappingCache.IsComplexType(property.PropertyType);
30+
IsComplexType = IsComplexTypeFunc(property.PropertyType);
3031

3132
// 🔥 Identify if this property is a constructor parameter
3233
var declaringType = property.DeclaringType!;
@@ -55,6 +56,15 @@ public MagicPropertyEntry(PropertyInfo property, IColumnNamed? columnNamedAttrib
5556
}
5657
}
5758

59+
private bool IsComplexTypeFunc(Type type)
60+
{
61+
return !(PropertyMappingCache.IsSimpleType(type)
62+
|| type == typeof(string)
63+
|| typeof(IEnumerable).IsAssignableFrom(type) // Non-generic IEnumerable
64+
|| (type.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(type.GetGenericTypeDefinition())) // Generic IEnumerable<T>
65+
|| type.IsArray); // Arrays are collections too
66+
}
67+
5868
public object? DefaultValue { get; }
5969

6070
/// <summary>

0 commit comments

Comments
 (0)