[英]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
表中刪除CreatorID
和Creator
列時,它可以正常遷移。
我如何重新組織我的關系以允許事件具有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
中的列UserId
和EventId
創建索引。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.