繁体   English   中英

如何在实体框架中映射类型为“字典”的属性?

[英]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结合在一起是唯一的。 如果是EnPl ,请将其用作密钥。

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_VmWordindex_Key可以是任何东西。 它们只是用于命名索引列的标识符。

数字0和1给出索引的排序顺序:首先通过index_VmWordId然后通过index_Key

现在,如果你有一个VmWordId == 10,也可以添加VmWordLocationVmWordId == 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM