Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Magic.IndexedDb/Exceptions/MagicConstructorException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Magic.IndexedDb.Exceptions;

public class MagicConstructorException(string message) : Exception(message);
14 changes: 10 additions & 4 deletions Magic.IndexedDb/Helpers/PropertyMappingCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Concurrent;
using System.Reflection;
using System.Text.Json.Serialization;
using Magic.IndexedDb.Exceptions;

namespace Magic.IndexedDb.Helpers;

Expand Down Expand Up @@ -37,12 +38,17 @@ public SearchPropEntry(Type type, Dictionary<string, MagicPropertyEntry> _proper
EnforcePascalCase = false;
}

// 🔥 Pick the best constructor: Prefer JsonConstructor, then fall back to a parameterized one, else fallback to parameterless
var jsonConstructor = constructors.FirstOrDefault(c => c.GetCustomAttribute<JsonConstructorAttribute>() != null);
if (jsonConstructor == null)
// 🔥 Pick the best constructor: Prefer MagicConstructor, then fall back to a parameterized one, else fallback to parameterless
try
{
Constructor = constructors.SingleOrDefault(c => c.GetCustomAttribute<MagicConstructorAttribute>() is not null);
}
catch (InvalidOperationException)
{
Constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
throw new MagicConstructorException("Only one magic constructor is allowed");
}
Constructor ??= constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();

HasConstructorParameters = Constructor != null && Constructor.GetParameters().Length > 0;

// 🔥 Cache constructor parameter mappings
Expand Down
31 changes: 24 additions & 7 deletions Magic.IndexedDb/Models/MagicContractResolver.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization;
using Magic.IndexedDb.Helpers;
Expand Down Expand Up @@ -62,25 +63,41 @@ internal class MagicContractResolver<T> : JsonConverter<T>

private object CreateObjectFromDictionary(Type type, Dictionary<string, object?> propertyValues, SearchPropEntry search)
{
// 🚀 If there's a constructor with parameters, use it
if (search.ConstructorParameterMappings.Count > 0)
object? obj = null;
List<string> unInitializedProperties = new List<string>();
// If the constructor set in the SearchPropEntry contains parameters, fill them
if (search.HasConstructorParameters)
{
var constructorArgs = new object?[search.ConstructorParameterMappings.Count];
foreach (var (paramName, index) in search.ConstructorParameterMappings)
{
if (propertyValues.TryGetValue(paramName, out var value))
{
constructorArgs[index] = value;
propertyValues.Remove(paramName);
}
else
constructorArgs[index] = GetDefaultValue(type.GetProperty(paramName)?.PropertyType ?? typeof(object));
{
constructorArgs[index] =
GetDefaultValue(type.GetProperty(paramName)?.PropertyType ?? typeof(object));
}
}

return search.InstanceCreator(constructorArgs) ?? throw new InvalidOperationException($"Failed to create instance of type {type.Name}.");
obj = search.InstanceCreator(constructorArgs);
}
else
{
// 🚀 Use parameterless constructor
obj = search.InstanceCreator([]);
}

if (obj is null)
{
throw new InvalidOperationException($"Failed to create instance of type {type.Name}.");
}

// 🚀 Use parameterless constructor
var obj = search.InstanceCreator(Array.Empty<object?>()) ?? throw new InvalidOperationException($"Failed to create instance of type {type.Name}.");

// 🚀 Assign property values
// 🚀 Assign property values (to properties not passed to constructor)
foreach (var (propName, value) in propertyValues)
{
if (search.propertyEntries.TryGetValue(propName, out var propEntry))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Magic.IndexedDb.SchemaAnnotations;

/// <summary>
/// Sets the preferred constructor for serialization for MagicDB
/// </summary>
public class MagicConstructorAttribute : Attribute;
5 changes: 3 additions & 2 deletions TestBase/Models/Person.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ public class Nested

public class Person : MagicTableTool<Person>, IMagicTable<DbSets>
{
[JsonConstructor]

[MagicConstructor]
public Person()
{
DoNotMapTest2 = "Test";
}

public Person(int _Id)
{
DoNotMapTest2 = _Id.ToString();
this._Id = _Id;
}

public List<IMagicCompoundIndex> GetCompoundIndexes() =>
Expand Down