簡體   English   中英

在 Entity Framework Core 中,如果我定義這些關系,它會在兩個實體之間創建循環關系嗎?

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

我有一個帶有 SQL Server 數據庫的事件調度應用程序。 它作為使用 Entity Framework Core 的 ASP.NET Web 應用程序的一部分運行。 它具有以下表/實體:

  • User :可以作為參與者參加許多活動

  • Event :可能有許多用戶作為參與者。 它也是由單個用戶創建的

這種多對多關系可以使用連接表來解決: UserEvent

我最終得到了這張關系圖。

但是,我的問題是我不確定如果單個User作為參與者和創建者兩次鏈接到單個Event是否可以。 事件的創建者會將他們的 ID 作為 FK 存儲在他們創建的Event中,他們會將他們的 ID 作為 FK 存儲在將他們注冊為Event參與者的UserEvent中。 該應用程序假定如果他們創建了活動,他們就會參加。

有沒有更好的方法來配置這種關系?

當我嘗試添加遷移時,出現以下錯誤:

在表“事件”上引入外鍵約束“FK_Events_MyUsers_CreatorID”可能會導致循環或多個級聯路徑。 指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 約束。

這是我的代碼示例:

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模型:

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

重寫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");
            });
}

當我從Event表中刪除CreatorIDCreator列時,它可以正常遷移。

我如何重新組織我的關系以允許事件具有Creator字段和/或跟蹤哪個User創建了Event


@SNBS 值得所有的答案。 請參閱下面的帖子。 我不得不調整他們的代碼才能編譯。

為了避免問題中詳述的關系,有必要向UserEvent表添加一個 BIT 字段以表示該事件中的用戶“角色”。

為確保一個事件不能有多個創建者,我必須添加自定義 SQL 函數並將其強制執行為UserEvent表的約束。

此說明假定您使用的是 Visual Studio。

自定義函數

如果您像我一樣不熟悉 SQL Server,您可能想知道如何添加該函數。 在 SQL Server 對象資源管理器中的 visual studio 中。 右鍵單擊: Programmability->Functions -> Add New Scalar Valued Function

在新窗口中編寫代碼后,請確保單擊“更新數據庫”並檢查代碼是否編譯。 如果它有效,您應該能夠在資源管理器中看到您的新功能。

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

添加約束

要將此函數添加為約束,請在 Visual Studio 的 SQL 對象資源管理器中右鍵單擊您的數據庫,然后選擇新建對象。 將打開一個代碼窗口。 再次輸入代碼后,確保單擊“更新數據庫”並查看代碼是否編譯。 如果成功,您將在表格下方看到添加的約束。

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

我認為如果每個用戶在他參與的所有事件中都有一個角色(創建者或一般參與者)會更好。您可以在表UserEvent中創建一個新列並將其命名為IsCreator (數據類型 - bit ,這是 SQL 等效項.NET 類型bool )。 此列應包含 0(代表一般參與者)和 1(代表創作者)。

更新:需要一個約束來檢查事件是否只能有一個創建者。 首先,使用以下 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

並為表UserEvent定義一個約束:

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

PS 僅當您確實不希望用戶被鏈接兩次時才使用此功能(因為查詢需要時間)。 並為表UserEvent中的列UserIdEventId創建索引。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM