Code First or Database First
TrapModel First belongs to EF6. EF Core has no visual designer and no EDMX file.
Asked in interviews
AskedWhat are the main approaches to using Entity Framework Core?DbSet<T> is where queries and changes for that type start.DbContext is one short-lived session with the database and your unit of work. It isn't thread-safe, so each web request gets its own.IQueryable<T> builds an expression tree that EF Core turns into SQL. Nothing runs until you enumerate. That's deferred execution.SaveChanges finds the differences and writes them in one transaction.Up to apply it and Down to revert it.TrapModel First belongs to EF6. EF Core has no visual designer and no EDMX file.
Asked in interviews
AskedWhat are the main approaches to using Entity Framework Core?Id<TypeName>IdAdd(e)Update(e)Remove(e)Products.LocalTipAn entity must be a class, not a struct. It needs no parameterless constructor.
AddDbContext<T>() DbContextOptions<T> OnConfiguring()new DbContextOptionsBuilder<T>() AddDbContextPool<T>() TrapDbContext is not thread-safe. Use one instance per unit of work, such as one web request.
SaveChanges()await SaveChangesAsync() TipBoth write in one transaction. Async helps a busy app scale, but one save is no faster.
TrapAwait each call before the next one. EF Core doesn't support two operations at once on one DbContext.
[Key][Required][MaxLength(100)] [Column(TypeName = "decimal(18,2)")][NotMapped].IsRequired().HasMaxLength(100) .HasColumnName("Cost") .HasDefaultValue(0) .HasComputedColumnSql("...") HasKey(e => new { e.A, e.B })TipUse it for what has no annotation, like sequences and computed columns. It overrides annotations, which override conventions.
HasIndex(p => p.Name)[Index(nameof(Name))]HasIndex(p => new { p.A, p.B }).IsUnique().HasFilter("[Email] IS NOT NULL")TrapColumn order matters. The database can use a composite index for filters on its leading column.
Asked in interviews
AskedHow do you configure indexes in Entity Framework Core?.HasConversion<string>() .HasConversion(toDb, fromDb)Property<DateTime>("x") EF.Property<DateTime>(p, "x")TipA shadow property is in the model and the table, but not on the class. It suits audit columns.
TrapA converted list needs a ValueComparer. Without one, EF Core compares references and never saves a tag you add.
HasOne().WithOne() HasOne().WithMany() HasMany().WithMany() TrapA navigation stays empty until you load it, with Include, Load or lazy loading.
[ForeignKey("Blog")] TipPost holds the foreign key, so it's the dependent. BlogId matches by convention, so this setup is optional.
Asked in interviews
AskedHow do you configure One-to-Many relationships?.UsingEntity<StudentCourse>(...) int? ManagerId.OnDelete(DeleteBehavior.Restrict) TrapA join class does nothing on its own. Map the skip navigations through it with UsingEntity<T>.
dotnet ef migrations add <name>dotnet ef database updatedotnet ef database update <name>dotnet ef database update 0dotnet ef migrations removedotnet ef migrations scriptTipadd compares your model with the last snapshot. update logs each run in __EFMigrations.
TrapDon't deploy with database update or at app startup. Use a bundle or a reviewed SQL script.
migrationBuilder.RenameColumn(...) migrationBuilder.Sql("...") TipHasData rows flow into the next migration you add.
TrapEF Core usually can't tell a rename from a drop plus an add. So it drops the column and loses the data.
Asked in interviews
AskedHow can you customize migration generation?git merge --abortdotnet ef migrations removedotnet ef migrations add <name>TrapUp to EF Core 10, Git can merge the snapshot with no conflict, so nobody notices. From EF Core 11, a diverged history always conflicts.
Asked in interviews
AskedWhat happens when you have migration conflicts in a team environment?dotnet ef dbcontext scaffold "<conn>" <provider>--no-onconfiguring--forceTipScaffold-Db does the same in the Package Manager Console. The classes are partial, so keep your code in separate files.
Asked in interviews
AskedHow do you scaffold an existing database using Entity Framework Core?HasData(...)Set every primary key.UseSeeding(...)UseAsyncSeeding(...) TrapUseSeeding runs even when there is nothing to migrate, so check before you insert.
Asked in interviews
AskedHow do you implement database seeding in Entity Framework Core?First()Take the first, or throwFirstOrDefault() Take the first, or nullSingle()Demand exactly oneSingleOrDefault() Allow at most oneTrapWhen several rows can match First, add an OrderBy. The database returns rows in no fixed order.
IQueryable<T>IEnumerable<T>AsEnumerable()TipBoth run only when you enumerate.
TrapProducts. loads every row, then filters in memory.
Skip(n).Take(size) The database still reads every skipped row.Where(p => p.Id > lastId).Take(size) It can't jump to a page number.TrapAlways order by a unique key, or rows can repeat or go missing between pages.
Asked in interviews
AskedHow do you implement paging efficiently in Entity Framework Core?Include(b => b.Posts)ThenInclude(p => p.Tags)Include(b => b.Posts.Where(...)) Collection(x => x.Posts).LoadAsync() UseLazyLoadingProxies() It also needs the Proxies package and virtual navigations.TipTwo collections in Include multiply rows. A blog with 10 posts and 10 tags returns 100 rows.
TrapA lazy load in a loop runs one query per item, the N+1 problem. It also fails once the context is disposed.
FromSql($"... {price}")FromSqlRaw("EXEC Proc {0}", id)ExecuteSql($"...") TipThe SQL must return every column of the entity. For scalars, use Database..
TrapFromSqlRaw is open to SQL injection if you concatenate user input into the string.
Asked in interviews
AskedHow do you write raw SQL queries in Entity Framework Core?Entry(e).StateChangeTracker.Entries() Property(p => p.Name).IsModified .OriginalValue.CurrentValueTipDetached means not tracked. SaveChanges calls DetectChanges, which compares values with the snapshot.
AsNoTracking()QueryTrackingBehavior.NoTracking TipA projection with no entity types is never tracked. AsTracking() opts one query back in.
TrapSaveChanges ignores changes to these entities until you attach them again. A row that appears twice becomes two objects.
Asked in interviews
AskedHow do you disable Change Tracking for read-only scenarios?Update(entity)Attach(e).IsModified = trueTipEditing a tracked entity updates only the columns you changed.
TrapUpdate() writes every column, so it can overwrite a value another user changed in a column you never touched.
ExecuteDeleteAsync() ExecuteUpdateAsync(...) TipThey start no transaction and skip concurrency tokens, but they return the rows affected.
TrapTracked entities keep their old values. A later save that sets the same property overwrites the bulk change.
[ConcurrencyCheck] [Timestamp].IsRowVersion()GetDatabaseValuesAsync() WITH (UPDLOCK, ROWLOCK)Pessimistic. EF Core has no API, so use raw SQL.TipOptimistic locking adds the token to WHERE. If no row matches, SaveChanges throws Db. With no token, the last write wins.
TrapUPDLOCK lasts only until the transaction ends. Read and save in one explicit transaction, and keep it short.
BeginTransactionAsync() CommitAsync()new TransactionScope(...) TipEach SaveChanges is already one transaction.
TrapA scope across two databases becomes a distributed transaction. .NET supports those only on Windows, from .NET 7.
Asked in interviews
AskedHow do you work with database transactions in Entity Framework Core?Include(...)Select(p => p.Name)Where that can't translateEF.Functions.Like(...) EF.CompileQuery(...) TipA compiled query skips the cache lookup. Parameters must be scalars, and the saving is often small, so measure first.
TrapToList() before Select runs the projection in memory, after every column has loaded.
AsSplitQuery()AsSingleQuery()UseQuerySplittingBehavior(...) TrapEach query is a round trip. A concurrent change can leave results inconsistent unless you use a serializable or snapshot transaction.
Asked in interviews
AskedHow do you use Split Queries to optimize performance?TipMany apps use EF Core for writes and Dapper for heavy reads. Profile before you switch.
Asked in interviews
AskedHow does EF Core compare to Dapper, and when would you choose each?HasQueryFilter(p => !p.IsDeleted)IgnoreQueryFilters() Remove into an updateentry.State = EntityState.Modified Override SaveChanges and SaveChangesAsync ..HasFilter("[IsDeleted] = 0")TipDatabase cascades never fire, so mark the children yourself.
TrapA second HasQueryFilter on a type replaces the first. Use one lambda, or named filters (10+).
TipThe filter covers only queries, so set TenantId on new rows yourself.
TrapRead the tenant from a context field. EF Core caches the model, so a copied value leaks the first tenant's rows to everyone.
Asked in interviews
AskedHow do you implement multi-tenancy with Entity Framework Core?HasDiscriminator<string>("Type") Share one table, the default (TPH)ToTable("Dogs")Give every type its own table (TPT)UseTpcMappingStrategy() Give each concrete type a table (TPC)TipStart with TPH. Choose TPC when you mostly query concrete types.
TrapTPC (7+) keys must be unique across tables, so SQL Server uses a shared sequence.
OwnsOne(c => c.Address)OwnsMany(o => o.Items)ToTable("Blogs") They share one primary key.HasNoKey().ToView("V") TipThe docs suggest complex types for value objects. They compare by value and have no hidden key.
TrapEF Core never tracks a keyless type, so SaveChanges can't write it. It also can't be the principal of a relationship.
tb.IsTemporal()TemporalAsOf(date) TemporalAll()TemporalFromTo(a, b)TipNormal queries and saves see only current data.
ToList()Don't return IQueryable from a using block.IServiceScopeFactory IDbContextFactory<T> TrapAlso look for Dispose on an injected context, an unawaited task, or a singleton holding the context.
Asked in interviews
AskedHow do you handle the "Cannot access a disposed object" error?EnableRetryOnFailure() LogTo(Console.WriteLine) EnableSensitiveDataLogging() TrapNever turn on sensitive data logging in production.
IRepository<T>context.Set<T>() DbContext is already a unit of work. Use a repository for test mocks.TipA thin service loads the aggregate, calls its methods and saves.
TestcontainersDataSource=:memory: Keep the connection open, or the data disappears.TrapAvoid the EF Core in-memory provider. It isn't relational, has no transactions and can't run raw SQL.
Nothing matches. Try a shorter word, or clear the search.
With Pro, you can:
Already have Pro? Sign in