Chris Stillwell
Chris Stillwell

Reputation: 10547

Mapping To a Single Lookup Table For Multiple Tables in EF Core

I'm attempting to map to a lookup table that contains values for multiple tables. I cannot modify the database. The table has a one-to-many relationship with multiple tables, but no foreign key constraints. The lookup table has three columns code, category and description. A sample set of the data would look like this:

Lookup table:

code category description
1 Role Admin
2 Role User
1 Job Mechanic
2 Job Plumber
3 Job Electrician
1 Activity Work
2 Activity Overtime
3 Activity Training

I'm interested in joining the Activity table that looks like this (some columns omitted for clarity):

Activity table:

id code hours
1 1 8.0
2 1 8.0
3 2 1.0
4 2 5.3

I want to join the Activity table to the Lookup table using EF Core. If I was writing a SQL statement I'd just have something like this:

SELECT *
FROM Activity 
JOIN Lookup ON Lookup.code = Activity.code
            AND Lookup.category = 'Activity'

So in EF Core I created my classes to represent my tables:

public class Lookup 
{
    [Key]
    public string Code { get; set; }
    public string Category { get; set; }
    public string Description { get; set; }
}

public class Activity 
{
    [Key]
    public string Id { get; set; }
    public string Code { get; set; }
    public double Hours { get; set; }

    [NotMapped]
    public string LookupCategory { get; set; } = "Activity";
    public Lookup ActivityType { get; set; }
}

I also have the following fluent relationship setup:

modelBuilder.Entity<Activity>()
    .HasOne<Lookup>(x => x.ActivityType)
    .WithMany()
    .HasForeignKey(x => x.Code);

But this doesn't work since there are multiple rows with the same code value. How do I get the key constraint to also take in to account the LookupCategory value since it is not mapped?

Upvotes: 2

Views: 2648

Answers (2)

Chris Stillwell
Chris Stillwell

Reputation: 10547

Following pjs's suggestions I was able to implement the following successfully:

I created a new class that extended Lookup. This class has the attribute NotMapped, which was an important part in getting everything to finally work.

[NotMapped]
public class ActivityType : StaffLookup{ }

In Activity I modified the class to use ActivityType instead of StaffLookup. I also removed the LookupCategory property

public class Activity 
{
    [Key]
    public string Id { get; set; }
    public string Code { get; set; }
    public double Hours { get; set; }

    // This is mapped to our subclass so that we can get the filtered values
    public ActivityType ActivityType { get; set; }
}

In my OnModelCreating I removed the existing fluent relationship and added the following new ones:

// This tells EF what column is used as a the filter
modelBuilder.Entity<StaffLookup>()
    .HasDiscriminator(x => x.Category);

// This is what filters the Lookup table for us
modelBuilder.Entity<ActivityType>()
    .HasDiscriminator()
    .HasValue("Activity");

// Since the foreign key isn't mapped in the database 
// we need to add this relationship
modelBuilder.Entity<ActivityMain>()
    .HasOne(x => x.ActivityType)
    .WithMany()
    .HasForeignKey(x => x.ActivityCode);

Upvotes: 3

pjs
pjs

Reputation: 383

Look at setting up a Table-Per-Hierarchy, TPH, with Category as the discriminator. Then your EF model can have Lookup as the base table, with RoleType, ActivityType, etc. as child entities in your EF model. The relationship would be from ActivityType to Activity, and EF would already know that the only applicable values in ActivityType are the Lookup rows with Category = 'Activity'. You would want to verify all existing data meets those constraints since they aren't enforced in the database.

Upvotes: 2

Related Questions