In EF Code First, we can create one-to-one relationship by coding like this:
public class User
{
public int UserID {get;set;}
public string Name {get;set;}
public int UserDetailID {get;set;}
public UserDetail Detail {get;set;}
}
public class UserDetail
{
public int UserDetailID {get;set;}
public string Address {get;set:}
public int UserID {get;set;}
public User User {get;set;}
}
However, when I tried to create the same relationship by using EF Database first in visual studio 2012, I got in trouble. Here is my code:
CREATE TABLE [dbo].[Users] (
[UserID] UNIQUEIDENTIFIER CONSTRAINT [DF_Users_UserID] DEFAULT (newid()) NOT NULL,
[UserDetailID] UNIQUEIDENTIFIER NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([UserID] ASC),
CONSTRAINT [FK_Users_UserDetails] FOREIGN KEY ([UserDetailID]) REFERENCES [UserDetails]([UserDetailID])
);
CREATE TABLE [dbo].UserDetails] (
[UserDetailID] UNIQUEIDENTIFIER CONSTRAINT [DF_UserDetails_UserDetailID] DEFAULT (newid()) NOT NULL,
[UserID] UNIQUEIDENTIFIER NOT NULL,
[Address] NVARCHAR(100) NOT NULL,
CONSTRAINT [PK_UserDetails] PRIMARY KEY CLUSTERED ([UserDetailID] ASC),
CONSTRAINT [FK_UserDetails_Users] FOREIGN KEY ([UserID]) REFERENCES [dbo].[Users] ([UserID])
The error message is something like
"Error 2 SQL01767: Foreign key 'FK_Users_UserDetails' references invalid table 'UserDetails'.
I think the reason for this error probably be when it tries to reference the foreign key "UserDetailID", it finds that it hasn't been created yet. But I don't know how to fix this, and I don't even know this is the way to do it, I know doing one-to-one relationship with EF is tricky, or some people even says it's impossible. Can anyone give me any suggestion? Thank you.
Update: Just to clarify my case, I am trying to design the database in visual studio 2012 database project, then publish it to the SQL server, afterward, create/update my .edmx file from the database in SQL server. I am not sure about how to create a one-to-one relationship that the EF can recognize correctly and create the right classes in .edmx file.
Creating a 1:1 relationship is not that tricky and certainly not impossible, although it is not a particularly common requirement and in this case I can't see why you would want it? If people are saying this then you are talking to the wrong people.
Anyhow using SQL queries as you seem to be is not to do with EF, you are just working directly with the database, In the first CREATE you are trying to add the constraint but you haven't created the other table yet... As you mentioned in your question.
I think you need to create both tables first and then add the constraint with ALTER TABLE.
Additionally searching SO for questions about 1:1 turns up quite a lot so I suggest you do that.
EDIT: So using a database project (I only have VS Express so I don't have those) you want to create a "1:1" relationship using SQL and then add an Entity Data Model to a (probably different) project which references the database and automatically create 1:1 relationship?
That is a whole different story unfortunately. When I was talking about possibility to create 1:1 that was in reference to EF only and not to databases as such. It is actually very difficult/impossible as you said to create 1:1 in SQL. I think that it makes sense that in order to insert into a 1:1 realationship you would need to somehow insert into both tables at exactly the same time or fiddle about with disabling constraints briefly when adding rows.
In general there are a few different option.
Don't split the tables unnecessarily. In true 1:1 all data is required so the only reason to split is for performance reasons (e.g partioning) which I would avoid in this case.
Map multiple table to a single entity as show here.
Create a 1:0..1 relationship and enforce you own requirements in the application.
In either option 2 or 3 you can use the following SQL to create a relationship which uses the same PK on the second table as the FK in the relationship.
CREATE TABLE [dbo].[Users] (
[UserID] UNIQUEIDENTIFIER CONSTRAINT [DF_Users_UserID] DEFAULT (newid()) NOT NULL,
[Name] NVARCHAR (50) NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([UserID] ASC),
);
CREATE TABLE [dbo].[UserDetails] (
[UserID] UNIQUEIDENTIFIER NOT NULL,
[Address] NVARCHAR(100) NOT NULL,
CONSTRAINT [PK_UserDetails] PRIMARY KEY CLUSTERED ([UserID] ASC),
CONSTRAINT [FK_UserDetails_Users] FOREIGN KEY ([UserID]) REFERENCES [dbo].[Users] ([UserID]) ON DELETE CASCADE
);
I suggest you also use store generated identity as well where you can.
Just remove UserDetailID from the UserDetail table and make UserID both primary key and a foreign key to the UserID column of the User table.
This the correct way to make 1:1 relationships in a database and EF recognizes it and maps the entities appropriately with database-first approach.
The question is a couple years old.. and the ef version wasn't stated.. but one answer is to remove UserDetailID from both tables. UserID should be the only primary key on both tables.
the 'unqieidentifier' (GUID) data type shouldn't pose an issue (opposed to using INT), but you certainly don't want to populate it with newId..
Related
I am trying to create the following tables in Postgres 13.3:
CREATE TABLE IF NOT EXISTS accounts (
account_id Integer PRIMARY KEY NOT NULL
);
CREATE TABLE IF NOT EXISTS users (
user_id Integer PRIMARY KEY NOT NULL,
account_id Integer NOT NULL REFERENCES accounts(account_id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS calendars (
calendar_id Integer PRIMARY KEY NOT NULL,
user_id Integer NOT NULL,
account_id Integer NOT NULL,
FOREIGN KEY (user_id, account_id) REFERENCES users(user_id, account_id) ON DELETE CASCADE
);
But I get the following error when creating the calendars table:
ERROR: there is no unique constraint matching given keys for referenced table "users"
Which does not make much sense to me since the foreign key contains the user_id which is the PK of the users table and therefore also has a uniqueness constraint. If I add an explicit uniqueness constraint on the combined user_id and account_id like so:
ALTER TABLE users ADD UNIQUE (user_id, account_id);
Then I am able to create the calendars table. This unique constraint seems unnecessary to me as user_id is already unique. Can someone please explain to me what I am missing here?
Postgres is so smart/dumb that it doesn't assume the designer to do stupid things.
The Postgres designers could have taken different strategies:
Detect the transitivity, and make the FK not only depend on users.id, but also on users.account_id -> accounts.id. This is doable but costly. It also involves adding multiple dependency-records in the catalogs for a single FK-constraint. When imposing the constraint(UPDATE or DELETE in any of the two referred tables), it could get very complex.
Detect the transitivity, and silently ignore the redundant column reference. This implies: lying to the programmer. It would also need to be represented in the catalogs.
cascading DDL operations would get more complex, too. (remember: DDL is already very hard w.r.t. concurrency/versioning)
From the execution/performance point of view: imposing the constraints currently involves "pseudo triggers" on the referred table's indexes. (except DEFERRED, which has to be handled specially)
So, IMHO the Postgres developers made the sane choice of refusing to do stupid complex things.
I've got two tables - Appointment and User. Appointments can be linked to two different Users - a student and a member of staff. The Appointment table contains two foreign keys: StaffUsername and ExternalID. These reference columns in the User table named Username (the User table's PK) and ExternalID (a UNIQUE index). Here are the table definitions:
CREATE TABLE [dbo].[Appointment]
(
[ID] INT NOT NULL IDENTITY(1,1),
[AppointmentTypeID] INT NOT NULL,
[StartTime] DATETIME NOT NULL,
[EndTime] DATETIME NOT NULL,
[AppointmentSlotID] INT NULL,
[StaffUsername] NVARCHAR(200) NOT NULL,
[ExternalID] NVARCHAR(10) NULL,
[BookedBy] NVARCHAR(200) NOT NULL,
[BookedTimestamp] DATETIME NOT NULL,
[ReminderEmailSentTimestamp] DATETIME NULL,
[CancelledBy] NVARCHAR(200) NULL,
[CancelledTimestamp] DATETIME NULL,
[StudentDidNotAttend] BIT NULL,
[LastModifiedTimestamp] DATETIME NOT NULL,
[LastModifiedBy] NVARCHAR(200) NOT NULL,
CONSTRAINT [PK_Appointment] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [FK_Appointment_AppointmentType] FOREIGN KEY ([AppointmentTypeID]) REFERENCES [dbo].[AppointmentType]([ID]),
CONSTRAINT [FK_Appointment_AppointmentSlot] FOREIGN KEY ([AppointmentSlotID]) REFERENCES [dbo].[AppointmentSlot]([ID]),
CONSTRAINT [FK_Appointment_User_StaffUsername] FOREIGN KEY ([StaffUsername]) REFERENCES [dbo].[User]([Username]),
CONSTRAINT [FK_Appointment_User_ExternalID] FOREIGN KEY ([ExternalID]) REFERENCES [dbo].[User]([ExternalID])
)
CREATE TABLE [dbo].[User]
(
[Username] NVARCHAR(200) NOT NULL,
[FirstName] NVARCHAR(200) NULL,
[LastName] NVARCHAR(200) NULL,
[EmailAddress] NVARCHAR(200) NULL,
[IsStaff] BIT NOT NULL DEFAULT 0,
[ExternalID] NVARCHAR(10) NOT NULL,
[LastLogin] DATETIME NULL,
CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED ([Username] ASC),
CONSTRAINT [UQ_ExternalID] UNIQUE ([ExternalID])
)
Unfortunately, when I use the Update model from database option in the EDMX model designer, it will not pick up the foreign key on the ExternalID columns. It remains looking like this (highlighted in green are the properties relating to the relationship which is modelled correctly, in yellow are the properties which should relate to a second relationship but are being ignored):
I know from experience that the EDMX designer can be quirky at times, especially when detecting changes to objects, so I've tried all the usual tricks. I've checked in Web.config that my connection string is pointing to the correct database. I've deleted the Appointment and User tables in the designer completely and run the Update command again. I've tried that with a save and restart of Visual Studio between deletion and update, too.
To check the relationship is correct in the database I've created a database diagram in SSMS which shows the troublesome relationship correctly:
I've also created a brand new project and added a new Entity Data Model pointing to the same database with the same credentials, just in case the issue was related to the fact that I'm updating an existing model, but no dice. Even in the new project, the relationship isn't detected.
I also tried to create the Navigation Property manually, but as you can see from this screenshot, the foreign key I'd need to select isn't available in the dropdown list:
I don't know if the issue somehow relates to the fact that the ExternalID column isn't the primary key of the User table, or maybe its NVARCHAR(10) data type. I've no idea, to be honest.
Any suggestions as to why this foreign key isn't being detected? And how I can fix it? My project targets .NET Framework 4.6 and I'm using EF6. Obviously I'm using Database First.
In EF6 an Entity only has one key, and so all Navigation Properties must use a Foreign Key that references the same key. EF Core supports Alternate Keys, and supports a Database-First workflow with Reverse Engineering.
Is there a way to set entity reference names from the database?
I’m using database first.
I have a table with multiple foreign keys to the same table
(Address) as shown below.
EF creates entity relations for the 2 addresses and sets the names
to AddressReference and Address1Reference which is not as
descriptive as I would like.
I know I can do this from the EF side with annotations but is there a way to set the entity reference names from the database?
CREATE TABLE [dbo].[Person] (
[ID] INT PRIMARY KEY IDENTITY NOT NULL,
[Name] NVARCHAR(50) NOT NULL,
[HomeAddressID] INT,
[WorkAddressID] INT
)
GO
CREATE INDEX [IX_Person_HomeAddressID] ON [dbo].[Person] ([HomeAddressID])
GO
CREATE INDEX [IX_Person_WorkAddressID] ON [dbo].[Person] ([WorkAddressID])
GO
ALTER TABLE [dbo].[Person] ADD CONSTRAINT [FK_Person_HomeAddressID] FOREIGN KEY ([HomeAddressID]) REFERENCES [Address] ([HomeAddressID])
GO
ALTER TABLE [dbo].[Person] ADD CONSTRAINT [FK_Person_WorkAddressID] FOREIGN KEY ([WorkAddressID]) REFERENCES [Address] ([WorkAddressID])
GO
No, using database first, you will need to update the navigation names in the data model by-hand.
Of course you could modify the T4 file to generate a different navigation name (it uses the FK table name by default).
I have a database which has three tables
Messages - PK = MessageId
Drafts - PK = DraftId
History - FK = RelatedItemId
The History table has a single foreign Key [RelatedItemId] which maps to one of the two Primary keys in Messages and Drafts.
Is there a name for this relationship?
Is it just bad design?
Is there a better way to design this relationship?
Here are the CREATE TABLE statements for this question:
CREATE TABLE [dbo].[History](
[HistoryId] [uniqueidentifier] NOT NULL,
[RelatedItemId] [uniqueidentifier] NULL,
CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED ( [HistoryId] ASC )
)
CREATE TABLE [dbo].[Messages](
[MessageId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_Messages] PRIMARY KEY CLUSTERED ( [MessageId] ASC )
)
CREATE TABLE [dbo].[Drafts](
[DraftId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_Drafts] PRIMARY KEY CLUSTERED ( [DraftId] ASC )
)
In a short description the solution you have used is called:
Polymorphic Association
Objective: Reference Multiple Parents
Resulting anti-pattern: Use dual-purpose foreign key, violating first normal form (atomic issue), loosing referential integrity
Solution: Simplify the Relationship
More information about the problem.
BTW createing a common super-table will help you:
Is there a name for this relationship?
There is no standard name that I'm aware of, but I've heard people using the term "generic FKs" or even "inner-platform effect".
Is it just bad design?
Yes.
The reason: it prevents you from declaring a FOREIGN KEY, and therefore prevents the DBMS from enforcing referential integrity directly. Therefore you must enforce it trough imperative code, which is surprisingly difficult.
Is there a better way to design this relationship?
Yes.
Create separate FOREIGN KEY for each referenced table. Make them NULL-able, but make sure exactly one of them is non-NULL, through a CHECK constraint.
Alternatively, take a look at inheritance.
Best practice I have found is to create a Function that returns whether the passed in value exists in either of your Messages and Drafts PK columns. You can then add a constraint on the column on the History that calls this function and will only insert if it passes (i.e. it exists).
Adding non-parsed example Code:
CREATE FUNCTION is_related_there (
IN #value uniqueidentifier )
RETURNS TINYINT
BEGIN
IF (select count(DraftId) from Drafts where DraftId = #value + select count(MessageId) from Messages where MessageId = #value) > 0 THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
ALTER TABLE History ADD CONSTRAINT
CK_HistoryExists CHECK (is_related_there (RelatedItemId) = 1)
Hope that runs and helps lol
I have a WCF Data Service (5.5) sitting over an EF (5.0) model,
I'm getting the following error when I query $metadata:
"An IEdmModel instance was found that failed validation. The following errors were reported:
InvalidMultiplicityOfDependentEnd : The multiplicity of the dependent end 'QuestionsetMember' is not valid. Because the dependent properties don't represent the dependent end key, the the multiplicity of the dependent end must be '*'."
QuestionsetMember has a composite primary key of 2 columns, each of which is hooked to a primary key of another table, i.e. a foreign key exist from each column of the key to the two tables' primary keys.
I've searched but cannot find any info on "InvalidMultiplicityOfDependentEnd".
Also tried fiddling with the relationships in the EDMX, but changing the End Multiplicity causes errors which won't allow the model to compile.
Any ideas how to get round this (hopefully without changing my schema) ?
This seems to be a very rare error. I did not find anywhere else an explanation of that error. So i did find for me a solution after inspecting every single constraint and every column in both tables. To my great surprise the order of the primary key columns seems to be relevant.
For explanation: I do the database-design within the sql server management studion, and update my model with the entity framework designer.
First Table:
CREATE TABLE Table1
(
Column1 int NOT NULL,
Column2 int NOT NULL,
PRIMARY KEY (Column1,Column2)
)
Second Table:
CREATE TABLE Table2
(
Column1 int NOT NULL,
Column2 int NOT NULL,
PRIMARY KEY (Column1,Column2)
FOREIGN KEY (Column1,Column2) REFERENCES Table1(Column1,Column2)
)
This would work. But it do not work, if you would define the columns of the primary key in the second table in another order:
CREATE TABLE Table2
(
-- Changed order in definition:
Column1 int NOT NULL,
Column2 int NOT NULL,
-- Changed order in PK group:
PRIMARY KEY (Column2,Column1)
-- Leave the FK definition untouched:
FOREIGN KEY (Column1,Column2) REFERENCES Table1(Column1,Column2)
)
I think the order of the column definition has impact on the generated model. And this order could maybe have an impact in the model validation within the IEdmModel class. Who knows...