Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for modelling Discriminated Unions #66

Open
simon-reynolds opened this issue Mar 28, 2021 · 2 comments
Open

Support for modelling Discriminated Unions #66

simon-reynolds opened this issue Mar 28, 2021 · 2 comments
Assignees

Comments

@simon-reynolds
Copy link
Collaborator

The F# Discriminated Union (DU) is a sum type where a type can contain a value of (e.g.) type A or B, but not both, for instance

type UserType =
| User
| Admin

The constituent types can be empty like above, or can have associated values, including plain types, complex types like records, or even other union types

type Person = {Id: int; First:string; Last:string}  // define a record type
type IntOrBool = I of int | B of bool

type MixedType =
  | Tup of int * int  // a tuple
  | P of Person       // use the record type defined above
  | L of int list     // a list of ints
  | U of IntOrBool    // use the union type defined above

EFCore.FSharp needs to be able to handle these types and

  • Generate valid migrations that can encode such a union
  • Be able to correctly identify cases where a database structure can be effectively modelled as a DU (Add support for many types per table relationships #21 for example)
  • Existing tables with discriminator types should be mapped as a discriminated union.

Migrations

Each DU should be mapped to a table with a discriminator column and each associated type modelled aa linked entity in its own right. This approach will result in separate tables in the example above that model each field type

CREATE TABLE PersonId
(
    Id INT,
    First NVARCHAR,
    Last NVARCHAR
)

CREATE TABLE IntOrBool
(
    Id INT,
    Discriminator
    Int1 INT,
    Bool1 BIT
)

CREATE TABLE IntOrBool_Int
(
    Id INT,
    Int1 INT
)

CREATE TABLE IntOrBool_Bool
(
    Id INT,
    Bool1 BIT
)

CREATE TABLE MixedType
(
    Id INT,
    Discriminator NVARCHAR
)

CREATE TABLE MixedType_Tup
(
    Id INT PRIMARY KEY,
    MixedTypeId INT FOREIGN KEY, -- UNIQUE
    Int1 INT,
    Int2 INT
)

CREATE TABLE MixedType_P
(
    Id INT PRIMARY KEY,
    MixedTypeId INT FOREIGN KEY,
    PersonId INT FOREIGN KEY
)

CREATE TABLE MixedType_L
(
    Id INT PRIMARY KEY,
    MixedTypeId INT FOREIGN KEY, -- Duplicates allowed as it models a list
    Int1 INT
)

CREATE TABLE MixedType_U
(
    Id INT PRIMARY KEY,
    MixedTypeId INT FOREIGN KEY,
    IntOrBool INT FOREIGN KEY
)

This results in a lot of tables being generated but is I believe the most flexible system allowing for further expansion of the domain in future, for instance, if a new property was added to Person, or a new entry included in MixedType.Tup
It also allows the union type to be extended easily, just adding a new table instead of adding additional columns to existing tables

Scaffolding

We will need to be able to support scaffolding code from existing tables that would also include types such multiple types per table

Example from #21:

Where a many-types-per-table relationship from a Table Person with Discrimator values of Employee and Manager would be modelled in C# as

public abstract class Person
{
    public int Id {get; set;}
    public string First {get; set;}
    public string Last {get; set;}
}

public class Employee : Person
{
    public string JobTitle {get; set;}
}

public class Manager : Person
{
    public string Department {get; set;}
}

we should model it as

type Employee = { Id:int; First:string; Last:string; JobTitle:string }
type Manager = { Id:int; First:string; Last:string; Department:string }

type Person =
| Employee of Employee
| Manager of Manager

A potential issue here will be mapping columns to multiple types

@sonicbhoc
Copy link

This would be the coolest improvement to EF in general.

@jkone27
Copy link

jkone27 commented Mar 21, 2023

this would be really really lovely as F# models occupy much less space than C# models in code, they really fit well fro domain modeling!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

3 participants