【问题标题】:dotnet Entity Framework INSERT SELECT FROM other tabledotnet 实体框架 INSERT SELECT FROM 其他表
【发布时间】:2020-06-29 08:56:47
【问题描述】:

我不是数据库和 SQL 方面的专家。我正在使用实体框架开发一个 dotnet 核心应用程序来访问(postgresql)数据库。 我有以下表格,其中包含以下列:

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

当我在 Log 表中插入一条记录/行时,我想在 Status 表中查找属于 Name 的 ID,并在 Log 表的 Status 列中填写值。如果找不到名称,我想在状态列中插入 -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();

这行得通。我认为它可以更有效地实施。我相信这个实现也存在一些问题。例如,当我临时停止数据库时,状态 id 的查询将失败,并执行带有 -1 的插入。重新启动数据库时,仍会执行插入(我认为实体框架会兑现操作),但使用的是 -1 而不是正确的值。当插入可以作为单个语句执行时,我期望/希望插入正确的值。 因此,我想知道一些事情。这些是我的实际问题:

  1. 如何使用 SQL 语句(在数据库管理工具上)实现此目的?
  2. 如何使用实体框架实现它,以便在数据库上执行单个 SQL 语句(从而在数据库临时关闭时插入正确的数据)?

【问题讨论】:

  • 这通常通过在数据库中创建一个存储过程来完成工作,然后调用该存储过程来实现

标签: sql .net postgresql .net-core entity-framework-core


【解决方案1】:

ad 2) 将其实现为单个语句不推荐;原因是使用代码,应该作为异步运行。最好像这样两个单独的步骤

// 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();

【讨论】:

  • 嗨马丁,我有点困惑。这“几乎”是我所拥有的。我没有从您的提案/解决方案中得到以下信息:您是否建议以不同于我的方式处理 SingleOrDefault 的结果?
  • 其次,尽管分两步完成可能是更好的实现方式。你“知道”我最初问题的答案吗? (我也在努力学习:))
  • 我的答案是 C# 语言中更好的方法,异步并删除 catch {},这在调试中应该是问题。如果您要求更有效的方式更快地使用 SQL DB 是的,它可以通过直接 SQL 命令作为原始语法、存储过程、来自 c# 的简单调用或可能作为批量操作来实现。
【解决方案2】:

我确实找到了一种方法来进行一个 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);
}

虽然这可行,但我还是选择了另一种解决方案。由于 Status 表没有很大的列表,因此我将表加载到内存中的字典中,并在其中查找必须添加的每条记录的 ID。

statusValues = context.Status.ToDictionary(kvp => kvp.Name, kvp => kvp.Id);

【讨论】:

    猜你喜欢
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多