Does EF core always require a primary key? - entity-framework

I defined a class (as the model for EF), but when I ran migrations to update the DB schema I got this error:
The entity type 'xyz' requires a primary key to be defined.
Sure, it's easy enought fix to add:
public int Id { get; set; }
From a domain model point of view I do not need a primary key on this entity. It is a dependent entity with one-many relationship and doesn't need to be unique.
So, this is more of a curiousity (than a serious problem) but is there a way not to define a primary key?

Related

Entity Framework 6 Database-First and Foriegn Key Naming Conventions

We've started using EF6 as part of rewriting our application suite. There are many perfectly reasonable tables in the existing suite and we're reusing them using a database-first approach. My problem is that EF6 seems to be enforcing what I think are code-first conventions on my database-first model.
Consider this minimal example with two tables defined thusly and appropriately populated with a few rows:
CREATE TABLE [dbo].[Table1] (
[Id] INT NOT NULL PRIMARY KEY,
[Table2Reference] INT NOT NULL REFERENCES [dbo].[Table2](Id) )
CREATE TABLE [dbo].[Table2] (
[Id] INT NOT NULL PRIMARY KEY,
[SomeColumn] NVARCHAR(25) )
After running Update Model From Database we get this model:
(Oops. Not enough reputation to post images. It's what you would imagine.)
So far so good, but when you write code to access the Table1 entity, like so...
var q = _context.Table1.ToList();
foreach (var item in q)
Debug.WriteLine("{0}", item.Table2Reference);
... it compiles fine but will throw on the ToList() line. This is because the SQL generated contains a request for a column that doesn't even exist:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Table2Reference] AS [Table2Reference],
[Extent1].[Table2_Id] AS [Table2_Id] <-- this one doesn't exist
FROM [dbo].[Table1] AS [Extent1]
I gather this has something to do with a code-first naming convention for foreign keys. I know I can rename Table2's Id column to Table2Id and rename Table2Reference to Table2Id and it will work. However, this is supposed to be database-first. Is there some way to tell EF to get out of the way and just go with what is actually in the pre-defined database? I did discover early on that I had to turn off the name pluralizing convention, but I can't seem to identify a convention to turn off that fixes this problem. I tried removing these:
modelBuilder.Conventions.Remove<PrimaryKeyNameForeignKeyDiscoveryConvention>();
modelBuilder.Conventions.Remove<TypeNameForeignKeyDiscoveryConvention>();
modelBuilder.Conventions.Remove<NavigationPropertyNameForeignKeyDiscoveryConvention>();
Anyway, I'm stumped. Is there an easy workaround that doesn't involve modifying the existing database?
Thanks for reading.
You can use data annotations attributes or fluent API to configure EF mapping to actual database tables. Here is how it can be done with attributes:
[Table("Table1")]
public class Table1
{
public int Id { get; set; }
public int Table2Reference { get; set; }
[ForeignKey("Table2Reference")]
public Table2 Table2 { get; set; }
}
It turns out that there is a very important piece to a database-first approach besides having an EDMX file. That is, your connection string must contain the following section:
metadata=res:///IPE.csdl|res:///IPE.ssdl|res://*/IPE.msl; (replacing IPE with the base name of your EDMX)
Otherwise, EF will be unable to locate the EDMX information in the assembly and code-first conventions can come into play. Mostly things just work, until they don't.

How to tell EntityFramework 5.0 that there's a one-to-one association between two entities?

I could go into the EDMX in design view and update the cardinality (1-, 0-, etc.) of associations, but I want to know if it is alright to do that in this scenario.
I have these two tables, let's say:
User
-----
Id
UserProfile
-------------
Id
UserId
Stuff
A single user may have just one profile. Therefore, I have set a UNIQUE constraint no the UserId column in the UserProfile table. Also, UserProfile.Id is defined as a foreign key that references User.Id.
However, in the Entity Framework model, I get the following:
class User
{
ICollection<UserProfile> UserProfiles { get; set; }
}
At first, I had the UserProfile table without any primary key of its own like this:
UserProfile
--------------
UserId
Stuff
But EntityFramework made that read-only as it needed to have a primary key on every table that you want to be made writable. So, I put an Id column in the UserProfile table as well.
UPDATE
I tried setting the multiplicit/cardinality of the association between the User table and the UserProfile table in my EDMX in the designer. However, I get this error suggesting I should turn it back to what it was, i.e. a one-to-many relationship.
Running transformation: Multiplicity is not valid in Role 'UserBasicProfile'
in relationship 'FK_UserBasicProfile_User'. Because the Dependent Role
properties are not the key properties, the upper bound of the multiplicity
of the Dependent Role must be *.
I've found a way. Well, not purely a 1-1 relationship but a 1-0..1 relationship was what I was after, which, for all practical purposes is a one-to-one relationship.
If you ever fall into this trap like I described in my question, here's what you do:
1) Remove the Id field from the dependent table, in this case the UserProfile table. Therefore, do not give it a separate primary key.
2) Instead, mark the foreign key in the dependent table as its own primary key. In this case, mark the UserId field itself as the primary key of the UserProfile table.
In addition to these two, I assume, as I outlined in the question that you've got a primary and foreign key relationship between the authority (the User table in this case) and the dependent table (the UserProfile table in this example).
So, you new table structure should look like this:
User
------
Id
UserProfile
-------------
UserId (foreign key, and also the primary key of this UserProfile table)
Stuff
Entity Framework will then recognize this 1-0..1 relationship.

Entity Framework table per type inheritance with PK->FK, not PK->PK

I have to create an entity framework model for a badly designed database. The database uses table per type inheritance but it does so with using a PK->FK relationship, not a PK->PK relationship. E.g.
Person
PersonID (PK)
Name
Employee
EmployeeID (PK)
PersonID (FK)
DateStarted
HourlyEmployee
HourlyEmployeeID (PK)
EmployeeID (FK)
HourlyRate
Obviously this is just badly designed, but I can't change it. Table per type inheritance in the entity framework essentially wants EmployeeID not to exist and the PK for Employee to be PersonID. Is it possible to create a model for this database, or do I choose another tool? any recommendations?
You will not map this as TPT inheritance because your database is configured in the way that doesn't allow you cheating EF.
If Employee.EmployeeID is auto-generated in the database and Employee.PersonID is unique (uniqueness must be enforced in the database) you should be able (not tested) to cheat EF by simply mapping:
public Employee : Person {
public DateTime DateStarted { get; set; }
}
This class will tell EF that Employee inherits key from Person (PersonID) and you will hide the real key from EF - this should work if the real key is auto-generated.
The problem is your next level of inheritance which breaks this pattern. To make this work your HourlyEmployee will have to reference PersonID - not EmployeeID. EF now doesn't know about EmployeeID existence so it even cannot map relation with HourlyEmployee.
TPT inheritance in code first has one additional limitation - PK column must have the same name in all tables.
You can create a model from the database if it exists but it might not be what you expect. EF sometimes doesn't work that great with weird database structures.

How do you signify a primary key in EF Code First?

I am new to EF Code First. How do you go about representing that a variable in an object should be the primary key when it is peristed to a database table?
By default and by convention, if you have a column called ID or (EntityName)ID (e.g. CustomerID for an entity of type Customer), then that will be your primary key.
Otherwise, you need to use the [Key] attribute on another column.

Entity Framework Is it possible to add an ASSOCIATION between Primary Keys and a Foreign Key

I've got the following entities on my EDMX :-
These two entites were generated by Update Model From Database.
Now, notice how my country has the following primary key :-
Name & IsoCode
this is because each country is UNIQUE in the system by Name and IsoCode.
Now, with my States ... it's similar. Primary Key is :-
Name & CountryId
Each state is unique by name and per country.
Now, the Foreign Key for States is a CountryId. This is the sql :-
ALTER TABLE [dbo].[States] WITH CHECK ADD
CONSTRAINT [FK_States_Countries] FOREIGN KEY([CountryId])
REFERENCES [dbo].[Countries] ([CountryId])
ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[States] CHECK CONSTRAINT [FK_States_Countries]
GO
Pretty simple stuff.
BUT EntityFramework doesn't like it :( It's assuming that i need to connect some properties from State entity to both primary key properties in the Country entity.
Is it possible to add an ASSOCIATION between Country and State on Country.CountryId <-> State.CountryId ... like i have mapped in my DB ?
Cheers ;)
In EF (3.5 and 4.0) FKs MUST point to Primary Keys.
But you appear to be attempting to point to a Candidate Key (i.e. [Countries].[CountryId]
I know that this is something the EF team are considering for the next version though :)
Hope this helps
Alex
For proper DB normalization, first thing is that primary keys must be only CountryId and StateId fields - the main Id fields for each table.
And ss I see from the description Name & IsoCode and Name & CountryId should be actually Unique keys, not primary.
Then the model class State should have a field:
public Country Country { get; set; }
Now EF have very good examples and since 4.3.1 + it fully supports Code first / DB first models, which I think will ease solving this.
EF 5 have more compatibility updates so I think it wont be a problem for legacy DB engines.