Как мне сопоставить свойство типа «Словарь» с Entity Framework? - PullRequest
0 голосов
/ 15 октября 2018
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', поскольку оно имеет тип 'Dictionary'который не является поддерживаемым типом примитива или допустимым типом объекта.Либо явным образом сопоставьте это свойство, либо игнорируйте его, используя атрибут «[NotMapped]» или «EntityTypeBuilder.Ignore» в «OnModelCreating».

Как сопоставить свойство с типом «Словарь»

Ответы [ 3 ]

0 голосов
/ 15 октября 2018

Вопрос в том, что такое словарь и как вы видите это сопоставление с физической таблицей базы данных (хотите ли вы иметь таблицу со списком слов, а затем другую таблицу со списком слов, повторяемых для каждого перевода)?Я здесь «плюнул», так как не уверен, чего именно вы пытаетесь достичь, но вместо базового слова со ссылкой на перевод вы можете захотеть просто иметь таблицу с идентификатором слова и идентификатором языка.который составляет составной первичный ключ, тогда вы просто применили бы слова whereid = x и language = y, чтобы получить правильную версию слова.В качестве альтернативы, если вы настроены на свою структуру, вам нужно реализовать дочернюю таблицу с внешним ключом и определить ее примерно так в классе VmWord:

public List<VmWordLocalisation> {get;set;}

А затем в дочерней таблице вы определитевнешний ключ, например, такой:

[ForeignKey("Id")]
public VmWord Parent {get;set;}

Вам также потребуется настроить составной первичный ключ на дочерней таблице для определения идентификатора и языка

Надеюсь, это поможет!

0 голосов
/ 15 октября 2018

Причина, по которой вы хотите получить объект со свойством Dictionary, заключается в том, что вам нужен быстрый поиск: Give me the VmWord with the Localization with this Key.Другими словами: для данного ключа требуется быстрый поиск объекта VnWordLocalization, содержащего этот ключ.

В Entity Framework a DbSet<...> представляет таблицу в реляционной базе данных.Реляционные базы данных не знают концепцию Dictionary.

К счастью, база данных знает концепцию быстрого поиска с использованием ключа.Обычно это первичный ключ, но вы можете добавить другие индексы, которые можно использовать в качестве ключа.

Увы, вы забыли сообщить нам, какую строку вы хотите использовать для быстрого поиска для 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;}
}

Примечание: если ключ в целом уникален, рассмотрите возможность его использования в качестве первичного ключа.

Примечание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 == "Hello".Но если вы попытаетесь добавить вторую 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

0 голосов
/ 15 октября 2018

Возможно иметь свойство Dictionary в EF.Решение состоит в том, чтобы заменить 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; }
    }
}
...