使用实体框架数据库优先方式创建一对一关系

4
在EF Code First中,我们可以通过以下代码创建一对一关系:

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;}

}

然而,当我尝试在Visual Studio 2012中使用EF Database First创建相同的关系时,遇到了麻烦。以下是我的代码:

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])

错误信息大致如下:
"Error  2   SQL01767: Foreign key 'FK_Users_UserDetails' references invalid table 'UserDetails'.    

我认为这个错误的原因可能是在尝试引用“UserDetailID”外键时,它发现该外键还没有被创建。但我不知道如何解决这个问题,也不确定这是否是正确的解决方法。使用EF建立一对一关系很棘手,甚至有些人认为不可能。有人能给我建议吗?谢谢。
更新:为了澄清我的情况,我正在尝试在Visual Studio 2012数据库项目中设计数据库,然后将其发布到SQL服务器中,之后再从SQL服务器中的数据库创建/更新我的.edmx文件。我不确定如何创建一个EF可以正确识别并在.edmx文件中创建正确类的一对一关系。

你的意思是使用EF模型优先,因为当你已经有一个数据库时,才会使用数据库优先? - Ibrahim Najjar
在尝试插入外键之前,您需要先创建两个表。 - Dave Williams
你不应该使用 Database First,而应该使用 Model First,它是专门为这些情况设计的。 - Ibrahim Najjar
3个回答

9
创建一个1:1关系并不是很棘手,当然也不是不可能的,尽管这不是一个特别常见的要求,在这种情况下我看不出来为什么你想要它?如果有人这样说,那么你跟错人了。
无论如何,使用SQL查询似乎与EF无关,你只是直接在数据库中工作,在第一个CREATE中,你正在尝试添加约束,但你还没有创建另一个表...正如你在问题中提到的那样。
我认为你需要先创建两个表,然后使用ALTER TABLE添加约束。
此外,在SO上搜索有关1:1的问题会得到很多结果,所以我建议你这样做。
编辑:因此,使用数据库项目(我只有VS Express,所以我没有那些)你想使用SQL创建“1:1”关系,然后将实体数据模型添加到(可能是不同的)项目中,该项目引用数据库并自动创建1:1关系?
不幸的是,这是完全不同的故事。当我谈到创建1:1的可能性时,我指的是仅限于EF,而不是数据库本身。实际上,在SQL中创建1:1非常困难/不可能,就像你所说的那样。我认为,为了插入到1:1关系中,你需要同时插入到两个表中,或者在添加行时暂时禁用约束。
一般来说,有几个不同的选择。
1. 不要不必要地拆分表格。在真正的1:1中,所有数据都是必需的,所以拆分的唯一原因是出于性能考虑(例如partioning),在这种情况下我会避免。
2. 将多个表映射到单个实体,如此处所示
3. 创建一个1:0..1关系,并在应用程序中强制执行你自己的要求。
在选项2或3中,你可以使用以下SQL创建关系,该关系在第二个表中使用与关系中的FK相同的PK。
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 
);

我建议您在可以的情况下也使用存储生成的标识。


谢谢你,戴夫。接下来的问题是,我如何在Visual Studio 2012数据库项目中先创建没有约束的表,然后再添加约束? - yan
创建表并使用ALTER TABLE添加约束非常容易,你可以在SO、MSDN或其他地方找到相关信息。然而,我认为这并不会帮助你......请查看更新后的答案。 - Dave Williams
谢谢,你理解了我的意思,尽管我仍然遇到问题...我想给你的答案投票,但不幸的是我没有足够的声望。再次感谢你。 - yan
我不得不重新阅读这个答案,才确定你确实提供了一个答案(当我看到这句话“就像你所说的,在SQL中创建1:1实际上非常困难/不可能”时,我最初停止阅读)...这太啰嗦了...为什么你要留下那部分你误解问题的地方(这本来就几乎完全是评论)? - Brett Caswell

3

只需从UserDetail表中删除UserDetailID,并将UserID作为主键和外键同时指向User表的UserID列即可。

这是在数据库中建立1:1关系的正确方法,EF会识别并通过数据库优先的方法映射实体。


1
这个问题已经有几年了..并且ef版本没有说明..但是一个答案是从两个表中删除UserDetailIDUserID应该是两个表上唯一的主键。
'uniqueidentifier' (GUID)数据类型不应该引起问题(与使用INT相反),但你肯定不想用newId来填充它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接