Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get Linq to SQL to recognize the result set of a dynamic Stored Procedure?

I'm using Linq-to-SQL with a SQL Server backend (of course) as an ORM for a project. I need to get the result set from a stored procedure that returns from a dynamically-created table. Here's what the proc looks like:

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

I didn't write this procedure, but may be able to influence a change if there's a really serious problem.

My present problem is that when I add this procedure to my model, the designer generates this function:

[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));
}

which doesn't look anything like the result set I get when I run the proc manually:

Result Set

Can anybody tell me what's going wrong here?

This is basically the same problem as this question but due to the poor phrasing from the OP it was never really answered.


Thanks Marc for your reply. I will see about making the changes you suggested.

The problem was the temp table. Linq to Sql just doesn't know what to do with them. This was particularly difficult to diagnose, because Visual Studio caches information about stored procs, so when it initially failed to find a result set it set the return as a default integer type and didn't update when I made changes to the stored proc. Getting VS to recognize a change requires you to:

  • Delete proc from the dbml
  • delete the server connection from Server Explorer
  • save the dbml to force a recompile
  • close the project and restart VS
  • recreate the server connection and import the proc

You might not have to do every one of those steps, but that's what worked for me. What you need to do, if you must use a temp table, is to create a barebones proc that simply returns the correct schema, and then alter it to do what you want after you've imported it into the OR Designer.

like image 376
Adam Lassek Avatar asked Jan 30 '09 16:01

Adam Lassek


People also ask

How does a LINQ query transform to a SQL query?

LINQ to SQL translates the queries you write into equivalent SQL queries and sends them to the server for processing. More specifically, your application uses the LINQ to SQL API to request query execution. The LINQ to SQL provider then transforms the query into SQL text and delegates execution to the ADO provider.

Can we use LINQ with stored procedure?

Language-Integrated Query (LINQ) makes it easy to access database information, including database objects such as stored procedures. The following example shows how to create an application that calls a stored procedure in a SQL Server database.

How does LINQ to SQL work?

In LINQ to SQL, the data model of a relational database is mapped to an object model expressed in the programming language of the developer. When the application runs, LINQ to SQL translates into SQL the language-integrated queries in the object model and sends them to the database for execution.


1 Answers

First - IMPORTANT - your SQL is vulnerable to injection; the inner command should be parameterized:

if len(@l_author) > 0
set @sql =  @sql + ' and author like ''%''+@author+''%'''

EXECUTE sp_executesql  @sql, N'@author varchar(100)', @L_author

This passes the value of @L_author in as the @author parameter in the dynamic command - preventing injection attacks.


Second - you don't really need the temp table. It isn't doing anything for you... you just INSERT and SELECT. Perhaps just EXEC and let the results flow to the caller naturally?

In other circumstances a table-variable would be more appropriate, but this doesn't work with INSERT/EXEC.


Are the columns the same for every call? If so, either write the dbml manually, or use a temp SP (just with "WHERE 1=0" or something) so that the SET FMT_ONLY ON can work.

If not (different columns per usage), then there isn't an easy answer. Perhaps use regular ADO.NET in this case (ExecuteReader/IDataReader - and perhaps even DataTable.Fill).

Of course, you could let LINQ take the strain... (C#):

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

etc

like image 134
Marc Gravell Avatar answered Nov 15 '22 15:11

Marc Gravell