在实体框架 4 中,是否可以将递归外键建模为一对一?

发布于 2024-11-02 22:58:41 字数 1942 浏览 0 评论 0原文

我有下表:

CREATE TABLE [dbo].[Exception] (
    [ExceptionID]       INT           IDENTITY (1, 1) NOT NULL,
    [ParentExceptionID] INT           NULL,
    [ApplicationID]     INT           NOT NULL,
    [TypeName]          VARCHAR (256) NOT NULL,
    [Message]           VARCHAR (MAX) NULL,
    [StackTrace]        VARCHAR (MAX) NULL,
    [MachineName]       VARCHAR (128) NULL,
    [UserName]          VARCHAR (64)  NULL,
    [CreatedOn]         DATETIME      NOT NULL
)

用于存储应用程序中发生的异常。我有一个这样的外键:

ALTER TABLE [dbo].[Exception] ADD CONSTRAINT [FK_ParentException_Exception] 
FOREIGN KEY ([ParentExceptionID])
REFERENCES [dbo].[Exception] ([ExceptionID])
ON DELETE NO ACTION ON UPDATE NO ACTION;

为什么我将它设计为 ParentExceptionID 而不是 InnerExceptionID,我无法告诉你。这肯定会让在 EF 中建模变得更加混乱。不过,这完全是任意的,因为您可以通过任何一种方式表达相同的数据。

正如您应该知道的,一个异常只能有一个内部异常。但是,此架构允许多行声明具有相同的 ParentExceptionID。从逻辑上讲,这种情况永远不会发生,但数据支持它。

通常,当我想在架构中建立非递归一对一关系时,我要么在两个表中使用相同的主键列名称,要么向第二个表的外键添加唯一索引。

前者看起来像这样:

Table A
    PrimaryKey int
    [...]

Table B
    PrimaryKey int
    [...]

或者其他方式:

Table A
    PrimaryKeyA int
    [...]

Table B
    PrimaryKeyB int
    PrimaryKeyA int unique references A.PrimaryKeyA
    [...]

在任何一种情况下,SQL 图和其他工具都会将其识别为一对一关系。不过,我读过,实体框架仅识别第一个模式。

我的问题是,使用递归外键,我不能使用任何一种模式。我显然不能拥有第一个模式,因为我只有一张桌子。我无法使用第二个,因为我无法在外键列上拥有唯一索引,因为将有多个行具有 NULL ParentExceptionID。伙计,我希望 SQL Server 允许一个不强制 NULL 也是唯一的唯一索引!

当我尝试从此架构创建 EF 模型时,它发现多行可以声明具有相同的 ParentExceptionID,并且它迫使我将其建模为 0..1 -> *。我看不出有什么办法可以阻止这一切。我的 Exception 实体有一个 InnerException 导航属性,它是一个集合。显然,这不是我需要的。如果我尝试更改关联的多重性并强制其为 0..1,则会出现构建错误:

关系“FK_ParentException_Exception”中的角色“InnerException”中的多重性无效。由于从属角色属性不是关键属性,因此从属角色的重数上限必须为*。

有什么办法可以做我想做的事吗?即使这意味着改变我的模式,我也可能会考虑它。现在我觉得这是不可能的。

I have the following table:

CREATE TABLE [dbo].[Exception] (
    [ExceptionID]       INT           IDENTITY (1, 1) NOT NULL,
    [ParentExceptionID] INT           NULL,
    [ApplicationID]     INT           NOT NULL,
    [TypeName]          VARCHAR (256) NOT NULL,
    [Message]           VARCHAR (MAX) NULL,
    [StackTrace]        VARCHAR (MAX) NULL,
    [MachineName]       VARCHAR (128) NULL,
    [UserName]          VARCHAR (64)  NULL,
    [CreatedOn]         DATETIME      NOT NULL
)

This is used to store exceptions that happen in applications. I have a foreign key as such:

ALTER TABLE [dbo].[Exception] ADD CONSTRAINT [FK_ParentException_Exception] 
FOREIGN KEY ([ParentExceptionID])
REFERENCES [dbo].[Exception] ([ExceptionID])
ON DELETE NO ACTION ON UPDATE NO ACTION;

Why I designed it as ParentExceptionID instead of InnerExceptionID, I couldn't tell you. This definitely makes it extra confusing modeling this in E.F. It's entirely arbitrary, though, as you can express the same data either way.

As you should know, an Exception can only have one inner exception. However, this schema allows multiple rows to claim to have the same ParentExceptionID. Logically, this never happens, but the data supports it.

Normally when I want to make a non-recursive one-to-one relationship in my schema I either use the same primary key column name in both tables, or I add a unique index to the foreign key of the second table.

The former would look like this:

Table A
    PrimaryKey int
    [...]

Table B
    PrimaryKey int
    [...]

Or the other way:

Table A
    PrimaryKeyA int
    [...]

Table B
    PrimaryKeyB int
    PrimaryKeyA int unique references A.PrimaryKeyA
    [...]

In either case SQL diagrams and other tools will recognize this as a one-to-one relationship. Entity Framework only recognizes the first pattern, though, I've read.

My problem is, with a recursive foreign key, I can't use either pattern. I obviously can't have the first pattern because I have only one table. I can't use the second because I can't have a unique index on the foreign key column since there will be more than one row that has a NULL ParentExceptionID. Man, I wish SQL Server allowed a unique index that didn't force NULL to also be unique!

When I try to make an E.F. model from this schema it sees that multiple rows can claim to have the same ParentExceptionID, and it forces me to model this as a 0..1 -> *. I can't see any way to stop this. My Exception entity has an InnerException navigation property and it's a collection. Clearly, this is not what I need. If I try to change the multiplicity of the association and force it to be 0..1 I get a build error:

Multiplicity is not valid in Role 'InnerException' in relationship 'FK_ParentException_Exception'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be *.

Is there any way to do what I want? Even if that means changing my schema, I might consider it. Right now I'm thinking it's impossible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

嘿嘿嘿 2024-11-09 22:58:41

不,无法在 EF 中映射此内容,甚至外键上的唯一索引也无济于事,因为当前版本的 EF 不支持唯一键。

唯一的方法是将其映射为一对多并隐藏真正的导航属性,例如内部成员。相反,您将在实体类的部分部分公开另一个属性(不在实体图中),该属性将允许仅定义单个内部异常(它将简单地处理内部集合中的第一项)。

该解决方案的问题部分是相关异常的预先加载,因为您无法预先加载自定义属性,而只能加载导航属性。

No there is no way to map this in EF and even Unique index on the foreign key will not help you because current version of EF doesn't have support for unique keys.

The only way is to map it as one-to-many and hide the real navigation property as for example internal member. Instead of that you will expose another property (not in the entity diagram) in your partial part of the entity class which will allow definition of only single inner exception (it will simply work with the first item from the internal collection).

The problematic part of this solution would be eager loading of related exception because you can't eager load custom properties but only navigation properties.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文