Как заставить Linq to SQL распознавать результирующий набор динамической хранимой процедуры? - PullRequest
8 голосов
/ 30 января 2009

Я использую Linq-to-SQL с бэкэндом SQL Server (конечно) в качестве ORM для проекта. Мне нужно получить набор результатов из хранимой процедуры, которая возвращается из динамически созданной таблицы. Вот как выглядит процесс:

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

, который не похож на набор результатов, который я получаю, когда запускаю процедуру вручную:

Result Set

Кто-нибудь может мне сказать, что здесь происходит не так?

По сути, это та же проблема, что и в этом вопросе , но из-за плохой формулировки из ОП на нее так и не был получен ответ.


Спасибо Марк за ваш ответ. Я посмотрю о внесенных вами изменениях.

Проблема была во временной таблице. Linq to Sql просто не знает, что с ними делать. Это было особенно трудно диагностировать, потому что Visual Studio кэширует информацию о сохраненных процессах, поэтому, когда изначально не удалось найти набор результатов, он устанавливает возврат как целочисленный тип по умолчанию и не обновляется, когда я вносил изменения в сохраненный процесс. Чтобы VS узнал изменение, вам необходимо:

  • Удалить proc из базы данных
  • удалить соединение с сервером из Server Explorer
  • сохранить dbml для принудительной перекомпиляции
  • закрыть проект и перезапустить VS
  • воссоздать соединение с сервером и импортировать процесс

Возможно, вам не придется выполнять каждый из этих шагов, но это то, что сработало для меня. Если вам необходимо использовать временную таблицу, вам нужно создать пустой процесс, который просто возвращает правильную схему, а затем изменить его так, чтобы он делал то, что вы хотите, после того как вы импортировали его в OR Designer.

Ответы [ 2 ]

5 голосов
/ 31 января 2009

Первый - ВАЖНО - ваш 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 в динамической команде, предотвращая атаки с помощью инъекций.


Второе - вам не нужна временная таблица. Это ничего не делает для вас ... просто вставьте и выберите. Может быть, просто 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));
}
...

и т.д.

5 голосов
/ 30 января 2009

Нет простого способа сделать это. У меня была такая же проблема в прошлом. Я думаю, что проблема в том, что Linq to Sql не может «выяснить», какой тип будет возвращен, так как вы создаете инструкцию SELECT во время выполнения. То, что я сделал, чтобы обойти это, было в сохраненном процессе, я просто выбрал и выбрал все столбцы, которые мне, возможно, понадобились. Затем я заставил Linq to Sql сгенерировать функцию на основе этого. Затем я вернулся к SQL и изменил сохраненный процесс обратно таким, каким он должен быть. Хитрость здесь не в том, чтобы восстановить ваш DBML.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...