[英]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.