[英]How do I map property with of type 'Dictionary' with Entity Framework?
using Microsoft.EntityFrameworkCore;
namespace NameSpaceName
{
public class WordContext : DbContext
{
public DbSet<VmWord> Words { get; set; }
public DbSet<VmWordLocalization> WordLocalization { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=NameSpaceName.db");
}
}
}
namespace NameSpaceName
{
public class VmWord
{
public int Id { get; set; }
public string Name { get; set; }
public Dictionary<string, VmWordLocalization> Localization { get; set; }
}
}
namespace NameSpaceName
{
public class VmWordLocalization
{
public int Id { get; set; }
public string En { get; set; }
public string Pl { get; set; }
}
}
我有VmWord表,我需要添加VmWordLocalization作為字典,以便能夠通過鍵獲取本地化變量值;
當我嘗試執行時:
dotnet ef遷移添加Add_Localization_Table --context WordContext
我得到:
無法映射屬性“ VmWord.Localization”,因為它的類型為“字典”,不是受支持的原始類型或有效的實體類型。 顯式映射此屬性,或使用'[NotMapped]'屬性或'OnModelCreating'中的'EntityTypeBuilder.Ignore'忽略它。
如何映射“字典”類型的屬性?
EF中可能沒有Dictionary
屬性。 解決方案是用ICollection
替換Dictionary
:
namespace NameSpaceName
{
public class VmWord
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<VmWordLocalization> Localizations { get; set; }
}
public class VmWordLocalization
{
public int Id { get; set; }
public string Key { get; set; } // key from your previous dictionary
public string En { get; set; }
public string Pl { get; set; }
}
}
問題是,字典是什么?如何看待與物理數據庫表的映射(您要查找的是一個包含單詞列表的表,然后是另一個包含單詞列表的表,每次翻譯重復一次)? 我在這里是“吐口水”,因為我不確定您要實現的目標是什么,但是您可能希望僅擁有一個包含單詞ID和語言ID的表格,而不是使用帶有翻譯鏈接的基本單詞它組成了一個復合主鍵,那么您只需應用wordid = x和language = y的位置即可獲得該單詞的正確版本。 或者,如果您在結構上進行了設置,則需要使用外鍵實現子表,並在VmWord類上對其進行定義,如下所示:
public List<VmWordLocalisation> {get;set;}
然后在子表上您將定義外鍵,如下所示
[ForeignKey("Id")]
public VmWord Parent {get;set;}
您還需要在子表上設置一個復合主鍵,以標識ID和語言
希望這可以幫助!
您想要一個具有Dictionary
屬性的對象的原因是因為您想要快速查找: Give me the VmWord with the Localization with this Key
。 換句話說:給定一個鍵,您需要快速查找包含該鍵的VnWordLocalization對象。
在實體框架中, DbSet<...>
表示關系數據庫中的表。 關系數據庫不了解Dictionary
的概念。
幸運的是,數據庫知道使用鍵進行快速查找的概念。 通常,這是主鍵,但是您可以添加其他可用作鍵的索引。
las,您忘了告訴我們您要使用哪個字符串作為VmWordLocalizations
快速查找。 是En
嗎? 還是Pl
? 還是一個全新的鑰匙?
如果它是一個全新的Key,那么顯然每個VmWordLocalication
都有一個屬性,讓我們將其命名為Key
,它與外鍵VmWordId
結合在一起是唯一的。 如果是En
或Pl
,請將其用作密鑰。
class VmWord
{
public int Id { get; set; }
// every VmWord has zero or more Localizations (one-to-many)
public virtual ICollection<VmWordLocalization> VmWordLocalizations { get; set; }
... // other properties
}
class VmWordLocalization
{
public int Id { get; set; }
// every VmWordLocalization belongs to exactly one VmWord using foreign key
public int VmWordId {get; set;}
public virtual VmWord VmWord {get; set;}
// every VmWordLocalization has a property Key, which is unique in combination
// with VmWordId
public string Key {get; set;}
}
注意:如果Key是整體唯一的,請考慮將其用作主鍵。
注意2:在實體框架中,非虛擬屬性表示表中的列,表之間的關系由虛擬屬性表示
注意3:請考慮遵守實體框架的代碼優先約定。 它消除了對屬性或流暢的API的需求。 僅當您有充分理由時才偏離這些約定。 不必鍵入更少的字符不是一個很好的理由。
如果Key
不能作為您的主鍵,則必須添加一個額外的快速查找:索引。 這是在DbContext.OnModelCreating
完成的
protected overrid void OnModelCreating(...)
{
// Every VmWordLocalization has a property Key, which is unique per VmWordId
// create an index with (VmWordId, Key) as index key
var vmWordLocalizationEntity = modelBuilder.Entity<VmWordLocalization>();
// first index annotation: VmWordId:
vmWordLocalizationEntity.Property(entity => entity.VmWordId)
.IsRequired()
.HasColumnAnnotatin(IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("index_VmWordId", 0)));
// 2nd index annotation: Key:
vmWordLocalizationEntity.Property(entity => entity.Key)
.IsRequired()
.HasColumnAnnotatin(IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("index_Key", 1)));
}
標識符index_VmWord
和index_Key
可以是任何東西。 它們只是用於命名索引列的標識符。
數字0和1給出索引的排序順序:首先通過index_VmWordId
然后通過index_Key
。
現在,如果你有一個VmWord
與Id
== 10,也可以添加VmWordLocation
與VmWordId
== 10和Key
==“你好”。 但是,如果您嘗試使用這些值添加第二個VmWordLocation
,則會出現異常,就像將兩個具有相同鍵的對象添加到字典中一樣。
現在,在字典中獲取所請求的VmWords
及其本地化信息:
var vmWordsWithLocalizations = myDbContext.VmWords.Select(vmWord => new
{
// select only the properties you plan to use:
Id = vmWord.Id,
Name = vmWord.Name,
Localizations.VmWord.VmwordLocalizations
.Where(vmWordLocalization => ...) // only if you do't want all vmWordLocalizations
.ToDictionary(vmWordLocalization => vmWordLocalization.Key, // Dictionary Key
new // Dictionary value
{ // again: select only the properties you plan to use
Id = vmWordLocalization.Id,
En = vmWordLocalization.Em,
Pl = vmWordLocalization.Pl,
// no need: you know it equals vmWord.Id
// VmWordId = vmWordLocalization.VmWordId
}),
});
如果您必須經常執行此操作,請考慮創建擴展功能:
IEnumerable<VmWordWithDictionary> ToVmWordWithDictionary(this IQueryable<VmWord> vmWords)
{
return vmWords.Select(vmWord => new VmWordEx()
{
Id = vmWord.Id,
Name = vmWord.Name,
Localizations = vmWord.VmWordLocalizatons
.Where(vmWordLocalization => ...)
.ToDictionary(
vmWordLocalization => vmWordLocalization.Key // Key
vmWordLocalization => new VmWordLocalizationEx() // Value
{
Id = vmWordLocalization.Id,
En = vmWordLocalization.Em,
Pl = vmWordLocalization.Pl,
}),
});
注意:出於效率的考慮,我不想傳輸更多的數據,因此我創建了兩個額外的類,僅包含我需要的數據。
注意:由於字典的原因,返回必須是IEnumerable。 之后,您將無法再執行IQueryable LINQ語句
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.