Intro
When I wrote raw SQL with Entity Framework Core, sometimes I got unexpected results.
In this time, I will try how to avoid them.
Environments
- .NET 6.0.101
- Microsoft.EntityFrameworkCore ver.6.0.1
- Microsoft.EntityFrameworkCore.Design ver.6.0.1
- Npgsql.EntityFrameworkCore.PostgreSQL ver.6.0.2
- NLog.Web.AspNetCore ver.4.14.0
Prevents tables from being generated due to migration
When I put gotten data into a class what has custom properties, I add DbSet< T> into a DbContext class.
But by default, dotnet-ef generates a new table on migration.
SearchedBook.cs
using System.ComponentModel.DataAnnotations; namespace BookshelfSample.Books.Dto; public record SearchedBook { [Key] public int BookId { get; init; } public string BookName { get; init; } = ""; public string AuthorName { get; init; } = ""; }
BookshelfContext.cs
using BookshelfSample.Books.Dto; using BookshelfSample.Models.SeedData; using Microsoft.EntityFrameworkCore; namespace BookshelfSample.Models; public class BookshelfContext: DbContext { public BookshelfContext(DbContextOptions<BookshelfContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Book>() .HasOne(b => b.Author) .WithMany(a => a.Books) .HasForeignKey(b => b.AuthorId); modelBuilder.Entity<Book>() .HasOne(b => b.Language) .WithMany(L => L.Books) .HasForeignKey(b => b.LanguageId); modelBuilder.Entity<Language>() .HasData(LanguageData.GetAll()); } public DbSet<Author> Authors => Set<Author>(); public DbSet<Book> Books => Set<Book>(); public DbSet<Language> Languages => Set<Language>(); // I don't want to generate "SearchedBook" table. public DbSet<SearchedBook> SearchedBooks => Set<SearchedBook>(); }
To exclude generating the table, I add "ExcludeFromMigrations" in "OnModelCreating".
BookshelfContext.cs
... public class BookshelfContext: DbContext { ... protected override void OnModelCreating(ModelBuilder modelBuilder) { ... modelBuilder.Entity<SearchedBook>().ToTable("searched_book", t => t.ExcludeFromMigrations()); } public DbSet<Author> Authors => Set<Author>(); public DbSet<Book> Books => Set<Book>(); public DbSet<Language> Languages => Set<Language>(); public DbSet<SearchedBook> SearchedBooks => Set<SearchedBook>(); }
Add multiple where() methods
I can add multiple where() methods.
SearchBooks.cs
using BookshelfSample.Books.Dto; using BookshelfSample.Models; using Microsoft.EntityFrameworkCore; namespace BookshelfSample.Books; public class SearchBooks: ISearchBooks { private readonly ILogger<SearchBooks> logger; private readonly BookshelfContext context; public SearchBooks(ILogger<SearchBooks> logger, BookshelfContext context) { this.logger = logger; this.context = context; } public async Task<List<SearchedBook>> GetAsync(SearchBookCriteria criteria) { var query = this.context.SearchedBooks .FromSqlRaw("SELECT b.id AS \"BookId\", b.name AS \"BookName\", a.name AS \"AuthorName\" FROM book b INNER JOIN author AS a ON b.author_id = a.id"); if(string.IsNullOrEmpty(criteria.Name) == false) { query = query.Where(b => b.BookName.Contains(criteria.Name)); } if(string.IsNullOrEmpty(criteria.AuthorName) == false) { query = query.Where(b => b.AuthorName.Contains(criteria.AuthorName)); } return await query .OrderBy(b => b.BookId) .ToListAsync(); } }
According to logs, these methods are merged into one WHERE clause.
SELECT b."BookId", b."AuthorName", b."BookName" FROM ( SELECT b.id AS "BookId", b.name AS "BookName", a.name AS "AuthorName" FROM book b INNER JOIN author AS a ON b.author_id = a.id ) AS b WHERE ((@__criteria_Name_1 = '') OR (strpos(b."BookName", @__criteria_Name_1) > 0)) AND ((@__criteria_AuthorName_2 = '') OR (strpos(b."AuthorName", @__criteria_AuthorName_2) > 0)) ORDER BY b."BookId"
Top comments (1)
Nice content, bro. Thanks a lot.