將 imageURL 添加到我的資料庫并在我的構建器中傳遞外鍵時,外鍵會在我的資料庫中重復。我的程式添加了一個藝術品,然后嘗試將所述藝術品的影像添加到 SQL 中的單獨表中。
添加影像時,它會復制它用作外鍵的整個藝術品。我嘗試了一些事情,例如添加[ForeignKey("Artwork")]&[ForeignKey("ArtworkId")]以及擺弄我的構建器。
我的模型:
public partial class ArtworkImage
{
[Key]
public int ArtworkImageId { get; set; }
public string ImageURL { get; set; }
public string ImageSize { get; set; }
[ForeignKey("ArtworkId")]
public virtual Artwork Artwork { get; set; }
public int ArtworkId { get; set; }
}
public partial class Artwork
{
[Key]
public int ArtworkId { get; set; }
public string Identifier { get; set; }
public string Category { get; set; }
public ICollection<ArtworkImage> ArtworkImage { get; set; }
}
我的構建器看起來像:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Artwork>()
.HasKey(x => x.ArtworkId)
.HasMany(p => p.ArtworkImage)
.WithRequired(p => p.Artwork);
modelBuilder.Entity<ArtworkImage>()
.HasKey(b => b.ArtworkImageId)
.HasRequired(p => p.Artwork)
.WithMany(d => d.ArtworkImage)
.HasForeignKey(p => p.ArtworkId);
Database.SetInitializer<Artworkcontext>(null);
base.OnModelCreating(modelBuilder);
}
我的遷移:
public override void Up()
{
CreateTable(
"dbo.ArtworkImages",
c => new
{
ArtworkImageId = c.Int(nullable: false, identity: true),
ImageURL = c.String(),
ImageSize = c.String(),
ArtworkId = c.Int(nullable: false),
})
.PrimaryKey(t => t.ArtworkImageId)
.ForeignKey("dbo.Artworks", t => t.ArtworkId, cascadeDelete: true)
.Index(t => t.ArtworkId);
CreateTable(
"dbo.Artworks",
c => new
{
ArtworkId = c.Int(nullable: false, identity: true),
Identifier = c.String(),
Category = c.String(),
})
.PrimaryKey(t => t.ArtworkId);
}
添加藝術品然后添加藝術品影像的代碼:
Artwork newArtwork = new Artwork();
ArtworkImage newArtworkImage = new ArtworkImage();
newArtwork.Identifier = identifierCode;
artworkRepository.Add(newArtwork);
newArtworkImage.ImageURL = fileName;
newArtworkImage.Artwork = newArtwork;
artworkImageRepository.Add(newArtworkImage);
EFArtworkRepository:(添加)
public void Add(Artwork artwork)
{
context.Artworks.Add(artwork);
context.SaveChanges();
}
EFArtworkImageRepository:(添加)
public void Add(ArtworkImage artworkImage)
{
context.ArtworkImages.Add(artworkImage);
context.SaveChanges();
}

我看過并實施但沒有奏效的一些事情,也許實施錯誤?:
- 為 id EF Core 3.1 創建了重復的外鍵和列
- 物體框架外鍵插入重復鍵
uj5u.com熱心網友回復:
這里的問題不是配置,你可以從遷移代碼中看到已經正確定義了關系,所以現在我們繼續你的操作邏輯。
這可能并不明顯,但是您的代碼將newArtwork實體顯式添加到兩個不同的底層DbContext實體中,就您的代碼而言,它也可能是兩個不同的資料庫!從您的觀察和常見的實作模式來看,兩者EFArtworkRepository都有EFArtworkImageRepository自己的DbContext.
我們將介紹資料重復背后的根本原因,但在像這樣的 Repo 模式中,通過設定 FK 參考而不是物件,您會遇到更少的問題,特別是如果您要在多個存盤庫之間傳遞相同的物件。以下是常見的解決方法:
Artwork newArtwork = new Artwork();
newArtwork.Identifier = identifierCode;
artworkRepository.Add(newArtwork);
ArtworkImage newArtworkImage = new ArtworkImage();
newArtworkImage.ImageURL = fileName;
newArtworkImage.ArtworkId = newArtwork.ArtworkId;
artworkImageRepository.Add(newArtworkImage);
EF 中的底層DbContext實際上希望在創建新物體時為您管理 Id 和關聯 FK 的分配。為了實作這一點,它使用物件參考在內部跟蹤物件,它不能使用鍵列,因為對于插入這些鍵還沒有值,所以通常它們都為零。
如果您使用基于物件的關系并且您正在創建Principal和Dependent物體,則DbContext將支持將這些物件作為單個操作處理:
Artwork newArtwork = new Artwork();
newArtwork.Identifier = identifierCode;
ArtworkImage newArtworkImage = new ArtworkImage();
newArtworkImage.ImageURL = fileName;
newArtwork.ArtworkImage = new HashSet<ArtworkImage>();
newArtwork.ArtworkImage.Add(newArtworkImage);
artworkRepository.Add(newArtwork);
注意:我們只artworkRepository在這里使用過,已經達到了預期的效果
當您插入Dependent物體時,類似的邏輯也可以按需創建Principal物體。artworkImageRepository這實際上是從邏輯的角度執行的代碼:
Artwork newArtwork = new Artwork();
ArtworkImage newArtworkImage = new ArtworkImage();
newArtwork.Identifier = identifierCode;
//artworkRepository.Add(newArtwork);
newArtworkImage.ImageURL = fileName;
newArtworkImage.Artwork = newArtwork;
artworkImageRepository.Add(newArtworkImage);
這與這個流暢的初始化相同:
Artwork newArtwork = new Artwork();
ArtworkImage newArtworkImage = new ArtworkImage
{
ImageUrl = fileName,
Artwork = new {
ArtworkId = 12,
Identifier = identifierCode
}
};
您的代碼創建了一個實體,newArtwork該實體已保存到資料庫中,該資料庫為其分配了鍵值,在此示例中,我已宣告它被分配了12.
context從內部來看,這是一個外部參考,為了在這個artworkImageRepository資料庫中準確地重建這個唯一的物體圖,我們還需要創建一個新的 principal 實體。Artwork
在 EF6 中,物件參考將勝過Key和FK值。
不要假設在生成和執行關聯的 SQL 陳述句之前,會嘗試首先查找資料庫以確定主體表中是否已經存在匹配記錄。
- 在每次
DbSet.Add()操作之前這樣做是非常低效的。
EF的神奇之處在于,我們可以在模型中使用物件參考,如果您真的愿意,甚至根本不公開 FK。但是這個魔法只有在你為你的邏輯使用相同的實體時才有效。 DbContext
- 不要誤會我的意思,我是一個 FK 型別的人,在您的模型中使用 FK 可以真正簡化 EF 資料操作和邏輯,但從技術上講,我們的模型中不需要它們以使 EF 作業
要使 EF不使用當前代碼(使用物件參考)在 中插入重復 Artwork實體artworkImageRepository,您首先需要將的參考附加newArtwork到context. 通過這種方式,內部物件參考可能會被識別,而不是被標記為Inserted物體更改狀態。這個代碼示例不是很好的建議,不是很遠,所以不要嘗試復制它,但我希望它有助于解釋這種現象:
public void Add(ArtworkImage artworkImage)
{
context.Artworks.Attach(artworkImage.Artwork);
context.ArtworkImages.Add(artworkImage);
context.SaveChanges();
}
您的代碼演示的是作業單元模式和域存盤庫模式之間的沖突。您的代碼代表單個作業單元(UOW),但它正在單獨訪問多個域。當你這樣做時,不同的域存盤庫甚至不知道彼此,所以當我們在它們之間傳遞資料時,每個存盤庫不能假設你已經對其他存盤庫進行了特定呼叫以確保資料處于特定狀態,唯一合理的可以假設的狀態是進入Add()方法的物件尚不存在。
當我們將 EF 包裝DbContext在存盤庫中時,我們自己負責管理物件生命周期和參考。如果您需要一個無狀態的抽象層,或者如果您的存盤庫代表一個特定的隔離作業單元,則存盤庫模式非常有效。但是正如您已經確定的那樣,它引入了邏輯代碼實踐,可以阻止 EF 執行您可能期望它執行的操作。
EF 中的DbContext表示 UOW 本身,您的原始邏輯直接針對 DbContext 執行不會表現出您觀察到的行為。了解其中一些基本概念以充分利用您選擇的架構設計模式非常重要。此針對 EF 本身的代碼可能有效:
Artwork newArtwork = new Artwork();
ArtworkImage newArtworkImage = new ArtworkImage();
newArtwork.Identifier = identifierCode;
context.Artworks.Add(newArtwork);
newArtworkImage.ImageURL = fileName;
newArtworkImage.Artwork = newArtwork;
context.ArtworkImages.Add(newArtworkImage);
context.SaveChanges();
由于參考不明確,我希望這段代碼失敗,我根本不會添加使用這一行,context.ArtworkImages.Add(newArtworkImage);而是依賴已經建立的物件參考。
我說可能有,因為我沒有明確測驗過,也不希望你這樣做,除非你準備好放棄你的存盤庫模式;)
與您的代碼觀察相關的一些次要觀點:
在Principal物體中,為Dependent物體預初始化集合很有幫助,這簡化了與物體圖初始化和反序列化相關的代碼。對表示集合而不是單個參考的屬性使用復數名稱也是一種標準約定:
public partial class Artwork { [Key] public int ArtworkId { get; set; } public string Identifier { get; set; } public string Category { get; set; } public ICollection<ArtworkImage> ArtworkImages { get; set; } = new HashSet<ArtworkImage>(); }這允許更簡單的初始化語法:
Artwork newArtwork = new Artwork(); newArtwork.Identifier = identifierCode; newArtwork.ArtworkImages.Add(new ArtworkImage { ImageURL = fileName }); newArtwork.ArtworkImages.Add(new ArtworkImage { ImageURL = fileName2 });您還可以實作較少冗余的命名約定,并從導航屬性名稱中洗掉Artwork 。關注關系的名稱或描述關系的動詞而不是型別,以使您的代碼更自然地閱讀:
Artwork newArtwork = new Artwork(); newArtwork.Identifier = identifierCode; newArtwork.Images.Add(new ArtworkImage { ImageURL = fileName }); newArtwork.Images.Add(new ArtworkImage { ImageURL = fileName2 });在Fluent 配置中,您不需要指定關系的兩端,這樣做是受支持的,但如果一端與它的相互配置沖突,可能會導致配置問題。一個可以提供幫助的策略是對所有配置都遵循一個約定,例如只定義來自依賴物體的關系,而不是主體,反之亦然。
如果您使用的是屬性表示法(Code First Data Annotations),那么根本不需要指定關系映射
OnModelCreating。使用屬性表示法來減少您需要維護的流暢配置。我嘗試了一些事情,例如添加
[ForeignKey("Artwork")]&[ForeignKey("ArtworkId")]以及擺弄我的構建器。屬性表示法的關鍵
ForeignKeyAttribute在于,如果您注釋外鍵欄位,則該屬性用于描述Navigation Property。如果它被放置在Navigation Property上,那么它描述了外鍵。這兩種配置中的任何一種都有效:[ForeignKey(nameof(ArtworkId))] public virtual Artwork Artwork { get; set; } public int ArtworkId { get; set; }或這個:
public virtual Artwork Artwork { get; set; } [ForeignKey(nameof(Artwork))] public int ArtworkId { get; set; }兩者都將導致您顯示的相同 EF 配置和遷移,并意味著您的構建器保持干凈:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { Database.SetInitializer<Artworkcontext>(null); base.OnModelCreating(modelBuilder); }
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/517085.html
上一篇:如何填充MVC資料模型?
