X Tutup
Skip to content

Union Type and EF Core Inheritance #456

@Zerdayne

Description

@Zerdayne

Hey.

for my understanding the union type and the entity type hierarchy mapping from EF seems like a perfect match for each other.
But due to the limitation of needing an empty abstract class or interface to declare a union type or probably some bugs happening, it seems like this combination of features does not work currently.

Let's look at the following entity structure (simplified):

public abstract class BaseItem {
    public int Id { get; set; }
    public string Name { get; set; }
    public double Weight { get; set; }
}

public class General : BaseItem {
    public string Note { get; set; } 
}

public class Container : BaseItem {
    public double WeightLimit { get; set; }
}

public class Weapon : BaseItem {
    public int Damage { get; set; }
}

public class Armor : BaseItem {
    public string Class { get; set; }
}

And the corresponding DbContext:

public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : DbContext(options)
{
    public DbSet<BaseItem> Items => Set<BaseItem>();
    public DbSet<General> GeneralItems => Set<General>();
    public DbSet<Container> Containers => Set<Container>();
    public DbSet<Weapon> Weapons => Set<Weapon>();
    public DbSet<Armor> Armors => Set<Armor>();
}

Following the needed definitions for the schema (for this example we disregard the limitation of an empty abstract class needed for defining a union):

schema.AddUnion<BaseItem>("ItemUnion", "Get items");
schema.Type<BaseItem>().AddAllPossibleTypes();

We should be able to perform the following query:

query {
    items {
        ... on General {
            id
            name
            weight
            note
             __typename
        }
        ... on Container {
            id
            name
            weight
            weightLimit
             __typename
        }
        ... on Weapon {
            id
            name
            weight
            damage
             __typename
        }
        ... on Armor {
            id
            name
            weight
            class
             __typename
        }
    }
}

And receive the following example json as return:

{
    "data": {
        "items": [
            {
                "id": 1,
                "name": "Fork",
                "weight": 1,
                "type": "General",
                "note": "Normally used to eat",
                "__typename": "General"
            },
            {
                "id": 2,
                "name": "Chest",
                "weight": 5,
                "type": "Container",
                "weightLimit": 25,
                "__typename": "Container"
            },
            {
                "id": 3,
                "name": "Iron Sword",
                "weight": 1,
                "type": "Weapon",
                "damage": 3,
                "__typename": "Weapon"
            },
            {
                "id": 4,
                "name": "Iron Chestplate",
                "weight": 1,
                "type": "Armor",
                "class": "Heavy",
                "__typename": "Armor"
            }
        ]
    }
}

If the limitation of an empty abstract class for a union type could be disregarded, this would seem like a good use.
But due to the limitation we cannot do it. To get a working solution for EF and the union type, we can declare a new interface called IItem and let it be inherited by the entities. So the new class heads would look like that:

public interface IItem { }

public class General : BaseItem, IItem
public class Container : BaseItem IItem
public class Weapon : BaseItem, IItem
public class Armor : BaseItem, IItem

The updated schema definition would then look like this:

schema.AddUnion<IItem>("ItemUnion", "Get items");
schema.Type<IItem>().AddAllPossibleTypes();

A working entity for EF needs at least a key property, e.g. Id. So we can not use AItem to reference it as a DbSet or in a relation. An interface is disqualified for EF due to the same reason.

The items query has obviously BaseItem as type as the schema generation does not now the intended usage. Changing the return type by utilizing the Returns method does change the typing in the schema as needed. But obviously the data resolving is now not correct. Although we can overwrite the resolving by utilizing the Resolve method, I did not find a way to utilize it so return data is correct.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      X Tutup