2009-01-30 11 views
8

Estoy utilizando Linq-to-SQL con un servidor SQL Server (por supuesto) como un ORM para un proyecto. Necesito obtener el conjunto de resultados de un procedimiento almacenado que regresa de una tabla creada dinámicamente. Esto es lo que ve el proc como:¿Cómo obtengo Linq to SQL para reconocer el conjunto de resultados de un Procedimiento almacenado dinámico?

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 ''%'[email protected]_author+'%''' 

if len(@l_title) > 0 
set @sql = @sql + ' and title like ''%'[email protected]_title+'%''' 

if len(@L_isbn) > 0 
set @sql = @sql + ' and thirteendigitisbn like ''%'[email protected]_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 

no he escrito este procedimiento, pero puede ser capaz de influir en un cambio si hay un problema muy serio.

Mi problema actual es que cuando agrego este procedimiento para mi modelo, el diseñador genera esta función:

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

que no se parece en nada el conjunto de resultados que consigo cuando corro el proc de forma manual:

Result Set

¿Puede alguien decirme lo que va mal aquí?

Esto es básicamente el mismo problema que this question pero debido a la mala redacción de la OP nunca fue realmente respondida.


Gracias Marc por su respuesta. Me ocuparé de hacer los cambios que sugirió.

El problema era la tabla temporal. Linq to Sql simplemente no sabe qué hacer con ellos. Esto fue particularmente difícil de diagnosticar, porque Visual Studio almacena en caché la información sobre los procesos almacenados, por lo que cuando inicialmente no pudo encontrar un conjunto de resultados estableció el retorno como un tipo de entero predeterminado y no se actualizó cuando realicé cambios en el proceso almacenado. Conseguir VS para reconocer un cambio requiere que:

  • Eliminar proc del dbml
  • eliminar la conexión del servidor desde el Explorador de servidores
  • guardar el dbml para forzar una recompilación
  • cerrar el proyecto y reiniciar VS
  • recrear la conexión con el servidor e importar el proc

puede que no tenga que hacer cada uno de esos pasos, pero tha Es lo que funcionó para mí. Lo que debe hacer, si debe usar una tabla temporal, es crear un proceso barebone que simplemente devuelva el esquema correcto y luego modificarlo para hacer lo que desee después de importarlo en el Diseñador OR.

Respuesta

5

Primero - IMPORTANTE - su SQL es vulnerable a la inyección; el comando interior se deben parametrizar:

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

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

Esta pasa el valor de @L_author en como el parámetro @author en el comando dinámico - la prevención de ataques de inyección.


En segundo lugar, realmente no necesita la tabla de temperatura. No está haciendo nada por ti ... simplemente INSERTAR y SELECCIONAR. ¿Tal vez solo EXEC y deje que los resultados fluyan a la persona que llama naturalmente?

En otras circunstancias, una variable de tabla sería más apropiada, pero esto no funciona con INSERT/EXEC.


¿Las columnas son las mismas para cada llamada? Si es así, escriba el dbml manualmente o use un SP temporal (solo con "WHERE 1 = 0" o algo así) para que el SET FMT_ONLY ON pueda funcionar.

Si no (diferentes columnas por uso), entonces no hay una respuesta fácil. Tal vez utilice ADO.NET regular en este caso (ExecuteReader/IDataReader - y quizás incluso DataTable.Fill).

Por supuesto, usted podría dejar de tomar la tensión LINQ ... (C#):

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

etc

+0

Los dos primeros puntos son importantes para esta pregunta en particular, pero en general creo que [SET FMTONLY ON] [http://msdn.microsoft.com/en-us/library/ms173839.aspx] es una parte central del problema cuando el código generado por LINQ to SQL no devuelve los resultados esperados. En algunos casos lo he configurado en OFF [http://www.fishofprey.com/2009/08/detecting-when-nettiers-is.html] cuando sé que el proceso almacenado no tendrá ningún efecto sobre los datos. Es decir. Solo selecciona. –

5

No hay una manera muy fácil de hacerlo. He tenido el mismo problema en el pasado. Creo que el problema es que Linq to Sql no tiene forma de "descifrar" qué tipo se devolverá ya que está compilando la instrucción SELECT en el momento de la ejecución. Lo que hice para evitar esto, fue en el proceso almacenado, hice solo una selección y seleccioné todas las columnas que posiblemente necesitaba. Entonces, tuve Linq a Sql generar la función basada en eso. Luego, volví a SQL y cambié el proceso almacenado a la forma en que se supone que debe ser. El truco aquí no es regenerar tu DBML.

Cuestiones relacionadas