简体   繁体   English

在 Entity Framework Core 中,如果我定义这些关系,它会在两个实体之间创建循环关系吗?

[英]In Entity Framework Core, if I define these relationships, will it create a cyclic relationship between two entities?

I have an event scheduling app with a SQL Server database.我有一个带有 SQL Server 数据库的事件调度应用程序。 It is running as part of an ASP.NET web app using Entity Framework Core.它作为使用 Entity Framework Core 的 ASP.NET Web 应用程序的一部分运行。 It has the following tables/entities:它具有以下表/实体:

  • User : may join many events as a participant User :可以作为参与者参加许多活动

  • Event : may have many users as participants. Event :可能有许多用户作为参与者。 It's also created by a single user它也是由单个用户创建的

This many-to-many relationship can be resolved using a join table: UserEvent .这种多对多关系可以使用连接表来解决: UserEvent

I end up with this relationship diagram.我最终得到了这张关系图。

However, my problem is that I am not sure that it is okay if a single User is linked to a single Event twice, both as a participant and as a creator.但是,我的问题是我不确定如果单个User作为参与者和创建者两次链接到单个Event是否可以。 The creator of the event would have their ID stored as a FK in the Event they created and they would have their ID stored as a FK in the UserEvent that registers them as a participant of the Event .事件的创建者会将他们的 ID 作为 FK 存储在他们创建的Event中,他们会将他们的 ID 作为 FK 存储在将他们注册为Event参与者的UserEvent中。 The app assumes if they create the event, they are attending it.该应用程序假定如果他们创建了活动,他们就会参加。

Is there a better way to configure this relationship?有没有更好的方法来配置这种关系?

When I try to add the migration I get the following error:当我尝试添加迁移时,出现以下错误:

Introducing FOREIGN KEY constraint 'FK_Events_MyUsers_CreatorID' on table 'Events' may cause cycles or multiple cascade paths.在表“事件”上引入外键约束“FK_Events_MyUsers_CreatorID”可能会导致循环或多个级联路径。 Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

Here is an example of my code:这是我的代码示例:

User model: User模型:

public class User
{
    public User() {
        UserName = "Default";
    }

    public long ID { get; set; }
    public string UserName { get; set; }
    public virtual List<Event>? UserEvents { get; set; }
}

Event model: Event模型:

public class Event
{
    public Event() 
    {
        EventName = "Default Event Name";
    }

    public long ID { get; set; }
    public long CreatorID { get; set; }
    public User Creator { get; set; }

    public virtual List<User> EventUsers { get; set; }

    public string EventName { get; set; }
}

Override of OnModelCreating method:重写OnModelCreating方法:

protected override void OnModelCreating(ModelBuilder builder) 
{
    base.OnModelCreating(builder);

    builder.Entity<User>(
            b => {
                b.Property(user => user.ID)
                    .IsRequired();

                b.Property(user => user.UserName)
                    .IsRequired();

                b.HasKey(user => user.ID);

                b.HasMany(user => user.UserEvents)
                 .WithMany(e => e.EventUsers)
                 .UsingEntity(join => join.ToTable("UserEvents"));

                b.ToTable("MyUsers");
            });

    builder.Entity<Event>(
            b => {
                b.Property(e => e.ID)
                    .IsRequired();

                b.Property(e => e.EventName)
                    .IsRequired();

                b.Property(e => e.CreatorID)
                    .IsRequired();

                b.HasKey(e => e.ID);

                b.HasMany(e => e.EventUsers)
                 .WithMany(user => user.UserEvents)
                 .UsingEntity(join => join.ToTable("UserEvents"));

                b.HasOne(e => e.Creator)
                 .WithMany();

                b.ToTable("Events");
            });
}

When I remove the CreatorID and Creator columns from the Event table, it migrates fine.当我从Event表中删除CreatorIDCreator列时,它可以正常迁移。

How can I reorganize my relationships to get allow events to have a Creator field and/or track which User created an Event ?我如何重新组织我的关系以允许事件具有Creator字段和/或跟踪哪个User创建了Event


@SNBS Deserves all credit for the answer. @SNBS 值得所有的答案。 See their post below.请参阅下面的帖子。 I had to tweak their code to get it to compile.我不得不调整他们的代码才能编译。

To avoid the relationship detailed in the question it was necessary to add a BIT field to the UserEvent table to represent the user "Role" within that event.为了避免问题中详述的关系,有必要向UserEvent表添加一个 BIT 字段以表示该事件中的用户“角色”。

To ensure that an event could not have more than one creator I had to add a custom SQL function and enforce it as a constraint on the UserEvent table.为确保一个事件不能有多个创建者,我必须添加自定义 SQL 函数并将其强制执行为UserEvent表的约束。

This explanation assumes you're using Visual Studio.此说明假定您使用的是 Visual Studio。

Custom Function自定义函数

If you are new to SQL Server like me you might wonder how to add the function.如果您像我一样不熟悉 SQL Server,您可能想知道如何添加该函数。 In visual studio within the SQL Server Object Explorer.在 SQL Server 对象资源管理器中的 visual studio 中。 Right click on: Programmability->Functions -> Add New Scalar Valued Function .右键单击: Programmability->Functions -> Add New Scalar Valued Function

After writing the code in the new window make sure to click "Update Database" and check your code compiles.在新窗口中编写代码后,请确保单击“更新数据库”并检查代码是否编译。 If it works you should be able to see you new function in the explorer.如果它有效,您应该能够在资源管理器中看到您的新功能。

CREATE FUNCTION CheckOneCreator (@EventId BIGINT)
RETURNS BIT
AS
BEGIN
-- Create and open a cursor
DECLARE Participants CURSOR
FOR
SELECT UserID
FROM UserEvents
WHERE EventID = @EventId;
OPEN Participants;

-- Creators count
DECLARE @CreatorsCount INT;
SELECT @CreatorsCount = 0;

-- Iterate over participants
DECLARE @ParticipantId BIGINT;

FETCH NEXT FROM Participants
INTO @ParticipantId;

WHILE @@FETCH_STATUS = 0
BEGIN
-- Check whether the participant is the creator
DECLARE @IsCreator BIT;

SELECT @IsCreator = (
SELECT TOP 1 IsCreator
FROM UserEvents
WHERE UserID = @ParticipantId
AND EventID = @EventId
);

-- Increase creators count
IF @IsCreator = 1 SELECT @CreatorsCount = (@CreatorsCount + 1);

FETCH NEXT FROM Participants
INTO @ParticipantId;
END

IF @CreatorsCount = 1 RETURN 1;
RETURN 0;
END

Add Constraint添加约束

To add this function as a constraint right click on your database in the SQL Object Explorer in Visual Studio and select New Object.要将此函数添加为约束,请在 Visual Studio 的 SQL 对象资源管理器中右键单击您的数据库,然后选择新建对象。 A code window will open.将打开一个代码窗口。 Once you type the code in again ensure you click "Update Database" and see if your code compiles.再次输入代码后,确保单击“更新数据库”并查看代码是否编译。 If successful you will see the constraint added under the table.如果成功,您将在表格下方看到添加的约束。

ALTER TABLE [dbo].[UserEvents]
    ADD CONSTRAINT [SingleCreator] CHECK ([dbo].[CheckOneCreator]([EventID])=(1));

I think it would be better if each user had a role (creator or general participant) in all events he takes part in. You may create a new column in table UserEvent and name it IsCreator (data type — bit , which is the SQL equivalent of .NET type bool ).我认为如果每个用户在他参与的所有事件中都有一个角色(创建者或一般参与者)会更好。您可以在表UserEvent中创建一个新列并将其命名为IsCreator (数据类型 - bit ,这是 SQL 等效项.NET 类型bool )。 This column should contain 0 for general participants and 1 for creators.此列应包含 0(代表一般参与者)和 1(代表创作者)。

UPDATE : a constraint is necessary to check that an event can have only one creator.更新:需要一个约束来检查事件是否只能有一个创建者。 Firstly, create a function in your database using this SQL code:首先,使用以下 SQL 代码在您的数据库中创建一个函数:

CREATE FUNCTION CheckOneCreator (@EventId BIGINT)
RETURNS BIT
AS
BEGIN
-- Create and open a cursor
DECLARE Participants CURSOR
FOR
SELECT UserId
FROM UserEvent
WHERE EventId = @EventId;
OPEN Participants;

-- Creators count
DECLARE @CreatorsCount INT;
SELECT @CreatorsCount = 0;

-- Iterate over participants
DECLARE @ParticipantId BIGINT;

FETCH NEXT FROM Participants
INTO @ParticipantId;

WHILE @@FETCH_STATUS = 0
BEGIN
-- Check whether the participant is the creator
DECLARE @IsCreator BIT;

SELECT @IsCreator = (
SELECT TOP 1 IsCreator
FROM UserEvent
WHERE UserId = @ParticipantId
AND EventId = @EventId
);

-- Increase creators count
IF @IsCreator SELECT @CreatorsCount = (@CreatorsCount + 1);

FETCH NEXT FROM Participants
INTO @ParticipantId;
END

IF @CreatorsCount != 1 RETURN 0;
ELSE RETURN 1;
END

And define a constraint for table UserEvent :并为表UserEvent定义一个约束:

ALTER TABLE UserEvent
ADD CONSTRAINT CHECK (CheckOneCreator(EventId) = 1);

PS Use this function only if you really don't want a user to be linked twice (because the queries take time). PS 仅当您确实不希望用户被链接两次时才使用此功能(因为查询需要时间)。 And create an index for columns UserId and EventId in table UserEvent .并为表UserEvent中的列UserIdEventId创建索引。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在Entity Framework Core中删除实体之间的关系 - Delete relationship between entities in Entity Framework Core 实体框架核心 - 两个实体之间的多个一对多关系 - Entity Framework Core - Multiple one-to-many relationships between two entities 实体框架:处理两个实体之间的多个关系 - Entity Framework : Handle multiple relationships between two entities 使用数据注释基于多个字段在两个实体之间创建1-1实体框架关系 - Create a 1-1 Entity Framework relationship between two entities based on multiple fields using Data Annotations 与Entity Framework Core建立关系 - Create relationships with Entity Framework Core 在不同实体框架DbContext中的2个实体之间创建数据库关系 - Create a database relationship between 2 entities in different Entity Framework DbContexts 如何在 Entity Framework Core 中处理两个表之间的多个关系? - How to handle multiple relationships between two tables in Entity Framework Core? Entity Framework Core - 两个多对多关系之间的组合 - Entity Framework Core - combination between two many-to-many relationships 如何定义两个实体之间的多个关系? - How to define multiple relationships between two entities? 我要在依赖项或委托人上的两个实体之间定义关系吗? - Do I define a relationship between two entities on the dependent or the principal?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM