I was trying to accomplish the conversion dynamically across all of the models I have in my project:
DbContext.cs
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var entityTypes = modelBuilder.Model.GetEntityTypes().ToList();
foreach (var entityType in entityTypes)
{
foreach (var property in entityType.ClrType.GetProperties().Where(x => x != null && x.GetCustomAttribute<HasJsonConversionAttribute>() != null))
{
modelBuilder.Entity(entityType.ClrType)
.Property(property.PropertyType, property.Name)
.HasJsonConversion();
}
}
base.OnModelCreating(modelBuilder);
}
Then created a data annotation attribute to mark my fields in my model as "Json"
public class HasJsonConversionAttribute : Attribute {}
This is my extension method used to convert the Json to object and back to Json
public static class SqlExtensions
{
public static PropertyBuilder HasJsonConversion(this PropertyBuilder propertyBuilder)
{
ParameterExpression parameter1 = Expression.Parameter(propertyBuilder.Metadata.ClrType, "v");
MethodInfo methodInfo1 = typeof(Newtonsoft.Json.JsonConvert).GetMethod("SerializeObject", types: new Type[] { typeof(object) });
MethodCallExpression expression1 = Expression.Call(methodInfo1 ?? throw new Exception("Method not found"), parameter1);
ParameterExpression parameter2 = Expression.Parameter(typeof(string), "v");
MethodInfo methodInfo2 = typeof(Newtonsoft.Json.JsonConvert).GetMethod("DeserializeObject", 1, BindingFlags.Static | BindingFlags.Public, Type.DefaultBinder, CallingConventions.Any, types: new Type[] { typeof(string) }, null)?.MakeGenericMethod(propertyBuilder.Metadata.ClrType) ?? throw new Exception("Method not found");
MethodCallExpression expression2 = Expression.Call(methodInfo2, parameter2);
var converter = Activator.CreateInstance(typeof(ValueConverter<,>)
.MakeGenericType(propertyBuilder.Metadata.ClrType, typeof(string)), new object[]
{
Expression.Lambda( expression1,parameter1),
Expression.Lambda( expression2,parameter2),
(ConverterMappingHints) null
});
propertyBuilder.HasConversion(converter as ValueConverter);
return propertyBuilder;
}
}
For simplicity I'm using this User model:
public class User : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
// Apply some settings defined in custom annotations in the model properties
//builder.ApplyCustomAnnotationsAndConfigs(this);
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Username { get; set; }
[HasJsonConversion]
public List<LocalizedName> Values { get; set; }
}
and This is the class that I want to convert to and from JSON:
public class LocalizedName
{
public string Value { get; set; }
public string Code { get; set; }
}
Now here's the problem I'm facing, it keeps on detecting LocalizedName
object as another model
that doesn't have a Key
and throws an error telling me to add a Key/PK
even though this is not flagged as a model.
Now if I execute this from the User -> Configure()
it will work BUT it'll show me other issues like the model loses its own relationships and associations with other models and now it throws me other set of errors which I didn't even have in the first place.
I've also noticed that EF
removes LocalizedName
from the property list and shows it under Navigation
properties list. And lastly, I've checked the OnModelCreating -> modelBuilder.Model.GetEntityTypes()
and noticed that EF is treating it as a new Model
which is kinda weird.
Is there a way I can tell EF
to mark this as a string/json
field instead of EF auto-assuming it's a model?
Any help would be appreciated.