#c# #sql-server
Вопрос:
Я преобразую запрос C# linq в SQL, LINQ никогда не возвращает значения.
Но тот же запрос, который я написал в SQL, возвращает некоторые значения. Может ли кто-нибудь помочь мне выяснить, в чем проблема с моим SQL-запросом?
Запрос LINQ:
var query = from l in _DbContext.Licenses
join lp in _DbContext.LicenseParts on l.PartNumber equals lp.PartNumber
join lpc in _DbContext.LicensePartConfigurations on lp.Id equals lpc.LicensePartId
join p in _DbContext.Products on lp.ProductId equals p.Id
join lsn in _DbContext.LicenseSerialNumbers on l.Id equals lsn.LicenseId
join lact in _DbContext.LicenseActivations on new { a = lsn.Id, b = lp.ProductId } equals new { a = lact.LicenseSerialNumberId, b = lact.ProductId }
where lact.AccountId == AccountId amp;amp; JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.SubscriptionKey") !=
" " amp;amp; (JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.IsConverted") == null || JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.IsConverted") == "0" || JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.IsConverted") == "false") amp;amp; p.Name == "ClearPass Legacy"
select new SubscriptionKeys { SubscriptionKey = JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.SubscriptionKey"), CustomerMail = JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.CustomerMail"), CustomerName = JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.CustomerName") };
response.PageSize = pageSize;
response.PageNumber = pageNumber;
response.Model = await query.Distinct().ToListAsync();
response.ItemsCount = response.Model.Count();
SQL-запрос:
SELECT
l.AccountId,CustomerMail,
JSON_VALUE(ActivationInfo, '$.SubscriptionKey')
FROM
Licenses l
JOIN
LicenseParts lp ON l.PartNumber = lp.PartNumber
JOIN
LicensePartConfigurations lpc ON lp.Id = lpc.LicensePartId
JOIN
Products p ON lp.ProductId = p.Id
JOIN
LicenseSerialNumbers lsn ON l.Id = lsn.LicenseId
JOIN
LicenseActivations lact ON lsn.Id = lact.LicenseSerialNumberId
AND lp.ProductId = lact.ProductId
WHERE
lact.AccountId = 'QWNjb3VudDoxNTMwNDAzMi00MWM2LTExZTktOWYzMy1kMzQxZjE5OWZlYjM='
AND JSON_VALUE(lact.ActivationInfo, '$.SubscriptionKey') != ' '
AND (JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = NULL OR
JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = 0 OR
JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = 'false')
AND p.Name = 'ClearPass Legacy'
Комментарии:
1. EF Core-это ORM, а не замена SQL. Он использует сущности и классы, а не объекты. Вам вообще не нужно использовать объединения, ORM будет генерировать объединения по мере необходимости на основе отношений между сущностями. Вы не можете использовать в LINQ-to-Сущностях что-либо, что не сопоставляется непосредственно с SQL. Это означает, что вы не можете использовать какие-либо функции, если нет прямого сопоставления с SQL. Что бы это ни
JsonExtensions
было, это не одно из них.2. Только несколько очень распространенных методов, таких как
string.Contains
илиString.StartsWith
, отображаются напрямую. В остальном вам нужно использовать методы в dbфункциях . Функций, связанных с Json, не существует3.@PanagiotisKanavos Большинство СУБД сегодня поддерживают JSON-in-SQL, так что это вполне
JsonExtensions
могут быть допустимые[DbFunction]
методы. Просто говорю…4. Вы можете включить ведение журнала и ведение журнала конфиденциальных данных, после чего вы сможете сравнить сгенерированные выходные данные SQL с требуемыми.
5.Не делай этого!
JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = NULL
— сравнение сNULL
в SQL должно выполняться только с помощьюIS NULL
илиIS NOT NULL
— сравнение с оператором equal не будет работать !
Ответ №1:
Чтобы начать с допустимой точки, выполните код, в котором он запускает этот запрос linq, и используйте профилировщик sql, чтобы выполнить его. Это хороший способ найти точный эквивалент sql-оператора, который он в конечном итоге создает и выполняет. Вам необходимо настроить трассировку в профилировщик sql перед выполнением linq. Получите инструкцию, а затем вы сможете сравнить ее с уже имеющимся sql.
этот sql:
(JSON_VALUE(lact.ActivationInfo, '$.IsConverted') = NULL
не равно:
(JsonExtensions.JsonValue(lact.ActivationInfoJSON, "$.IsConverted") == null
как и в первом случае, вы сравниваете значение NULL с базой данных с помощью»=», и для правильной работы требуется, чтобы ansi_nulls был отключен, и это не очень хорошая практика.
Комментарии:
1. Это неверно.
NULL
не равно ничему, поэтому условие SQL никогда не будет соответствовать ниNULL
одному s. Условие LINQ-to-Entitiesx.Prop == null
будет преобразовано вProp IS NULL
само EF. Нет причин использовать DBNull. Ценность. Который использовался в ADO.NET до того, как были введены типы, допускающие обнуление2. Панайотис Канавос зависит от параметра ansi_nulls. Это очень хорошо сочетается. Проверьте это сами: создайте таблицу #test_null(id int) вставьте в значения #test_null(1) вставьте в значения #test_null(null) установите значение ansi_null выключено выберите * из #test_null, где id = null установите значение ansi_null на выбор * из #test_null, где id = null отбросьте таблицу #test_null
3. Да, если вы неправильно используете базу данных, вы можете многое сделать. Хотя это скорее ограничивает карьеру. И причина для увольнения. И отличный способ провалить каждый вопрос SQL — интервью. Просто потому, что некоторые системы неправильно использовали нулевые настройки 30 лет назад, не означает, что есть какие-либо причины продолжать это делать. Я не могу не подчеркнуть, насколько плоха эта идея. Это
wrapping-a-fuse-in-aluminum-foil
довольно плохо4. Конечно, но это не вопрос ваших или моих предпочтений, отраслевых стандартов или чего — то еще… поскольку опубликованный SQL был «= NULL», указывает, что настройки, вероятно, не являются ansi.
5. На самом деле, это стандарт.
= NULL
это ошибка, простая и понятная. Такой код обычно встречается в задаваемых вопросахWhy can't I find nulls
? Допустим, что говоритьit's a preference
в субботу SQL, SQL meetup или .NET meetup было бы не очень хорошо.
Ответ №2:
ORM, подобные EF Core, предназначены для сопоставления объектов с реляционными конструкциями. Вместо того, чтобы пытаться писать SQL на C# через LINQ, вам следует попытаться сопоставить необходимые атрибуты со свойствами сущности.
В этом случае поля SubscriptionKey
и IsConverted
должны отображаться в самой таблице либо как правильные поля, либо как вычисляемые столбцы. Если это невозможно, вы можете использовать вычисляемые столбцы для сопоставления атрибутов SubscriptionKey
и IsConverted
со свойствами сущностей, чтобы использовать их в запросах.
В свой LicenseActivation
класс добавьте эти свойства:
public bool? IsConverted {get;set;}
public string? SubscriptionKey {get;set;}
public string? CustomerEmail {get;set;}
В вашем DbContext вы можете указать вычисляемые столбцы с HasComputedColumnSql
:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<LicenseActivation>()
.Property(b => b.SubscriptionKey)
.HasComputedColumnSql("JSON_VALUE(ActivationInfo, '$.SubscriptionKey')");
modelBuilder.Entity< LicenseActivations >()
.Property(b => b.IsConverted)
.HasComputedColumnSql("JSON_VALUE(ActivationInfo, '$.IsConverted')");
modelBuilder.Entity< LicenseActivations >()
.Property(b => b.CustomerEmail)
.HasComputedColumnSql("JSON_VALUE(ActivationInfo, '$.CustomerEmail')");
}
Это позволит вам использовать свойства в запросах LINQ.
Вам также не нужно использовать все эти соединения. Задача ORM заключается в создании соединений на основе отношений между объектами. Если вы добавите правильные отношения между сущностями, запрос может быть упрощен до :
var binValue='QWNjb3VudDoxNTMwNDAzMi00MWM2LTExZTktOWYzMy1kMzQxZjE5OWZlYjM=';
var query=_dbContext.LicenseActivations
.Where(lact=>
lact.AccountId == binValue
amp;amp; (lact.IsConverted==null || !lact.IsConverted))
.Select(lact=>new {
lact.AccountId,
lact.SubscriptionKey,
lact.CustomerEmail});
или, если AccountId
поля не содержат одинаковые данные :
.Select(lact=>new {
AccountId =lact.LicensePart.License.AccountId,
lact.SubscriptionKey,
lact.CustomerEmail
});
EF Core создаст соответствующие предложения SQL и JOIN для перехода от LicenseActivation
к License
на основе отношений между сущностями
Комментарии:
1. пользователь просит преобразовать в SQL, а не улучшать код c# linkq
2. И вот что я ответил. Поля могут быть сопоставлены. После этого оператору остается написать запрос любым удобным для него способом, хотя для фактического преобразования даже не требуется никаких соединений.
3. Ему лучше забрать созданный sql — запрос из профилировщика sql и начать оттуда. Если он изменит код, он может изменить существующую логику. Бессмысленно что-либо менять в c# linq, так как его цель-создать эквивалентный оператор sql.