如何让Linq to SQL识别动态存储过程的结果集?

我正在使用Linq-to-SQL和SQL Server后端(当然)作为项目的ORM。 我需要从一个从动态创建的表返回的存储过程中获取结果集。 这是proc的样子:

CREATE procedure [RetailAdmin].[TitleSearch] ( @isbn varchar(50), @author varchar(50), @title varchar(50)) as declare @L_isbn varchar(50) declare @l_author varchar(50) declare @l_title varchar(50) declare @sql nvarchar(4000) set @L_isbn = rtrim(ltrim(@isbn)) set @l_author = rtrim(ltrim(@author)) set @l_title = rtrim(ltrim(@title)) CREATE TABLE #mytemp( [storeid] int not NULL, [Author] [varchar](100) NULL, [Title] [varchar](400) NULL, [ISBN] [varchar](50) NULL, [Imprint] [varchar](255) NULL, [Edition] [varchar](255) NULL, [Copyright] [varchar](100) NULL, [stockonhand] [int] NULL ) set @sql = 'select a.storeid, Author,Title, thirteendigitisbn ISBN, Imprint,Edition,Copyright ,b.stockonhand from ods.items a join ods.inventory b on a.itemkey = b.itemkey where b.stockonhand  0 ' if len(@l_author) > 0 set @sql = @sql + ' and author like ''%'+@L_author+'%''' if len(@l_title) > 0 set @sql = @sql + ' and title like ''%'+@l_title+'%''' if len(@L_isbn) > 0 set @sql = @sql + ' and thirteendigitisbn like ''%'+@L_isbn+'%''' print @sql if len(@l_author)  0 or len(@l_title)  0 or len(@L_isbn)  0 begin insert into #mytemp EXECUTE sp_executesql @sql end select * from #mytemp drop table #mytemp 

我没有写这个程序,但是如果有一个非常严重的问题,可能会影响一个改变。

我目前的问题是,当我将此过程添加到我的模型时,设计器生成此函数:

 [Function(Name="RetailAdmin.TitleSearch")] public int TitleSearch([Parameter(DbType="VarChar(50)")] string isbn, [Parameter(DbType="VarChar(50)")] string author, [Parameter(DbType="VarChar(50)")] string title) { IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), isbn, author, title); return ((int)(result.ReturnValue)); } 

这看起来不像我手动运行proc时得到的结果集:

结果集

谁能告诉我这里出了什么问题?

这与这个问题基本上是同一个问题,但由于OP的措辞不佳,它从未得到过真正的回答。


谢谢Marc的回复。 我将看到你提出的改变。

问题是临时表。 Linq到Sql只是不知道如何处理它们。 这特别难以诊断,因为Visual Studio会缓存有关存储过程的信息,所以当它最初找不到结果集时,它会将返回值设置为默认的整数类型,并且在我对存储过程进行更改时不会更新。 让VS识别更改需要您:

  • 从dbml中删除proc
  • 从Server Explorer中删除服务器连接
  • 保存dbml以强制重新编译
  • 关闭项目并重启VS.
  • 重新创建服务器连接并导入proc

您可能不必执行所有这些步骤,但这对我有用。 如果必须使用临时表,则需要做的是创建一个简单返回正确模式的准系统proc,然后在将其导入OR Designer后将其更改为执行所需操作。

首先 – 重要 – 您的SQL易受注入攻击; 内部命令应该参数化:

 if len(@l_author) > 0 set @sql = @sql + ' and author like ''%''+@author+''%''' EXECUTE sp_executesql @sql, N'@author varchar(100)', @L_author 

这会将@L_author的值作为动态命令中的@author参数@L_author – 防止注入攻击。


第二 – 你真的不需要临时表。 它没有为你做任何事……你只是INSERT和SELECT。 也许只是EXEC并让结果自然地流向来电者?

在其他情况下,表变量更合适,但这不适用于INSERT / EXEC。


每次通话的列是否相同? 如果是这样,要么手动编写dbml,要么使用临时SP(只有“WHERE 1 = 0”或其他东西),以便SET FMT_ONLY ON可以工作。

如果不是(每次使用不同的列),那么就没有一个简单的答案。 也许在这种情况下使用常规ADO.NET( ExecuteReader / IDataReader – 甚至可能是DataTable.Fill )。

当然,你可以让LINQ承受压力……(C#):

 ... if(!string.IsNullOrEmpty(author)) { query = query.Where(row => row.Author.Contains(author)); } ... 

等等

没有真正简单的方法可以做到这一点。 我过去也遇到过同样的问题。 我认为问题是Linq to Sql无法“搞清楚”因为你在执行时构建SELECT语句将返回哪种类型。 我做了什么来解决这个问题,在存储过程中,我做了一个选择并选择了我可能需要的所有列。 然后,我让Linq to Sql生成基于此的函数。 然后,我回到SQL并将存储过程恢复到它应该的方式。 这里的诀窍是不重新生成DBML。