Skip to content

Commit 176443a

Browse files
Caching fix
1 parent 541420c commit 176443a

File tree

2 files changed

+134
-69
lines changed

2 files changed

+134
-69
lines changed

Magic.IndexedDb/Helpers/PropertyMappingCache.cs

Lines changed: 97 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -13,38 +13,39 @@ namespace Magic.IndexedDb.Helpers
1313
{
1414
public static class PropertyMappingCache
1515
{
16-
internal static readonly ConcurrentDictionary<Type, Dictionary<PropertyInfo, MagicPropertyEntry>> _propertyCache = new();
17-
16+
internal static readonly ConcurrentDictionary<string, Dictionary<string, MagicPropertyEntry>> _propertyCache = new();
17+
1818
/// <summary>
1919
/// Gets the C# property name given a JavaScript property name.
2020
/// </summary>
2121
public static string GetCsharpPropertyName<T>(string jsPropertyName)
2222
{
23-
EnsureTypeIsCached<T>();
24-
25-
var properties = _propertyCache[typeof(T)];
26-
return properties.FirstOrDefault(kvp => kvp.Value.JsPropertyName == jsPropertyName).Value.CsharpPropertyName ?? jsPropertyName;
27-
}
28-
29-
/// <summary>
30-
/// Gets the C# property name given a PropertyInfo reference.
31-
/// </summary>
32-
public static string GetCsharpPropertyName<T>(PropertyInfo property)
33-
{
34-
return GetCsharpPropertyName<T>(property.Name);
23+
return GetCsharpPropertyName(jsPropertyName, typeof(T));
3524
}
3625

3726
/// <summary>
38-
/// Gets the JavaScript property name (ColumnName) given a C# property name.
27+
/// Gets the C# property name given a JavaScript property name.
3928
/// </summary>
40-
public static string GetJsPropertyName(string csharpPropertyName, Type type)
29+
public static string GetCsharpPropertyName(string jsPropertyName, Type type)
4130
{
4231
EnsureTypeIsCached(type);
32+
string typeKey = type.FullName!;
4333

44-
var properties = _propertyCache[type];
45-
return properties.FirstOrDefault(kvp => kvp.Key.Name == csharpPropertyName).Value.JsPropertyName ?? csharpPropertyName;
46-
}
34+
try
35+
{
36+
if (_propertyCache.TryGetValue(typeKey, out var properties) &&
37+
properties.TryGetValue(jsPropertyName, out var entry))
38+
{
39+
return entry.CsharpPropertyName;
40+
}
41+
}
42+
catch (Exception ex)
43+
{
44+
throw new Exception($"Error retrieving C# property name for JS property '{jsPropertyName}' in type {type.FullName}.", ex);
45+
}
4746

47+
return jsPropertyName; // Fallback to original name if not found
48+
}
4849

4950
/// <summary>
5051
/// Gets the JavaScript property name (ColumnName) given a C# property name.
@@ -54,20 +55,38 @@ public static string GetJsPropertyName<T>(string csharpPropertyName)
5455
return GetJsPropertyName(csharpPropertyName, typeof(T));
5556
}
5657

57-
/// <summary>
58-
/// Gets the JavaScript property name (ColumnName) given a PropertyInfo reference.
59-
/// </summary>
60-
public static string GetJsPropertyName(PropertyInfo property, Type type)
58+
public static string GetJsPropertyName<T>(PropertyInfo prop)
6159
{
62-
return GetJsPropertyName(property.Name, type);
60+
return GetJsPropertyName(prop.Name, typeof(T));
61+
}
62+
63+
public static string GetJsPropertyName(PropertyInfo prop, Type type)
64+
{
65+
return GetJsPropertyName(prop.Name, type);
6366
}
6467

6568
/// <summary>
66-
/// Gets the JavaScript property name (ColumnName) given a PropertyInfo reference.
69+
/// Gets the JavaScript property name (ColumnName) given a C# property name.
6770
/// </summary>
68-
public static string GetJsPropertyName<T>(PropertyInfo property)
71+
public static string GetJsPropertyName(string csharpPropertyName, Type type)
6972
{
70-
return GetJsPropertyName<T>(property.Name);
73+
EnsureTypeIsCached(type);
74+
string typeKey = type.FullName!;
75+
76+
try
77+
{
78+
if (_propertyCache.TryGetValue(typeKey, out var properties) &&
79+
properties.TryGetValue(csharpPropertyName, out var entry))
80+
{
81+
return entry.JsPropertyName;
82+
}
83+
}
84+
catch (Exception ex)
85+
{
86+
throw new Exception($"Error retrieving JS property name for C# property '{csharpPropertyName}' in type {type.FullName}.", ex);
87+
}
88+
89+
return csharpPropertyName; // Fallback to original name if not found
7190
}
7291

7392
/// <summary>
@@ -84,17 +103,30 @@ public static MagicPropertyEntry GetPropertyEntry<T>(string propertyName)
84103
public static MagicPropertyEntry GetPropertyEntry(string propertyName, Type type)
85104
{
86105
EnsureTypeIsCached(type);
106+
string typeKey = type.FullName!;
107+
108+
try
109+
{
110+
if (_propertyCache.TryGetValue(typeKey, out var properties) &&
111+
properties.TryGetValue(propertyName, out var entry))
112+
{
113+
return entry;
114+
}
115+
}
116+
catch (Exception ex)
117+
{
118+
throw new Exception($"Error retrieving property entry for '{propertyName}' in type {type.FullName}.", ex);
119+
}
87120

88-
var properties = _propertyCache[type];
89-
return properties.FirstOrDefault(kvp => kvp.Key.Name == propertyName).Value;
121+
throw new Exception($"Error: Property '{propertyName}' not found in type {type.FullName}.");
90122
}
91123

92124
/// <summary>
93125
/// Gets the cached MagicPropertyEntry for a given PropertyInfo reference.
94126
/// </summary>
95127
public static MagicPropertyEntry GetPropertyEntry<T>(PropertyInfo property)
96128
{
97-
return GetPropertyEntry<T>(property.Name);
129+
return GetPropertyEntry(property.Name, typeof(T));
98130
}
99131

100132
/// <summary>
@@ -105,6 +137,7 @@ public static MagicPropertyEntry GetPropertyEntry(PropertyInfo property, Type ty
105137
return GetPropertyEntry(property.Name, type);
106138
}
107139

140+
108141
/// <summary>
109142
/// Ensures that both schema and property caches are built for the given type.
110143
/// </summary>
@@ -115,40 +148,44 @@ internal static void EnsureTypeIsCached<T>()
115148

116149
internal static void EnsureTypeIsCached(Type type)
117150
{
118-
// Ensures both caches are built in a single operation
119-
if (!_propertyCache.ContainsKey(type) || !SchemaHelper._schemaCache.ContainsKey(type))
151+
string typeKey = type.FullName!;
152+
153+
// Avoid re-registering types if the typeKey already exists
154+
if (_propertyCache.ContainsKey(typeKey))
155+
return;
156+
157+
// Ensure schema metadata is cached
158+
SchemaHelper.EnsureSchemaIsCached(type);
159+
160+
// Initialize the dictionary for this type
161+
var propertyEntries = new Dictionary<string, MagicPropertyEntry>();
162+
163+
foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
120164
{
121-
SchemaHelper._schemaCache.GetOrAdd(type, t => t.GetCustomAttribute<MagicTableAttribute>());
165+
string propertyKey = property.Name; // Now stored as string, not PropertyInfo
122166

123-
_propertyCache.GetOrAdd(type, t =>
167+
var columnAttribute = property.GetCustomAttributes()
168+
.FirstOrDefault(attr => attr is IColumnNamed) as IColumnNamed;
169+
170+
if (columnAttribute != null && string.IsNullOrWhiteSpace(columnAttribute.ColumnName))
124171
{
125-
var propertyEntries = new Dictionary<PropertyInfo, MagicPropertyEntry>();
126-
127-
foreach (var property in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
128-
{
129-
var columnAttribute = property.GetCustomAttributes()
130-
.FirstOrDefault(attr => attr is IColumnNamed) as IColumnNamed;
131-
132-
if (columnAttribute != null && string.IsNullOrWhiteSpace(columnAttribute.ColumnName))
133-
{
134-
columnAttribute = null;
135-
}
136-
137-
var magicEntry = new MagicPropertyEntry(
138-
property,
139-
columnAttribute,
140-
property.IsDefined(typeof(MagicIndexAttribute), inherit: true),
141-
property.IsDefined(typeof(MagicUniqueIndexAttribute), inherit: true),
142-
property.IsDefined(typeof(MagicPrimaryKeyAttribute), inherit: true),
143-
property.IsDefined(typeof(MagicNotMappedAttribute), inherit: true)
144-
);
145-
146-
propertyEntries[property] = magicEntry;
147-
}
148-
149-
return propertyEntries;
150-
});
172+
columnAttribute = null;
173+
}
174+
175+
var magicEntry = new MagicPropertyEntry(
176+
property,
177+
columnAttribute,
178+
property.IsDefined(typeof(MagicIndexAttribute), inherit: true),
179+
property.IsDefined(typeof(MagicUniqueIndexAttribute), inherit: true),
180+
property.IsDefined(typeof(MagicPrimaryKeyAttribute), inherit: true),
181+
property.IsDefined(typeof(MagicNotMappedAttribute), inherit: true)
182+
);
183+
184+
propertyEntries[propertyKey] = magicEntry; // Store property entry with string key
151185
}
186+
187+
// Cache the properties for this type
188+
_propertyCache[typeKey] = propertyEntries;
152189
}
153190
}
154191
}

Magic.IndexedDb/Helpers/SchemaHelper.cs

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,51 @@
33
using System.Collections.Concurrent;
44
using System.Collections.Generic;
55
using System.Linq;
6-
using System.Linq.Expressions;
76
using System.Reflection;
8-
using System.Text;
97
using System.Threading.Tasks;
108

119
namespace Magic.IndexedDb.Helpers
1210
{
1311
public static class SchemaHelper
1412
{
15-
internal static readonly ConcurrentDictionary<Type, MagicTableAttribute?> _schemaCache = new();
13+
internal static readonly ConcurrentDictionary<string, MagicTableAttribute?> _schemaCache = new();
1614
private static readonly ConcurrentDictionary<string, List<StoreSchema>> _databaseSchemasCache = new();
1715
private static bool _schemasScanned = false;
1816
private static readonly object _lock = new();
1917

18+
/// <summary>
19+
/// Determines whether the given type is a complex entity registered in the schema cache.
20+
/// </summary>
21+
public static bool IsMagicEntity<T>()
22+
{
23+
return _schemaCache.ContainsKey(typeof(T).FullName!);
24+
}
25+
26+
public static bool IsMagicEntity(Type type)
27+
{
28+
return _schemaCache.ContainsKey(type.FullName!);
29+
}
30+
31+
/// <summary>
32+
/// Ensures the schema information for a given type is cached.
33+
/// </summary>
34+
internal static void EnsureSchemaIsCached(Type type)
35+
{
36+
string typeKey = type.FullName!;
37+
38+
_schemaCache.GetOrAdd(typeKey, _ => type.GetCustomAttribute<MagicTableAttribute>());
39+
}
40+
2041
/// <summary>
2142
/// Gets the schema name (table name) for the given type <typeparamref name="T"/>.
2243
/// Ensures all properties and schema information are cached together.
2344
/// </summary>
2445
public static string GetSchemaName<T>() where T : class
2546
{
2647
PropertyMappingCache.EnsureTypeIsCached<T>();
48+
EnsureSchemaIsCached(typeof(T));
2749

28-
if (!_schemaCache.TryGetValue(typeof(T), out var schemaAttribute) || schemaAttribute == null)
50+
if (!_schemaCache.TryGetValue(typeof(T).FullName!, out var schemaAttribute) || schemaAttribute == null)
2951
throw new InvalidOperationException($"Type {typeof(T).Name} does not have a [MagicTable] attribute.");
3052

3153
return schemaAttribute.SchemaName;
@@ -51,9 +73,13 @@ public static List<StoreSchema> GetAllSchemas(string databaseName = null)
5173
{
5274
if (!type.IsClass || type.IsAbstract) continue;
5375

54-
// Ensure type is cached and check if it has a MagicTableAttribute
76+
// 🚀 **Only process classes that actually have the [MagicTable] attribute**
77+
var schemaAttribute = type.GetCustomAttribute<MagicTableAttribute>();
78+
if (schemaAttribute == null) continue;
79+
80+
// 🚀 Now that we confirmed it's a schema, ensure it's cached
5581
PropertyMappingCache.EnsureTypeIsCached(type);
56-
if (!_schemaCache.TryGetValue(type, out var schemaAttribute) || schemaAttribute == null) continue;
82+
EnsureSchemaIsCached(type);
5783

5884
// Determine if the schema belongs to the target database
5985
string dbName = !string.IsNullOrWhiteSpace(databaseName) ? databaseName : "DefaultedNone";
@@ -73,15 +99,17 @@ public static List<StoreSchema> GetAllSchemas(string databaseName = null)
7399
}
74100
}
75101

102+
76103
/// <summary>
77104
/// Retrieves the store schema from a given type.
78105
/// Now fully optimized by leveraging cached attributes and property mappings.
79106
/// </summary>
80107
public static StoreSchema GetStoreSchema(Type type)
81108
{
82109
PropertyMappingCache.EnsureTypeIsCached(type);
110+
EnsureSchemaIsCached(type);
83111

84-
if (!_schemaCache.TryGetValue(type, out var schemaAttribute) || schemaAttribute == null)
112+
if (!_schemaCache.TryGetValue(type.FullName!, out var schemaAttribute) || schemaAttribute == null)
85113
throw new InvalidOperationException($"Type {type.Name} does not have a [MagicTable] attribute.");
86114

87115
var schema = new StoreSchema
@@ -91,8 +119,8 @@ public static StoreSchema GetStoreSchema(Type type)
91119
};
92120

93121
// Get the primary key property
94-
var primaryKeyProperty = type.GetProperties().FirstOrDefault(prop =>
95-
PropertyMappingCache.GetPropertyEntry(prop, type).PrimaryKey);
122+
var primaryKeyProperty = type.GetProperties()
123+
.FirstOrDefault(prop => PropertyMappingCache.GetPropertyEntry(prop, type).PrimaryKey);
96124

97125
if (primaryKeyProperty == null)
98126
throw new InvalidOperationException($"The entity {type.Name} does not have a primary key attribute.");

0 commit comments

Comments
 (0)