Причина, по которой вы хотите получить объект со свойством 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