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'。
如何映射“字典”类型的属性?
答案 0 :(得分:0)
在EF中可能有Dictionary
属性。解决方案是将Dictionary
替换为ICollection
:
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; }
}
}
答案 1 :(得分:0)
问题是,字典是什么?如何看待与物理数据库表的映射(您要查找的是一个包含单词列表的表,然后是另一个包含单词列表的表,每次翻译重复一次) ?我在这里是“吐口水”,因为我不确定您要实现的目标是什么,但是您可能希望仅拥有一个包含单词ID和语言ID的表格,而不是使用带有翻译链接的基本单词它组成了一个复合主键,那么您只需应用wordid = x和language = y的位置即可获得该单词的正确版本。或者,如果您在结构上进行了设置,则需要使用外键实现子表并在VmWord类上对其进行如下定义:
public List<VmWordLocalisation> {get;set;}
然后在子表上,您将像这样定义外键
[ForeignKey("Id")]
public VmWord Parent {get;set;}
您还需要在子表上设置一个复合主键,以标识ID和语言
希望这会有所帮助!
答案 2 :(得分:0)
之所以想要具有Dictionary
属性的对象,是因为您想要快速查找:Give me the VmWord with the Localization with this Key
。换句话说:给定一个密钥,您需要快速查找包含该密钥的VnWordLocalization对象。
在实体框架中,DbSet<...>
代表关系数据库中的表。关系数据库不知道Dictionary
的概念。
幸运的是,数据库知道使用键进行快速查找的概念。通常,这是主键,但是您可以添加其他可用作键的索引。
A,您忘了告诉我们您要使用哪个字符串作为VmWordLocalizations
的快速查找。是En
吗?或Pl
?还是一个全新的钥匙?
如果它是一个全新的密钥,那么显然每个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:考虑坚持使用entity framework code-first conventions.,它消除了对属性或流畅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
== 10的Id
,则可以添加一个VmWordLocation
== 10和VmWordId
的{{1}} ==“你好”。但是,如果您尝试使用这些值添加第二个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。此后,您将无法再执行IQueryable LINQ语句