Việc làm

4 ngày trước · Hồ Chí Minh · Hybrid

Senior/Principal .NET Developer

Saigon Technology · Fullstack Developer

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

10 câu hỏi có thể gặp khi phỏng vấn

Chọn theo kỹ năng và cấp của tin (C# & .NET, JavaScript & TypeScript, SQL & Database). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.

  1. 01Dependency Injection trong ASP.NET Core: phân biệt Transient, Scoped và Singleton.C# & .NET

    Gợi ý trả lời

    ASP.NET Core có DI container sẵn; lifetime quyết định khi nào tạo instance mới.

    • Transient: mỗi lần resolve tạo mới. Hợp với service nhẹ, không trạng thái.
    • Scoped: một instance cho mỗi request (mỗi scope). DbContext mặc định là scoped.
    • Singleton: một instance cho cả vòng đời ứng dụng. Phải thread-safe.
    builder.Services.AddScoped<IOrderService, OrderService>();
    builder.Services.AddSingleton<IClock, SystemClock>();
    

    Hay sai: inject service scoped (như DbContext) vào singleton, gọi là captive dependency: DbContext bị giữ mãi và dùng chung giữa nhiều thread. Môi trường Development sẽ báo lỗi khi validate scope. Nếu singleton cần dữ liệu scoped, hãy inject IServiceScopeFactory và tự tạo scope.

    Ôn thêm câu hỏi C# & .NET →
  2. 02Middleware trong ASP.NET Core là gì? Thứ tự đăng ký có quan trọng không?C# & .NET

    Gợi ý trả lời

    Middleware là các bước nối thành pipeline xử lý request: mỗi bước có thể xử lý, gọi bước kế tiếp, rồi xử lý tiếp response trên đường về. Thứ tự rất quan trọng vì request đi qua đúng theo thứ tự đăng ký.

    app.UseExceptionHandler("/error");
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseCors();
    app.UseAuthentication();
    app.UseAuthorization();
    app.MapControllers();
    
    • Exception handler phải đứng đầu để bắt lỗi của mọi bước sau.
    • UseAuthentication phải trước UseAuthorization, nếu không user luôn là ẩn danh.
    • Middleware có thể "short-circuit", vd. trả cache hoặc 401 mà không gọi next.

    Người phỏng vấn muốn nghe: bạn từng viết middleware riêng, vd. gắn correlation id hoặc log thời gian xử lý, và phân biệt được middleware với filter của MVC.

    Ôn thêm câu hỏi C# & .NET →
  3. 03Lỗi N+1 query trong Entity Framework Core là gì và xử lý thế nào?C# & .NET

    Gợi ý trả lời

    N+1 là khi lấy danh sách N bản ghi bằng 1 query, rồi mỗi bản ghi lại phát sinh thêm 1 query để lấy dữ liệu liên quan, thường do lazy loading hoặc truy vấn trong vòng lặp.

    Cách xử lý:

    • Eager loading: .Include(o => o.Items).
    • Tốt hơn nữa là projection chỉ lấy cột cần: .Select(o => new OrderDto(o.Id, o.Items.Count)).
    • Include nhiều collection gây "cartesian explosion" thì dùng .AsSplitQuery().
    • Màn hình chỉ đọc thì thêm .AsNoTracking() để giảm chi phí change tracking.

    Để phát hiện, bật log SQL của EF Core khi phát triển, hoặc dùng công cụ APM xem số query mỗi request.

    Người phỏng vấn muốn nghe: bạn kiểm tra SQL thực sự được sinh ra thay vì tin LINQ trông gọn là chạy nhanh.

    Ôn thêm câu hỏi C# & .NET →
  4. 04So sánh Promise.all, Promise.allSettled, Promise.race và Promise.any. Xử lý lỗi với async/await thế nào?JavaScript & TypeScript

    Gợi ý trả lời

    Cả bốn đều chạy nhiều Promise song song, khác nhau ở cách kết thúc:

    • all: chờ tất cả thành công; một cái lỗi là reject ngay.
    • allSettled: chờ tất cả xong, trả về trạng thái từng cái; không bao giờ reject.
    • race: lấy kết quả (thành công hay lỗi) của cái xong đầu tiên; hay dùng để đặt timeout.
    • any: lấy cái thành công đầu tiên; chỉ reject khi tất cả đều lỗi.
    try {
      const [user, orders] = await Promise.all([getUser(id), getOrders(id)]);
    } catch (err) {
      showError(err);
    }
    

    Hay sai: await lần lượt trong vòng lặp các request không phụ thuộc nhau, khiến tổng thời gian cộng dồn. Ngược lại, gọi Promise.all với hàng nghìn request cùng lúc có thể làm quá tải server; khi đó cần giới hạn số request đồng thời.

    Ôn thêm câu hỏi JavaScript & TypeScript →
  5. 05Trong TypeScript, interface và type khác nhau thế nào? Phân biệt any, unknown và never.JavaScript & TypeScript

    Gợi ý trả lời

    Cả hai đều mô tả hình dạng object và phần lớn trường hợp dùng thay nhau được.

    • interface có thể gộp khai báo (declaration merging) và extends; hợp để mô tả object, API công khai.
    • type mô tả được union, tuple, kiểu ánh xạ, kiểu điều kiện: type Status = "idle" | "loading".

    Ba kiểu đặc biệt:

    • any: tắt kiểm tra kiểu, lỗi lọt qua compiler.
    • unknown: nhận mọi giá trị nhưng bắt buộc thu hẹp kiểu trước khi dùng. Hợp cho dữ liệu từ JSON.parse, API, catch (e).
    • never: giá trị không bao giờ xảy ra; dùng để bắt thiếu case trong switch.
    function assertNever(x: never): never {
      throw new Error("Unhandled: " + x);
    }
    

    Hay sai: dùng as SomeType để ép dữ liệu từ API. Đó chỉ là lời hứa với compiler; muốn chắc chắn phải validate lúc runtime (ví dụ bằng Zod).

    Ôn thêm câu hỏi JavaScript & TypeScript →
  6. 06Một API chạy chậm, nghi do query. Bạn điều tra và tối ưu thế nào?SQL & Database

    Gợi ý trả lời

    Đo trước rồi mới sửa: tìm đúng query chậm, đọc execution plan, rồi chỉnh index hoặc cách viết query.

    1. Bật slow query log (MySQL) hoặc pg_stat_statements (PostgreSQL) để biết query nào tốn thời gian nhất.
    2. Chạy EXPLAIN (PostgreSQL nên dùng EXPLAIN (ANALYZE, BUFFERS)) xem có full scan, ước lượng số dòng lệch nhiều so với thực tế, sort trên đĩa không.
    3. Sửa theo nguyên nhân: thêm hoặc đổi composite index, bỏ hàm bọc cột, chỉ SELECT cột cần, phân trang bằng keyset thay cho OFFSET lớn.
    4. Kiểm tra phía ứng dụng: lỗi N+1 query, thiếu connection pool, lock chờ lâu.
    5. Cập nhật thống kê (ANALYZE) nếu planner ước lượng sai.

    Người phỏng vấn muốn nghe: quy trình có số liệu trước/sau, không phải đoán rồi thêm index bừa.

    Ôn thêm câu hỏi SQL & Database →
  7. 07Garbage Collector của .NET hoạt động thế nào? Khi nào cần IDisposable?C# & .NET

    Gợi ý trả lời

    GC quản lý heap theo thế hệ: object mới ở Gen 0, sống sót qua lần thu gom thì lên Gen 1, Gen 2. Phần lớn object chết trẻ nên thu gom Gen 0 rất rẻ. Object lớn (khoảng từ 85 KB) vào Large Object Heap và chỉ được thu cùng Gen 2.

    GC chỉ thu hồi bộ nhớ managed. Tài nguyên khác như file, socket, kết nối DB cần giải phóng chủ động qua IDisposable:

    await using var conn = new NpgsqlConnection(cs);
    using var stream = File.OpenRead(path);
    
    • Finalizer chỉ là lưới an toàn, chạy không xác định thời điểm và tốn chi phí.

    Hay sai: tạo mới HttpClient cho mỗi request rồi dispose, dễ cạn socket. Hãy dùng IHttpClientFactory hoặc một instance dùng chung.

    Ôn thêm câu hỏi C# & .NET →
  8. 08Ứng dụng ASP.NET Core bị memory leak hoặc CPU cao trên production. Bạn điều tra thế nào?C# & .NET · Senior

    Gợi ý trả lời

    Thu thập số liệu trước, đoán sau: xem metrics, chụp dump hoặc trace, rồi phân tích.

    1. Theo dõi bằng dotnet-counters: heap size, số lần GC từng gen, thread pool queue length, exception rate.
    2. Leak bộ nhớ: chụp hai lần dotnet-gcdump hoặc dotnet-dump cách nhau một khoảng, so sánh loại object tăng và tìm ai đang giữ tham chiếu.
    3. CPU cao hoặc treo: dotnet-trace để xem hot path; thread pool queue tăng thường do sync-over-async.

    Nguyên nhân hay gặp: event handler không gỡ đăng ký, cache static không giới hạn kích thước, giữ DbContext quá lâu, regex hoặc serialize lặp lại trong vòng lặp nóng.

    Người phỏng vấn muốn nghe: một ca thực tế bạn từng xử lý, công cụ đã dùng và kết quả trước/sau.

    Ôn thêm câu hỏi C# & .NET →
  9. 09Xử lý concurrency khi nhiều request cùng cập nhật một bản ghi trong EF Core thế nào?C# & .NET · Senior

    Gợi ý trả lời

    Hai hướng chính: optimistic concurrency (phát hiện xung đột khi lưu) và pessimistic (khoá trước khi sửa).

    Optimistic với EF Core: đánh dấu một cột làm concurrency token, EF thêm điều kiện so sánh vào UPDATE và ném DbUpdateConcurrencyException nếu không có dòng nào bị cập nhật.

    public class Product {
        public int Stock { get; set; }
        [Timestamp] public byte[] RowVersion { get; set; } = [];
    }
    
    • Bắt exception, tải lại dữ liệu, rồi retry hoặc báo người dùng.
    • PostgreSQL thường dùng cột hệ thống xmin làm token.
    • Trường hợp đơn giản như trừ tồn kho có thể dùng một câu ExecuteUpdateAsync atomic kèm điều kiện Stock >= qty.

    Người phỏng vấn muốn nghe: bạn cân nhắc mức độ tranh chấp: thấp thì optimistic, cao và nghiệp vụ quan trọng thì khoá hoặc đưa vào hàng đợi.

    Ôn thêm câu hỏi C# & .NET →
  10. 10Vì sao bạn chọn ứng tuyển vào công ty chúng tôi?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Câu này kiểm tra bạn có tìm hiểu không hay chỉ rải CV. Nối ba thứ: công ty, vị trí, và bạn.

    • Tìm hiểu trước: sản phẩm, khách hàng, công nghệ, tin tức gần đây, đánh giá của nhân viên cũ và hiện tại.
    • Chọn một hai điểm cụ thể bạn thật sự quan tâm: sản phẩm bạn từng dùng, bài toán kỹ thuật, quy mô người dùng, văn hoá làm việc.
    • Nối với kinh nghiệm hoặc mục tiêu của bạn.

    Câu mẫu: "Em có dùng ứng dụng của bên mình và thấy phần thanh toán rất mượt. Trong JD có nói team đang mở rộng hệ thống xử lý giao dịch, đúng mảng em đã làm hai năm. Em nghĩ mình đóng góp được ngay và cũng học được nhiều ở quy mô lớn hơn."

    Hay sai: "Vì công ty lớn, lương tốt" hoặc khen chung chung áp vào công ty nào cũng được.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở Saigon Technology?

Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.

Chia sẻ kinh nghiệm phỏng vấn