Изящные параметризованные запросы с автоматически сгенерированными типами LINQ

В своей работе я использую комбинацию LINQ и Dapper. Я заменяю свой код LINQ на Dapper из соображений производительности. У меня есть много объектов данных LINQ, созданных путем перетаскивания на диаграмму базы данных Visual Studio из SQL Server.

В следующем примере у меня уже есть объект LINQ в памяти, и я хотел бы передать его Dapper в качестве параметров запроса. Например:

Animal animal = con.Query<Animal>(" select * " +
        " from animal " +
        " where animalid = @AnimalId " +
        " and animaltype = @AnimalType ",
        cagedAnimal).SingleOrDefault();

CagedAnimal содержит общедоступные свойства AnimalId и AnimalType с геттерами и сеттерами.

Однако при выполнении этого кода я получаю следующую ошибку:

Тип: SMDApp.Models.Animal не поддерживается dapper.

Следующий код работает:

Animal animal = con.Query<Animal>(" select * " +
            " from animal " +
            " where animalid = @AnimalId " +
            " and animaltype = @AnimalType ",
            new 
            { 
            AnimalId = cagedAnimal.AnimalId, 
            AnimalType = cagedAnimal.AnimalType 
            }
            ).SingleOrDefault();

Мне было бы удобнее использовать существующий объект, особенно когда я использую более одного свойства объекта в качестве параметра для запроса. Может ли кто-нибудь сказать мне, почему это работает для анонимного объекта, но не для автоматически сгенерированного объекта LINQ?

Отредактировано в ответ на ответ Бена Робинсона.

Отредактировано во второй раз в ответ на ответ Марка Гравелла.


person Giles Roberts    schedule 29.06.2011    source источник
comment
Вы уверены, что делаете "...", cagedAnimal) ? возможно, вы делаете "...", new { cagedAnimal }) вместо этого? в основном - это уже должно работать, хотя это немного расточительно, поскольку дополнительные параметры могут быть добавлены без необходимости/   -  person Marc Gravell    schedule 29.06.2011
comment
просто чтобы вы знали, что я только что выдвинул исправление, которое: а: игнорирует любые свойства, которые не используются в вашем запросе, и б: выдает гораздо лучшее сообщение об ошибке, когда оно действительно борется. Я не выполнял развертывание в nuget, но с новым кодом ваш пример выше должен работать нормально.   -  person Marc Gravell    schedule 29.06.2011


Ответы (2)


Краткая версия должна уже работать; на основании ошибки:

Тип: SMDApp.Models.CagedAnimal не поддерживается dapper

Я пришел к выводу, что либо вы на самом деле передаете new {cagedAnimal} вместо cagedAnimal, или ваш CagedAnimal имеет свойство (возможно, Parent?), которое само по себе является CagedAnimal и которого щеголеватый не может понять. Текущее поведение заключается в том, что параметр добавляется для каждого общедоступного свойства предоставленного объекта параметра, и если он не может понять, как отправить какое-либо из свойств в база данных, он жалуется. Вы обнаружите, что простой POCO с членами-значениями работает нормально.

Однако! Обратите внимание, что он никогда не пытается анализировать ваш SQL, в частности, он не проверяет параметры в предоставленном запросе. Таким образом, использование подхода POCO будет означать, что вы добавляете в запрос ненужные свойства.

Мы широко используем dapper, и мы просто используем подход:

 new { obj.Foo, obj.Bar, id, key = "something else" }
person Marc Gravell    schedule 29.06.2011
comment
Спасибо, Марк, это все. Я неправильно прочитал сообщение об ошибке. Дох! Вы все равно каким-то образом получили правильный ответ. Я отредактировал сообщение об ошибке в вопросе сейчас. Класс CagedAnimal имел свойство типа Animal, которое Даппер не понимал. - person Giles Roberts; 29.06.2011
comment
@Giles, мы планируем изменить сообщение об ошибке, включив в него имя свойства (/ параметра), которым оно подавляется, поэтому ваш вопрос привел к положительному изменению/результату; p - person Marc Gravell; 29.06.2011
comment
@Giles, мы также обсуждаем, следует ли предварительно фильтровать свойства, которые мы отправляем на основе команды, то есть отправлять param.foo только в том случае, если мы можем видеть @foo или :foo в тексте команды, что удалит проблема с ненужными параметрами - person Marc Gravell; 29.06.2011
comment
@Marc Марк Это дало мне название свойства, которым оно задыхалось. Просто мой мозг каким-то образом неправильно перевел его в CagedAnimal, потому что Animal был таким же, как тип результата оператора запроса. Было бы неплохо, если бы вы могли удалить ненужные параметры, поскольку я считаю, что это позволит работать моему исходному коду. Хотя явно не в ущерб производительности. Кстати, Dapper намного быстрее, чем LINQ, поэтому большое спасибо за его публикацию. - person Giles Roberts; 29.06.2011
comment
@Giles - версия в google-коде делает эту зачистку. И он не использовался для отображения имени члена, AFAIK - только имя type. Конечно, если это совпадение? - person Marc Gravell; 29.06.2011
comment
@ Джайлс, обязательно скачай последнюю версию и прочитай мой ответ, теперь эта проблема исправлена. - person Sam Saffron; 30.06.2011
comment
@ Сэм, ты был прав насчет того, что сопоставление строк / регулярное выражение сложно. В данном случае это не работает. @Marc IndexOf(@ + prop.Name, ... в строке 907 файла SqlMapper.cs соответствует свойству Animal из CagedAnimal по отношению к @AnimalId и @AnimalType в строке sql. Так что теперь выдается более информативное сообщение об ошибке. - person Giles Roberts; 30.06.2011
comment
@ Джайлз, да, я уже отмечал этот ложноположительный риск. Я планирую улучшить это позже - person Marc Gravell; 30.06.2011
comment
@ Джайлс ... можешь попробовать последнюю версию, я думаю, теперь она должна работать как положено - person Sam Saffron; 01.07.2011
comment
@Sam - Большое спасибо. Последние работы, как и ожидалось. Кстати, профилировщик тоже любит профилировщик из-за медленного дня при переполнении стека. - person Giles Roberts; 05.07.2011

Марк только что зафиксировал изменение в исправить эту проблему, в частности:

  1. Мы выполняем тривиальную проверку перед отправкой попытки перевести свойства в params. Например, Dapper не будет отправлять какие-либо параметры на сервер в этом случае: cnn.Query("select 1", new {bla = 1}) потому что "bla" не существует в строке. Эта проверка пропускается для хранимых процессов.

  2. Ошибка, которая была довольно загадочной, теперь исправлена ​​и значительно улучшена.

--

Раньше Dapper не выполнял синтаксический анализ базового оператора SQL, например:

@"select * 
from animal
where animalid = @AnimalId"

Содержит единственный параметр с именем @AnimalId.

Это становится сложной причиной, чтобы быть на 100% правильным, вам нужно обрабатывать крайние случаи, например: @AnimalId в строку select '@AnimalId' -- @AnimalId \* @AnimalId *\? Регулярное выражение становится немного сложным, я не продумал все крайние случаи. Например: Oracle ставит перед своими параметрами префикс :, что еще больше усложняет ситуацию.

Поскольку dapper ничего не знал о параметрах в строке, он решил отправить каждое общедоступное свойство в качестве параметра. Некоторые из ваших общедоступных свойств не могут быть сопоставлены с DbParameters, поэтому он жаловался.

person Sam Saffron    schedule 29.06.2011