Paweł Łukasiewicz
2021-08-10
Paweł Łukasiewicz
2021-08-10
Udostępnij Udostępnij Kontakt
Wprowadzenie

Entity Framework Core udostępnia metodę DbSet.FromSqlRaw(), która pozwala na wykonywanie nieprzetworzonych zapytań SQL. Takie podejście może spodobać się szczególnie osobom, które chcą mieć pełną kontrolę nad wykonywanymi zapytaniami, pracowały w przeszłości sporo z technologią ADO.NET lub Dapper okazał się niewystarczającym rozwiązaniem w ich projekcie.

Spójrzmy na niezwykle prosty przykład wykonania nieprzetworzonego zapytania SQL:

using(var context = new ApplicationDbContext())
{
	var customers = context.Customer.FromSqlRaw("SELECT * FROM Customer").ToList();
}

Metoda FromSqlRaw używana jest na danej encji (w tym konkretnym przypadku: DbSet<Customer>). Tworzone zapytanie musi zwrócić rekordy z tabeli Customer, które zostaną przekształcone w encje Customer. Entity Framework Core wykona określone zapytanie na danej tabeli.

Zapytania sparametryzowane

Metoda FromRawSql pozwala na tworzenie sparametryzowanych zapytań przy użyciu składni znanej z interpolacji ciągów w języku C#. Spójrzcie poniżej:

using(var context = new ApplicationDbContext())
{
	string name = "Paweł";

	var customers = context.Customer.
		FromSqlRaw($"SELECT * FROM Customer WHERE FullName = '{name}'").ToList();
}

Poniższy zapis jest również poprawny:

using(var context = new ApplicationDbContext())
{
	string name = "Paweł";

	var customers = context.Customer.
		FromSqlRaw("SELECT * FROM Customer WHERE FullName = {0}", name).ToList();
}

Co tak naprawdę dzieje się pod spodem? Spójrzmy na SQL Profiler:

exec sp_executesql N'SELECT * FROM Customer WHERE FullName = @p0
',N'@p0 nvarchar(4000)',@p0=N'Paweł'

LINQ Standard Query Operators

Jest to zestaw metod rozszerzających tworzących wzorzec zapytania znany jako standardowe operatory zapytań LINQ. Ich użycie wprowadza szereg dodatkowych możliwości takich jak filtrowanie, sortowanie, agregacja czy projekcja. Czemu o tym mówię? Operatory LINQ mogą być używane wraz z nieprzetworzonymi zapytaniami:

using(var context = new ApplicationDbContext())
{
	string name = "Paweł";

	var customers = context.Customer.
		FromSqlRaw("SELECT * FROM Customer WHERE FullName = {0}", name)
		.OrderBy(a=>a.Id)
		.ToList();
}

Spójrzmy jeszcze na rezultat wykonania powyższego zapytania:

exec sp_executesql N'SELECT [c].[Id], [c].[FullName]
FROM (
    SELECT * FROM Customer WHERE FullName = @p0
) AS [c]
ORDER BY [c].[Id]',N'@p0 nvarchar(4000)',@p0=N'Paweł'

Ograniczenia

Znajomość ograniczeń wynikających ze stosowania nieprzetworzonych zapytań pozwoli nam uniknać licznych frustracji przy niepoprawnych implementacjach:

  • Zapytanie SQL musi zwrócić wszystkie kolumny tabeli. Zapytanie w poniższej formie nie jest dozwolone:
    var customers = context.Customer.
    	FromSqlRaw("SELECT Id FROM Customer WHERE FullName = {0}", name)
    	.ToList();
    
  • Zapytanie SQL nie może zawierać złączeń JOIN w celu uzyskania powiązanych danych. W tym wypadku musimy użyć metody Include, która będzie wykorzystana po metodzie FromRawSql.
  • Zapytanie SQL musi zwracać encje tego samego typu co DbSet. Dla omawianych wcześniej przypadków: nie możemy np. zwrócić danych typu Order jeżeli nieprzetworzone zapytanie wykonujemy ma encji Customer.