{"id":22065599,"url":"https://github.com/karenpayneoregon/ef-core-transforming","last_synced_at":"2026-04-30T02:31:20.025Z","repository":{"id":110840525,"uuid":"497435301","full_name":"karenpayneoregon/ef-core-transforming","owner":"karenpayneoregon","description":"Provides easy to follow transformations/conversions for EF Core","archived":false,"fork":false,"pushed_at":"2025-01-15T12:29:54.000Z","size":572,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-30T19:02:51.178Z","etag":null,"topics":["csharp","csharp-core","dateonly","efcore","efcore5","efcore6","hasconversion","linq","orm","sql","sqlserver"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karenpayneoregon.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-28T21:55:37.000Z","updated_at":"2025-01-15T12:29:55.000Z","dependencies_parsed_at":"2024-10-25T17:33:50.806Z","dependency_job_id":"07b48937-a578-446d-b9a3-f885c41e68d8","html_url":"https://github.com/karenpayneoregon/ef-core-transforming","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/karenpayneoregon/ef-core-transforming","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fef-core-transforming","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fef-core-transforming/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fef-core-transforming/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fef-core-transforming/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karenpayneoregon","download_url":"https://codeload.github.com/karenpayneoregon/ef-core-transforming/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karenpayneoregon%2Fef-core-transforming/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32452230,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-29T22:27:22.272Z","status":"online","status_checked_at":"2026-04-30T02:00:05.929Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","csharp-core","dateonly","efcore","efcore5","efcore6","hasconversion","linq","orm","sql","sqlserver"],"created_at":"2024-11-30T19:20:13.945Z","updated_at":"2026-04-30T02:31:20.007Z","avatar_url":"https://github.com/karenpayneoregon.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# EF Core Value Conversions\n\n:heavy_check_mark: December 2022 moved all projects to .NET Core 5, EF Core 7\n\n## Introduction\n\nEF Core provides methods to transform one type into another. Most common use is with enumerations while many other transformations are possible. In this article/code sample there are two samples for enumerations and one for string array.\n\n[Bulk-configuring a value converter](https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions?tabs=data-annotations#bulk-configuring-a-value-converter) is possible as shown below.\n\n[Pre-defined conversions](https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions?tabs=data-annotations#pre-defined-conversions)\nEF Core contains many pre-defined conversions that avoid the need to write conversion functions manually. Instead, EF Core will pick the conversion to use based on the property type in the model and the requested database provider type.\n\nThe code samples below allow a developer to get started by following along with provided code.\n\nCode by itself is not enough, take time to read [Microsoft documentation](https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions?tabs=data-annotations).\n\nCheck the following [page](https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.storage.valueconversion?view=efcore-5.0) for builtin converters.\n\n:small_orange_diamond: Although there are many articles out there on transformations, this has ready to run examples.\n\n:small_orange_diamond: Although I coded this repository, inspiration can from various incomplete post on the web.\n\n\n\n```csharp\npublic class CurrencyConverter : ValueConverter\u003cCurrency, decimal\u003e\n{\n    public CurrencyConverter()\n        : base(\n            v =\u003e v.Amount,\n            v =\u003e new Currency(v))\n    {\n    }\n}\n```\n\n# Enumerations\n\nIn this example we want to categorize wines. The model has a primary key, string which represents the wine name and a enum for category,\n\n\n```csharp\npublic class Wine\n{\n    public int WineId { get; set; }\n    public string Name { get; set; }\n    public WineVariantId WineVariantId { get; set; }\n    public WineVariant WineVariant { get; set; }\n    public override string ToString() =\u003e Name;\n}\n```\n\n**Enum**\n\n```csharp\npublic enum WineVariantId : int\n{\n    Red = 0,\n    White = 1,\n    Rose = 2\n}\n```\n\nThe following provides a `one to many relationship`\n\n```csharp\npublic class WineVariant\n{\n    public WineVariantId WineVariantId { get; set; }\n    public string Name { get; set; }\n    public List\u003cWine\u003e Wines { get; set; }\n    public override string ToString() =\u003e Name;\n}\n```\n\nUsing `HasConversion` in the `DbContext`\n\n```csharp\nprotected override void OnModelCreating(ModelBuilder modelBuilder)\n{\n    modelBuilder\n        .Entity\u003cWine\u003e()\n        .Property(e =\u003e e.WineVariantId)\n        .HasConversion\u003cint\u003e();\n\n    modelBuilder\n        .Entity\u003cWineVariant\u003e()\n        .Property(e =\u003e e.WineVariantId)\n        .HasConversion\u003cint\u003e();\n\n    modelBuilder\n        .Entity\u003cWineVariant\u003e().HasData(\n            Enum.GetValues(typeof(WineVariantId))\n                .Cast\u003cWineVariantId\u003e()\n                .Select(e =\u003e new WineVariant()\n                {\n                    WineVariantId = e,\n                    Name = e.ToString()\n                })\n        );\n}\n```\n\n**Let's query for all wines**\n\n```csharp\nusing System.Linq;\nusing HasConversion.Data;\nusing HasConversion.Models;\nusing Microsoft.EntityFrameworkCore;\nusing Spectre.Console;\nusing static HasConversion.Models.WineVariantId;\n\nnamespace HasConversion.Classes\n{\n    public class WineOperations\n    {\n        /// \u003csummary\u003e\n        /// If database does not exists than pass true to this method\n        /// to create and populate with several records.\n        ///\n        /// Otherwise passing false to view records in database\n        /// \u003c/summary\u003e\n        /// \u003cparam name=\"reCreate\"\u003e\u003c/param\u003e\n        public static void AddViewWines(bool reCreate = false)\n        {\n            using var context = new WineContext();\n            var allWines = context.Wines.Include(item =\u003e item.WineVariant).ToList();\n        }\n    }\n}\n```\n\n**Results**\n\n![All Wines](EntityFrameworkCoreHasConversion/assets/AllWines.png)\n\n**Display**\n\n![Wines](EntityFrameworkCoreHasConversion/assets/Wines.png)\n\nTo be consistent let's do the same pattern with books and category for books.\n\n```csharp\npublic partial class Book\n{\n    public int BookId { get; set; }\n    public string Title { get; set; }\n    public BookCategory BookCategory { get; set; }\n    public override string ToString() =\u003e Title;\n}\n\npublic class BookVariant\n{\n    [Key]\n    public BookCategory BookCategoryId { get; set; }\n    public string Name { get; set; }\n    public List\u003cBook\u003e Books { get; set; }\n    public override string ToString() =\u003e Name;\n}\n```\n\nSetup the conversion, in this case we are not setting up for one to many but by following the pattern above we can.\n\n```csharp\nprotected override void OnModelCreating(ModelBuilder modelBuilder)\n{\n    modelBuilder.HasAnnotation(\"Relational:Collation\", \"SQL_Latin1_General_CP1_CI_AS\");\n\n\n    modelBuilder\n        .Entity\u003cBook\u003e()\n        .Property(e =\u003e e.BookCategory)\n        .HasConversion\u003cint\u003e();\n\n    modelBuilder\n        .Entity\u003cBookVariant\u003e().HasData(\n            Enum.GetValues(typeof(BookCategory))\n                .Cast\u003cBookCategory\u003e()\n                .Select(e =\u003e new BookVariant()\n                {\n                    BookCategoryId = e,\n                    Name = e.ToString()\n                })\n        );\n}\n```\n\n**Get all books**\n\n```csharp\nvar bookList = context.Book.ToList();\n```\n\nGet by specific category `Adventure`\n\n```csharp\nvar list = bookList.Where(books =\u003e books.BookCategory == BookCategory.Adventure).ToList();\n```\n\n![Books](EntityFrameworkCoreHasConversion/assets/Books.png)\n\n\n# Enum tip\n\nWhen working with enum, consider placing the enum values in a database table and use a T4 template to generate the model. This will be helpful when there are many projects using the same enum and saves time if a member name changes, members are deleted or added.\n\n:small_orange_diamond: There are several [examples included](https://github.com/karenpayneoregon/ef-core-transforming/tree/master/EntityFrameworkCoreHasConversion/Templates).\n\n\n## String to array\n\nThis one although provides a decent sample for using HasConversion the data model can be improved. This comes from the following [forum question](https://docs.microsoft.com/en-us/answers/questions/866164/parse-json-data-for-insert-into-sql-table.html).\n\n\u003e I know I can create classes from the JSON data, and parse out the elements. Butttt how do I then insert into MS SQL Server tables?\n\nTheir code\n\n```csharp\npublic class Account\n {\n     public string Email { get; set; }\n     public bool Active { get; set; }\n     public DateTime CreatedDate { get; set; }\n     public IList\u003cstring\u003e Roles { get; set; }\n }\n    \n    \n string json = @\"{\n   'Email': 'james@example.com',\n   'Active': true,\n   'CreatedDate': '2013-01-20T00:00:00Z',\n   'Roles': [\n     'User',\n     'Admin'\n   ]\n }\";\n    \n Account account = JsonConvert.DeserializeObject\u003cAccount\u003e(json);\n    \n Console.WriteLine(account.Email);\n```\n\nOne forum member recommended to string string concatenation which is okay but EF Core can handle this.\n\nMy recommendation\n\nModel which changes the Roles property to a string array.\n\n```csharp\npublic partial class Account\n{\n    public int Id { get; set; }\n    public string UserName { get; set; }\n    public string Email { get; set; }\n    public bool? Active { get; set; }\n    public DateTime? CreatedDate { get; set; }\n    public string[] Roles { get; set; }\n}\n```\n\n**Conversion**\n\n```csharp\nprotected override void OnModelCreating(ModelBuilder modelBuilder)\n{\n    modelBuilder.HasAnnotation(\"Relational:Collation\", \"SQL_Latin1_General_CP1_CI_AS\");\n\n    modelBuilder.Entity\u003cAccount\u003e()\n        .Property(e =\u003e e.Roles)\n        .HasConversion(\n            value =\u003e string.Join(',', value),\n            value =\u003e value.Split(',',\n                StringSplitOptions.RemoveEmptyEntries));\n\n    OnModelCreatingPartial(modelBuilder);\n}\n```\n![Has Conversion](EntityFrameworkCoreHasConversion/assets/HasConversion.png)\n\n\n\n**View records**\n\n```csharp\npublic static void ViewAccounts()\n{\n    using var context = new AccountContext();\n    var accountList = context.Account.ToList();\n\n    var table = CreateViewTable();\n\n    foreach (var account in accountList)\n    {\n\n        if (account.Id.IsEven())\n        {\n            table.AddRow($\"[bold yellow on green]{account.Id}[/]\", $\"[bold yellow on green]{account.UserName}[/]\");\n        }\n        else\n        {\n            table.AddRow($\"{account.Id}\", account.UserName);\n        }\n\n        foreach (var role in account.Roles)\n        {\n            table.AddRow(\"\", role.PadLeft(10));\n        }\n\n        table.AddEmptyRow();\n\n    }\n\n    AnsiConsole.Write(table);\n\n    var admins = accountList.Where(account =\u003e account.Roles.Contains(\"Admin\")).ToList();\n\n\n}\n```\n\n## Storing List\n\nIn this case a List\u0026lt;int\u003e\n\n**Model**\n\n```csharp\npublic class EntityType\n{\n    public int Id { get; set; }\n    public List\u003cint\u003e ListProperty { get; set; }\n}\n```\n\n**Conversion**\n\n```csharp\nprotected override void OnModelCreating(ModelBuilder modelBuilder)\n{\n    \n    modelBuilder\n        .Entity\u003cEntityType\u003e()\n        .Property(e =\u003e e.ListProperty)\n        .HasConversion(\n            v =\u003e JsonSerializer.Serialize(v, null),\n            v =\u003e JsonSerializer.Deserialize\u003cList\u003cint\u003e\u003e(v, null),\n            new ValueComparer\u003cList\u003cint\u003e\u003e(\n                (list1, list2) =\u003e list1.SequenceEqual(list2),\n                c =\u003e c.Aggregate(0, (a, v) =\u003e HashCode.Combine(a, v.GetHashCode())),\n                c =\u003e c.ToList()));\n    \n}\n```\n\n**Add record**\n\n```csharp\nvar entity = new EntityType { ListProperty = new List\u003cint\u003e { 1, 2, 3 } };\ncontext.Add(entity);\ncontext.SaveChanges();\n```\n\n**Result**\n\n![image](EntityFrameworkCoreHasConversion/assets/listQuery.png)\n\n\n## BoolToStringConverter\n\nThis converter transforms a bool to string where the value in the database table will be a string then when read back as a bool.\n\n![Bool To String](EntityFrameworkCoreHasConversion/assets/BoolToString.png)\n\n```csharp\npublic class Person\n{\n    [Key]\n    public int Id { get; set; }\n    public string FirstName { get; set; }\n    public string LastName { get; set; }\n    public bool IsFriend { get; set; }\n    public override string ToString() =\u003e $\"{FirstName} {LastName}\";\n}\n```\n\n\n\n## Json example\n\nTaken from a Stackoverflow [post](https://stackoverflow.com/a/44832143/5509738).\n\n\u003e Is there a way to indicate that this is not a relationship but should be stored as a big string?\n\n```csharp\npublic class Campaign\n{\n    private string _extendedData;\n\n    [Key]\n    public Guid Id { get; set; }\n\n    [Required]\n    [MaxLength(50)]\n    public string Name { get; set; }\n\n    [NotMapped]\n    public JObject ExtendedData\n    {\n        get\n        {\n            return JsonConvert.DeserializeObject\u003cJObject\u003e(string.IsNullOrEmpty(_extendedData) ? \"{}\" : _extendedData);\n        }\n        set\n        {\n            _extendedData = value.ToString();\n        }\n    }\n}\n```\n\nIn the DbContext\n\n```csharp\nprotected override void OnModelCreating(ModelBuilder modelBuilder)\n{\n    modelBuilder.Entity\u003cCampaign\u003e()\n        .Property\u003cstring\u003e(\"ExtendedDataStr\")\n        .HasField(\"_extendedData\");\n}\n```\n\n\u003c/br\u003e\n\n# Summary\n\nIn this article there are enough code to run and study to learn the basics of transforming properties in models using Entity Framework Core. Couple this with Microsoft docs and the links below a developer can easily perform conversions.\n\n:small_orange_diamond: Some coders may look for places to use what has been presented while the reverse should be the path, tuck these away and when a situation arises you have a solution.\n\n# Improvements to HasConversion API\n\n[For EF Core 6](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-6.0/whatsnew#improvements-to-hasconversion-api) \n\nBefore EF Core 6.0, the generic overloads of the HasConversion methods used the generic parameter to specify the type to convert to.\n\n# See also\n\n- [khalidabuhakmeh](https://twitter.com/buhakmeh) [Entity Framework Core 5 Value Converters](https://khalidabuhakmeh.com/entity-framework-core-5-value-converters)\n- [Entity Framework Core – Improved Value Conversion Support](https://www.thinktecture.com/en/entity-framework-core/improved-value-conversion-support-in-2-1/)\n- [EF Core 7 code sample](https://github.com/karenpayneoregon/ef-core-7-samples) for [Json Columns](http://example.com)\n\n# NuGet packages\n\n[Spectre.Console](https://spectreconsole.net/) provided classes to provide clear ways to present data. [Package](https://www.nuget.org/packages/Spectre.Console/0.44.1-preview.0.17).\n\nhttps://marcominerva.wordpress.com/2022/01/07/dateonly-and-timeonly-support-with-entity-framework-core-6-0/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenpayneoregon%2Fef-core-transforming","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarenpayneoregon%2Fef-core-transforming","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarenpayneoregon%2Fef-core-transforming/lists"}