已经有一个与此连接关联的打开的 DataReader,必须首先关闭它。

huangapple go评论83阅读模式
英文:

There is already an open DataReader associated with this Connection which must be closed first in rawsql sentences

问题

context.Database.ExecuteSql引发了标题异常。
我应该如何在.NET Core中使用原始SQL检索数据集并进行处理?

英文:

context.Database.ExecuteSql raise the title exception.
How must I use raw sql in .net core to retrieve a dataset and process it like ?

    public async Task<IActionResult> Refresh_Formatos()
    {            
        using (var context = _context)
        {
            var padre = context.Database.SqlQuery<int>(
                $@"SELECT id
                 FROM pds_etiquetas where nombre='FORMATOS' and tipo = 'Cat'
                 ").ToList();
            int padre_id = padre[0];
           
            var formatos = context.Database.SqlQuery<string>(
                $@"SELECT xdescripcion
                 FROM vc00_formatos a
                 left join pds_etiquetas c on a.xdescripcion=c.Nombre and {padre_id}=c.PadreId and c.tipo = 'Val'
                 where c.id is null
                 order by xdescripcion");
            context.Database.CloseConnection();
            foreach (var formato in formatos)
            {
                var affectedRows = context.Database.ExecuteSql(
                                    $@"INSERT INTO [imp].[pds_etiquetas]
                                           ([Nombre]
                                           ,[Tipo]
                                           ,[PadreId])
                                     VALUES
                                           ('{formato}'
                                           ,'Val'
                                           ,{padre_id})
                    ");
            }
        }                        
        return Ok();            
    } 

答案1

得分: 2

SqlQuery 返回一个 IEnumerable,当它被枚举时会执行查询。在关闭连接之后枚举它,所以它尝试在关闭连接后执行查询。

在关闭连接之前使用 ToList() 应该可以解决这个问题,因为它会在调用时枚举它。即:

var formatos = context.Database.SqlQuery<string>(
    $@"SELECT xdescripcion
       FROM vc00_formatos a
       left join pds_etiquetas c on a.xdescripcion=c.Nombre and {padre_id}=c.PadreId and c.tipo = 'Val'
       where c.id is null
       order by xdescripcion").ToList();
context.Database.CloseConnection();

更多信息请参考:Microsoft 文档

英文:

SqlQuery returns an IEnumerable that will execute the query when it is enumerated. You are enumerating it after you have closed the connection, so it is trying to execute the query after closing the connection.

Using ToList() before you close the connection should fix it as it will enumerate it when that is called. I.e.:

        var formatos = context.Database.SqlQuery&lt;string&gt;(
            $@&quot;SELECT xdescripcion
             FROM vc00_formatos a
             left join pds_etiquetas c on a.xdescripcion=c.Nombre and {padre_id}=c.PadreId and c.tipo = &#39;Val&#39;
             where c.id is null
             order by xdescripcion&quot;).ToList();
        context.Database.CloseConnection();

https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.database.sqlquery?view=entity-framework-6.2.0

答案2

得分: 1

你需要将formatos放入一个List中。

  • 您还应该正确参数化您的查询。
  • 您应该使用Async函数,但似乎旧的 EF 不提供SqlQueryAsync
public async Task<IActionResult> Refresh_Formatos()
{
    using (var context = _context)
    {
        var padre_id = context.Database.SqlQuery<int>(
            @"
            SELECT id
            FROM pds_etiquetas
            where nombre = 'FORMATOS'
            and tipo = 'Cat'
            ").First();

        var formatos = context.Database.SqlQuery<string>(
            @"
            SELECT xdescripcion
            FROM vc00_formatos a
            left join pds_etiquetas c on a.xdescripcion=c.Nombre and @padre_id = c.PadreId and c.tipo = 'Val'
            where c.id is null
            order by xdescripcion",
            new SqlParameter("@padre_id", SqlDbType.Int) { Value = padre_id }
        ).ToList();

        foreach (var formato in formatos)
        {
            var affectedRows = await context.Database.ExecuteSqlAsync(
                @"
                INSERT INTO imp.pds_etiquetas
                (Nombr, Tipo, PadreId)
                VALUES (@formato, 'Val', @padre_id)
                ",
                new SqlParameter("@padre_id", SqlDbType.Int) { Value = padre_id },
                new SqlParameter("@formato", SqlDbType.VarChar, 250) { Value = formato }
            );
        }
    }
    return Ok();
}

话虽如此,您不需要做任何这些,您可以只执行一个大的联接INSERT

您还可以将LEFT JOIN更改为NOT EXISTS,并且应该使用新的上下文而不是现有的一个。

public async Task<IActionResult> Refresh_Formatos()
{
    using var context = new YourContextHere();
    var affectedRows = await context.Database.ExecuteSqlAsync(@"
    INSERT INTO imp.pds_etiquetas
      (Nombr, Tipo, PadreId)
    SELECT
      a.xdescripcion,
      'Val',
      p.id
    FROM pds_etiquetas p
    CROSS JOIN vc00_formatos a
    where p.nombre = 'FORMATOS'
      and p.tipo = 'Cat'
      AND NOT EXISTS (SELECT 1
        FROM pds_etiquetas c
        WHERE a.xdescripcion = c.Nombre
          and p.id = c.PadreId
          and c.tipo = 'Val'
      );
    ");
    return Ok();
}
英文:

You need to put formatos into a List

  • You should also parameterize your queries properly
  • You should use Async functions, but it appears the old EF does not provide SqlQueryAsync.

public async Task&lt;IActionResult&gt; Refresh_Formatos()
{            
    using (var context = _context)
    {
        var padre_id = context.Database.SqlQuery&lt;int&gt;(
            @&quot;SELECT id
             FROM pds_etiquetas
             where nombre = &#39;FORMATOS&#39;
               and tipo = &#39;Cat&#39;
             &quot;).First();
       
        var formatos = context.Database.SqlQuery&lt;string&gt;(
            @&quot;SELECT xdescripcion
             FROM vc00_formatos a
             left join pds_etiquetas c on a.xdescripcion=c.Nombre and @padre_id = c.PadreId and c.tipo = &#39;Val&#39;
             where c.id is null
             order by xdescripcion&quot;,
           new SqlParameter(&quot;@padre_id&quot;, SqlDbType.Int) { Value = padre_id }
           ).ToList();

        foreach (var formato in formatos)
        {
            var affectedRows = await context.Database.ExecuteSqlAsync(
                @&quot;INSERT INTO imp.pds_etiquetas
                      (Nombr, Tipo, PadreId)
                  VALUES (@formato, &#39;Val&#39;, @padre_id)
                &quot;,
                new SqlParameter(&quot;@padre_id&quot;, SqlDbType.Int) { Value = padre_id },
                new SqlParameter(&quot;@formato&quot;, SqlDbType.VarChar, 250) { Value = formato }
           );
        }
    }                        
    return Ok();            
} 

Having said that, you don't need to do any of this, you can just do one big joined INSERT.

You can also change the LEFT JOIN to a NOT EXISTS, and you should use a new context rather than an existing one.

public async Task&lt;IActionResult&gt; Refresh_Formatos()
{            
    using var context = new YourContextHere();
    var affectedRows = await context.Database.ExecuteSqlAsync(@&quot;
INSERT INTO imp.pds_etiquetas
  (Nombr, Tipo, PadreId)
SELECT
  a.xdescripcion,
  &#39;Val&#39;,
  p.id
FROM pds_etiquetas p
CROSS JOIN vc00_formatos a
where p.nombre = &#39;FORMATOS&#39;
  and P.tipo = &#39;Cat&#39;
  AND NOT EXISTS (SELECT 1
    FROM pds_etiquetas c
    WHERE a.xdescripcion = c.Nombre
      and p.id = c.PadreId
      and c.tipo = &#39;Val&#39;
);
  &quot;);
    return Ok();            
}

huangapple
  • 本文由 发表于 2023年6月19日 22:12:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76507450.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定