I am trying to map my Name
column to a dynamic object. This is how the raw JSON data looks (note that this is SQL-morphed from our old relational data and I am not able to generate or interact with this column via EF Core):
{ "en": "Water", "fa": "آب", "ja": "水", ... }
Just to note, available languages are stored in a separate table and thus are dynamically defined.
Through T-SQL I can perfectly interact with these objects eg
SELECT *
FROM [MyObjects]
WHERE JSON_VALUE(Name, '$.' + @languageCode) = @searchQuery
But it seems EF Core doesn't want to even deserialize these objects as whole, let alone query them.
What I get in a simple GetAll
query is an empty Name
. Other columns are not affected though.
I have tried so far
[JsonExtensionData]
dictionary inside: DynamicObject
inheritance and implementing GetDynamicMembers
, TryGetMember
, TrySetMember
, TryCreateInstance
All yield the same results: an empty Name
.
I have other options like going back to a junction table relational which I have many issues with, hardcoding languages which is not really intuitive and might cause problems in the future, using HasJsonConversion
which basically destroys the performance on any search action... so I'm basically stuck here with this.
I think currently it's not fully supported:
I suggest this workaround while the EF team improves the functionality.
Create a static class with static methods to be used as decoys in the expression tree. This will be mapped to the server built-in functions.
public static class DBF { public static string JsonValue(this string column, [NotParameterized] string path) => throw new NotSupportedException(); public static string JsonQuery(this string column, [NotParameterized] string path) => throw new NotSupportedException(); }
Include the database functions on your OnModelCreating method.
protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.HasDbFunction( typeof(DBF).GetMethod(nameof(DBF.JsonValue))! ).HasName("JSON_VALUE").IsBuiltIn(); modelBuilder.HasDbFunction( typeof(DBF).GetMethod(nameof(DBF.JsonQuery))! ).HasName("JSON_QUERY").IsBuiltIn(); /// ... modelBuilder.Entity(entity => { //treat entity as text entity.Property(x => x.Metadata) .HasColumnType("varchar") .HasMaxLength(8000); }); }
Call them dynamically with LINQ.
var a = await _context.FileInformation .AsNoTracking() .Where(x => x.Metadata!.JsonValue("$.Property1") == "some value") .Select(x => x.Metadata!.JsonValue("$.Property2")) .ToListAsync();
You can add casts or even build anonymous types with this method.
My solution was I added a new class which has KEY
and VALUE
, which will represent the dictionary i needed :
public class DictionaryObject
{
public string Key { set; get; }
public string Value { set; get; }
}
and instead of having this line in the JSON class :
public Dictionary<string, string> Name { get; set; }
I changed to :
public List<DictionaryObject> Name { get; set; }
Hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With