Основные объекты EF имеют свойство общего типа - PullRequest
1 голос
/ 29 мая 2019

У меня есть следующая схема (сокращенно соответствующий код)

class Person
{
  public int Id {get;set;}
  public string Name {get;set;}
  public ICollection<MetaData> MetaData {get;set;}
}
class Car
{
  public int Id {get;set;}
  public string Make {get;set;}
  public ICollection<MetaData> MetaData {get;set;}
}
class Building
{
  public int Id {get;set;}
  public string Address {get;set;}
  public ICollection<MetaData> MetaData {get;set;}
}
class MetaData
{
  public int Id {get;set;}
  public string CLRType {get;set;}
  public string Value {get;set;}

  // These two fields together form a connection to the other entities
  public int Key {get;set;} // the PK of the connected entity
  public string Type {get;set;} // nameof(Entity)
}

Как я могу представить это в EF Core?

Используя OwnsMany создать таблицу для каждого типа + тип метаданныхсоединение Если я пытаюсь использовать TPH, то таблица метаданных заканчивается дополнительными полями для каждого подключенного объекта

Ни одно из этих решений не идеально подходит для меня.Мне не нужны ограничения в базе данных, я просто хочу выразить это отношение таким образом, чтобы EF Core мог гидрировать мои модели, даже если мне придется предоставлять sql вручную (даже для чтения и записи), чтобыло бы работоспособным

Если это невозможно с существующими API, есть ли способ написать расширения самостоятельно, которые будут делать то, что мне нужно?

1 Ответ

1 голос
/ 29 мая 2019

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

class MetaData
{
  public int Id {get;set;}
  public string CLRType {get;set;}
  public string Value {get;set;}
  public int PersonId {get;set;}
  public Person Person {get;set;}
  public int CarId {get;set;}
  public Car Car {get;set;}
  public int BuildingId {get;set;}
  public Person Building {get;set;}
}

Или оставить его слабо связанным и выполнять соединения вручную

class Car
{
  public int Id {get;set;}
  public string Make {get;set;}
}

// not mapped in the database, just used for display purposes
class CarViewModel 
{
  public int Id {get;set;}
  public string Make {get;set;}
}




var carIds = new [] { 1, 2, 3 };

// we use 2 contexts here so we can query both tables at the same time.
// otherwise Task.WhenAll(...) would throw an exception.
// you should also disable change tracking to improve speed.
var carsTask = context1.Cars
    .Where(c => carIds.Contains(c.Id))
    .ToListAsync();
var metadataTask = context2.Metadata
    .Where(m => carIds.Contains(m.Key) && m.Type == "Car")
    .GroupBy(m => m.Key)
    .ToDictionaryAsync(g => g.Key, g => g.ToList());

await Task.WhenAll(carsTask, metadataTask).ConfigureAwait(false);

var cars = carsTask.Result
    .Select(c => new CarViewModel
    {
        Id = c.Id,
        Make = c.Make,
        Metadata = metadataTask.Result.TryGetValue(c.Id, out var m) ? m : Array.Empty<Metadata>(),
    })
    .ToList();

Или иметь отдельные таблицы для метаданных

abstract class MetaData
{
  public int Id {get;set;}
  public string CLRType {get;set;}
  public string Value {get;set;}
}

class CarMetaData : MetaData
{
  public int CarId {get;set;}
  public Car Car {get;set;}
}

class Car
{
  public int Id {get;set;}
  public string Make {get;set;}
  public ICollection<CarMetaData> MetaData {get;set;}
}

Какая версия подходит вам больше всего, в зависимости от ваших потребностей и потребностей вашего бизнеса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...