do tnet Entity Framework INSERT SELECT FROM из другой таблицы - PullRequest
0 голосов
/ 18 марта 2020

Я не специалист по базам данных и SQL. Я занимаюсь разработкой базового приложения do tnet с использованием Entity Framework для доступа к базе данных (postgresql). У меня есть следующие таблицы со следующими столбцами:

Table: Log
Columns:
  Timestamp: timestamp without time zone
  Value: smallint
  Status: smallint
Table: Status
  ID: smallint
  Name: character varying(30)

Когда я вставляю запись / строку в таблицу журнала, я хочу найти идентификатор, принадлежащий имени, в таблице состояния и заполнить значение в столбце Состояние в таблице журнала. Если Имя не найдено, я хочу вставить -1 в столбце Статус. Я реализовал это как:

Status status = null;
try {
  status = context.Status.Single(a => a.Name == statusName);
} catch {}
var entry = new Log {
  Timestamp = timestamp,
  Value = value,
  Status = (status != null) ? status.Id : Convert.ToInt16(-1)
};
context.Log.Add(entry);
context.SaveChanges();

Это работает. Я думаю, что это может быть реализовано более эффективно. Также есть некоторые проблемы с этой реализацией, я считаю. Например, когда я временно останавливаю базу данных, запрос идентификатора состояния завершается неудачно и выполняется вставка с -1. Когда база данных перезапускается, вставки все еще выполняются (я думаю, что Entity Framework обналичивает действия), но с -1 и неправильным значением. Когда вставка может быть выполнена как одно утверждение, я ожидаю / надеюсь, что будут вставлены правильные значения. Поэтому я хотел бы знать несколько вещей. Вот мои настоящие вопросы:

  1. Как я могу добиться этого, используя оператор SQL (в инструменте администрирования базы данных)?
  2. Как я могу реализовать его, используя Entity Framework так, что в базе данных выполняется один оператор SQL (и, таким образом, вставляются правильные данные, когда база данных временно закрыта)?

Ответы [ 2 ]

0 голосов
/ 19 марта 2020

Я нашел способ сделать один запрос sql, чтобы выполнить поиск и выполнить вставку. Мне (только) удалось реализовать это, используя ExecuteSqlRaw.

string sqlText = "INSERT INTO \"Log\" (\"Timestamp\", \"Value\", \"Status\") " +
                 "SELECT {0}, {1}, COALESCE(\"Stat\".\"ID\", -1) " +
                 "FROM \"Status\" AS \"Stat\" " +
                 "RIGHT JOIN (SELECT 'xxx' as CONSTANT) AS \"Dummy\" " +
                 "ON \"Stat\".\"Name\" = {2}";
int rows = 0;
try
{
  rows = context.Database.ExecuteSqlRaw(sqlText, timestamp, value, status);
  if (rows != 1)
  {
    Console.WriteLine("Error writing status log to database: rows returned is {0}", rows);
  }
}
catch (Exception ex)
{
  Console.WriteLine("Error writing status log to database: {0}", ex.Message);
}

Хотя это работает, я пошел к другому решению. Поскольку таблица «Состояние» не содержит большого списка, я загружаю ее в словарь в памяти и ищу там идентификатор для каждой добавляемой записи.

statusValues = context.Status.ToDictionary(kvp => kvp.Name, kvp => kvp.Id);
0 голосов
/ 18 марта 2020

объявление 2) реализовать его как единый оператор не рекомендуется ; причина использует код, который должен быть запущен как asyn c. Лучше это два отдельных шага вот так:

// step 1: load data from DB
var status = await context.Status.SingleOrDefaultAsync(a => a.Name == statusName);
if(status == null)
{
   // warning or error
}

// step 2: create new one or more instances + Save back to DB
context.Log.Add(new Log
{
   Timestamp = timestamp,
   Value = value,
   Status = (status != null) ? status.Id : Convert.ToInt16(-1)
};
await context.SaveChangesAsync();
...