0

I have an issue I don't know the best solution for. Hopefully someone here can help =)

What I'm trying to solve: We have to type of users in a system, person and organization. I want to have a shared login table for the two (ie the user probably won't know which type of user they are, they just relate to username and password). So I've created a login table for usernames and passwords. But I need to know who the login is connected to, so I need a reference to either person or organization.

Consider the following classes (simplified):

public class Login
{
    public string Username {get; set;}
    public string Password {get;set;}
}

public class LoginPerson : Login
{
    public Person Person {get;set;}
}

public class LoginOrg : Login
{
    public Organization Organization {get;set;}
}

public class Person
{
    public LoginPerson LoginPerson {get;set;}
    //Lots of other properties. Removed for simplicity
}

public class Organization
{
    public LoginOrg LoginOrg {get;set;}
    //Lots of other properties. Removed for simplicity
}

The person configuration is set up as follows:

public class PersonConfiguration : EntityTypeConfiguration<Person>
    {
        public PersonConfiguration()
        {
            HasRequired(p => p.LoginPerson).WithRequiredPrincipal(p => p.Person);
        }
    }

First of all, this doesn't work. I get an exception saying "System.Data.EntityCommandCompilationException: System.Data.EntityCommandCompilationException: An error occurred while preparing the command definition. See the inner exception for details. ---> System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.." So my first question is why doesn't this work? My second question is: which strategy is best suited for this kind of inheritance? TPT, TPH or TPC?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Zaphod
  • 1,412
  • 2
  • 13
  • 27

2 Answers2

1

Well, for starters, none of your entities have keys. You need a primary key to make them work. EF uses a convention to do this, which is the class name plus Id at the end, like PersonId, or you key be explicit with an attribute of [Key]

Second, your model is confusing and fairly circular. And without primary keys, there's no way to to create associations.

I'm confused about why you have a member that is a LoginPerson in a Person object, and the same for an Organization? In any event, you really need to rethink this model and figure out what your keys are.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Sorry about not including the primary keys. I removed them when I simplified the classes. All classes has an Id column which is a PK. The circular reference is intentional, as they are navigational properties. However, I solved the exception issue. And my second question can be answered by google =) – Zaphod Aug 30 '13 at 06:45
0

The solution to my exception was to set up the correct configuration ;-) PersonConfiguration didn't need to include any configuration for the LoginPerson property. I added a LoginPersonConfiguration ->

public class LoginPersonConfiguration : EntityTypeConfiguration<LoginPerson>
{
    public LoginPersonConfiguration()
    {
        ToTable("LoginPerson");
        HasKey(l => l.Id);
        HasRequired(l => l.Person).WithOptional(p => p.LoginPerson).Map(t => t.MapKey("PersonId")); 
    }
}

And I also had to add the Login to the DbContext class

public class MyDbContext : DbContext
{
    public DbSet<Person> Persons { get; set; }
    public DbSet<Login> Logins { get; set; }
}

When it comes to which strategy is best, I have decided to go for TPT.

Zaphod
  • 1,412
  • 2
  • 13
  • 27