ValueConverters in Entity Framework Core

By Mirek on (tags: ef core, Entity Framework, ORM, ValueConverters, categories: code)

Today I want to show an example of how great are value converters in EF Core.

Basically a value converter informs the EF engine how to store and materialize value type that it does not know by default. It means you can have a property of any types you want as long as you tell EF how to convert to and from your type to any type it already knows. That gives a huge possibilities and makes things more transparent compared to how it was in EF 6 or older. More on that on Microsoft documentation here, where you can also find the list of predefined built-in converters. Now without any further ado let’s jump into the code.

Lets say we have a simple entity class TranslatedText that looks like this:

public class TranslatedText
{
public int Id { get; set; }
public string Value { get; set; }
public CultureInfo Language { get; set; } }

Note that the Language property is of type CultureInfo which is not known to EF as starndard value type. We want to store this property as string and save only leftLanguageTag. This is sufficient to uniquelly define the language and dialect of the translated text.

With use of ValueConverters its as easy as providing the EF with two extra methods. One to convert from CultureInfo to string and second to convert from string to CultureInfo. All this can be done by FluentAPI in OnModelCreating method.

public class AppDbContext : DbContext
{
public DbSet<TranslatedText> Texts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=(local);Initial Catalog=test;Integrated Security=True");
optionsBuilder.LogTo(m => Debug.WriteLine(m), LogLevel.Information);
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TranslatedText>()
.Property(x => x.Language)
.HasConversion<string>(
x => x.IetfLanguageTag,
x => CultureInfo.GetCultureInfo(x));
} }

Having that in place lets create and seed some data.

var enLang = CultureInfo.GetCultureInfo("en-US");
var deLang = CultureInfo.GetCultureInfo("de-DE");
var plLang = CultureInfo.GetCultureInfo("pl-PL");

using (var db = new AppDbContext())
{
db.Database.EnsureCreated();
db.Texts.Add(new TranslatedText { Language = enLang, Value = "This is EN text" });
db.Texts.Add(new TranslatedText { Language = deLang, Value = "This is DE text" });
db.Texts.Add(new TranslatedText { Language = plLang, Value = "This is PL text" });
db.SaveChanges();
}

EF Core will create a database exactly how we expect it, with the nvarchar column for Language property

2020-12-23 18_54_51-SQLQuery7.sql - (local).test (MIREK-PC_miedz (54)) - Microsoft SQL Server Manage

Now we can easilly create, query and also filter the TranslatedText entities by the Language property.
For example quering texts by CultureInfo language is as simple as:

var enLang = CultureInfo.GetCultureInfo("en-US");
var enText = db.Texts.Where(x => x.Language == enLang).ToList();

and the SQL query generated by EF is

SELECT [t].[Id], [t].[Language], [t].[Value]
FROM [Texts] AS [t]
WHERE [t].[Language] = @__enLang_0
ORDER BY [t].[Language]

That’s it. Cheers