Firstordefaultasync là gì

FirstOrDefaultAsync () & SingleOrDefaultAsync () so với FindAsync () EFCore

Hoàng Thanh Lâm · Hoàng Thanh Lâm 03:37 22/02/2019

hôm qua

Chúng tôi có 3 cách tiếp cận khác nhau để lấy các mục đơn lẻ từ EFCore FirstOrDefaultAsync(), SingleOrDefaultAsync()(bao gồm cả các phiên bản của nó không có giá trị mặc định được trả về, chúng tôi cũng có FindAsync()và có thể nhiều hơn nữa với cùng mục đích như LastOrDefaultAsync().

var findItem = await dbContext.TodoItems .FindAsync(request.Id) .ConfigureAwait(false); var firstItem = await dbContext.TodoItems .FirstOrDefaultAsync(i => i.Id == request.Id) .ConfigureAwait(false); var singleItem = await dbContext.TodoItems .SingleOrDefaultAsync(i => i.Id == request.Id) .ConfigureAwait(false);

Tôi muốn biết sự khác biệt giữa mỗi người trong số họ. Cho đến nay, những gì tôi biết là chúng ta FirstOrDefaultAsync()lấy một điều kiện cho trước đầu tiên, (thường sử dụng điều này vì chúng ta biết rằng nhiều mặt hàng có thể thỏa mãn điều kiện), mặt khác chúng ta sử dụng SingleOrDefaultAsync()vì chúng ta biết rằng chỉ có một điều kiện khả thi. để tìm và FindAsync()lấy một mục được cung cấp khóa chính của nó.

Tôi nghĩ FirstOrDefaultAsync()& SingleOrDefaultAsync()luôn nhấn cơ sở dữ liệu (không chắc chắn về điều này) và FindAsync()đây là những gì tài liệu của Microsoft nói:

Tìm một thực thể không đồng bộ với các giá trị khóa chính đã cho. Nếu một thực thể có các giá trị khóa chính đã cho tồn tại trong ngữ cảnh, thì nó sẽ được trả về ngay lập tức mà không cần đưa ra yêu cầu đối với cửa hàng. Nếu không, một thực thể có các giá trị khóa chính đã cho sẽ được gửi đến cửa hàng và thực thể này, nếu được tìm thấy, sẽ được gắn vào ngữ cảnh và được trả về. Nếu không tìm thấy thực thể nào trong ngữ cảnh hoặc cửa hàng, thì giá trị null được trả về.

Vì vậy, câu hỏi của tôi là, nếu điều kiện nhất định của chúng tôi sử dụng cho FirstOrDefault(), SingleOrDefault()và FindAsync()là chìa khóa chính, chúng ta có bất kỳ sự khác biệt thực tế?

Những gì tôi nghĩ là lần đầu tiên chúng được sử dụng luôn luôn đạt db, nhưng những gì về cuộc gọi tiếp theo? . Và có lẽ EFCore có thể sử dụng cùng một ngữ cảnh để lấy các giá trị cho FirstOrDefault()và SingleOrDefault()như nó cho FindAsync(), có thể không? .

  • c#
  • linq
  • ef-core-2.0

24 hữu ích 3 bình luận 11k xem chia sẻ

Asynchronous trong Entity Framework

Đăng bởi Trung Nguyen 2 năm trước trong Entity Framework

Việc thực thi không đồng bộ (asynchronous) đã được giới thiệu trong .NET 4.5 và nó rất hữu ích trong Entity Framework (EF).

Entity Framework (EF) 6 cho phép chúng ta thực thi một truy vấn và câu lệnh không đồng bộ bằng cách sử dụng một thể hiện của DbContext.

Trước tiên, hãy xem cách thực thi các truy vấn không đồng bộ, sau đó chúng ta sẽ thấy gọi phương thức context.SaveChanges không đồng bộ.

Giới thiệu về Entity Framework Core

EF Core là framework (thư viện khung) để ánh xạ các đơn vị dữ liệu mô tả bằng lớp (đối tượng) vào cơ sở dữ liệu quan hệ, nó cho phép ánh xạ vào các bảng CSDL, tạo CSDL, truy vấn với LINQ, tạo và cập nhật vào database.

Để sử dụng EF Core hãy thêm những package cần thiết vào, chạy các lệnh sau:

dotnet add package System.Data.SqlClient dotnet add package Microsoft.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Design dotnet add package Microsoft.Extensions.DependencyInjection dotnet add package Microsoft.Extensions.Logging dotnet add package Microsoft.Extensions.Logging.Console

Những namespace có thể dùng:

using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Threading.Tasks;

QueryableExtensions.FirstOrDefaultAsync Method

  • Reference

Is this page helpful?

Please rate your experience

Yes No

Any additional feedback?

Feedback will be sent to Microsoft: By pressing the submit button, your feedback will be used to improve Microsoft products and services. Privacy policy.

Submit

Thank you.

Xây dựng web app multi-tenant đơn giản với Citus data và AspNet Core

December 15, 2020 0 By Nam Vu

Bài viết trước mình có đề cập đến một tính năng nổi bật của Citus Data đó là việc mở rộng multi-tenants. Bạn có thể đọc lại tại đây

Ở bài viết này mình sẽ giúp bạn việc mà citus scale multi-tenants thông qua việc xây dựng một ứng dụng nhỏ được viết băng aspnet core kết hợp với postgres.

Bắt đầu thôi!

Khi một ứng dụng multi tenants được xây dựng có khả năng mở rộng lớn thì việc chọn asp net platform là một lựa chọn không tồi. Cũng như những platform phổ biến khác như Express và Django thì Asp Net được sử dụng để xây dựng một ứng dụng web application và api cực kỳ mạnh mẽ.

Trong quá khứ, ứng dụng Asp Net chỉ hoạt động được trên nền windows servers, nhưng đối với phiên bản mới của AspNet Core thì nó lại có thể chạy được trên mọi nền tàng (cross platfrom) bên cạnh việc nó còn là mã nguồn mở và tăng hiệu suất đáng kinh ngạc nữa chứ.

Như mình đã đề cập ở bài viết trước thì vấn đề scale database chúng ta cần tổ chức kiến trúc tốt ngay từ ban đầu. Ngày trước các bạn thường biết đến với 2 kiểu tổ chức kiến trúc đó là dồn tất cả tenants vào chung một database, hoặc mỗi tenants bạn có thể để một database riêng (Cơ sở dữ liệu phân tán theo chiều dọc với khóa chốt là tenant)

Trong bài viết này mình sẽ sử dụng sharding để chia mỗi tenant thành khóa để các câu lệnh query sau đó sẽ được gắn tenant id vào khi đó sẽ truy vấn nhanh hơn. Bên cạnh đó sau này cũng dễ dàng mở rộng theo tenant mới trên từng node.

Đầu tiên, bạn phải cài docker trước đã, có nhiều trang hướng dẫn cài docker rồi nên mình cũng không đề cập nó tại đây nữa.

Đến đây bạn có 2 cách để có thể cài đặt postgres citus

Cách 1: Sử dụng trực tiếp psql

> psql connection-string

> docker exec -it citus_master psql -U postgres

Cách 2: Tạo file docker-compose.yml vàdocker-compose.override.yml

Cho những ai chưa biết về 2 file này:

Docker compose là một công cụ vô cùng đơn giản để thực thi nhiều container cùng một lúc cho ứng dụng của bạn. Để có thể dùng được docker compose, bạn cần tạo một compose file như docker-compose.yml để thiết lập các container cần cho ứng dụng của bạn.

Và sau đó để build, run và stop các container, các bạn có thể sử dụng các command sau:

– “docker-compose build” dùng để build tất cả container được định nghĩa trong compose file.

– “docker-compose up” dùng để thực hiện tạo và khởi chạy các container.

– “docker-compose down” dùng để dừng các container và xóa hết những gì được tạo từ lệnh up.

Còn Docker compose override thì như cái tên của nó, nó dùng để ghi đè những biến môi trường mà mình định nghĩa khi chạy docker compose thôi.

Vậy là khi chạy mình chỉ sử dụng duy nhất 1 câu lệnh cmd này thôi

docker-compose -f docker-compose.yml -f docker-compose.override.yml up -d –build

Mình thích sử dụng cách thứ 2 hơn vì mỗi lần restart lại máy và docker thì các containers này tự động restart up theo. Chứ cách 1 chỉ dùng ở tại thời điểm đó thôi. Nhưng cách 2 thì bạn lại phải giữ một image trong máy, càng về sau dữ liệu càng phình to thì máy bạn sẽ phình theo nếu bạn không pune image (xóa nó đi).

Sau khi chạy câu lệnh trên thì bạn đã có một môi trường postgres citus

Bây giờ mình đã có cơ sở dữ liệu postgres rồi và mình sẽ viết câu sql cơ bản để tạo 2 bảng tenants và questions nhằm mục đích thể hiện bộ câu hỏi theo từng tenant mà thôi. Đây là 2 bảng cha con với khóa ngoại là tenant_id

CREATE TABLE tenants (

id uuid NOT NULL,

domain text NOT NULL,

name text NOT NULL,

description text NOT NULL,

created_at timestamptz NOT NULL,

updated_at timestamptz NOT NULL

);

CREATE TABLE questions (

id uuid NOT NULL,

tenant_id uuid NOT NULL,

title text NOT NULL,

votes int NOT NULL,

created_at timestamptz NOT NULL,

updated_at timestamptz NOT NULL

);

ALTER TABLE tenants ADD PRIMARY KEY (id);

ALTER TABLE questions ADD PRIMARY KEY (id, tenant_id);

Đến phần quan trọng nhất mà postgres citus data hỗ trợ đó là sharding với loại sharding distributed (Mình sẽ viết riêng 1 bài viết khác với chủ đề sharding này sau). Ở đây mình chạy localhost và chỉ tạo 1 node duy nhất nên sau khi chạy sharding nó sẽ tạo ra một loạt các bảng và có khoảng giá trị tenant khác nhau.

SELECT create_distributed_table(‘tenants’, ‘id’);

SELECT create_distributed_table(‘questions’, ‘tenant_id’);

Hãy tưởng tượng bạn có 1 tenant mới được thêm vào thì lập tức citus sẽ trigger phân loạitenant_id thuộc khoảng nào sau đó nhét vào bảng tưng ứng. Khi truy vấn lên nó cũng dựa vàotenant_id để biết được vào chính xác bảng nào thuộc node nào để lấy dữ liệu lên. Đó là cách tối ưu hiệu năng mà citus thực hiện.

Được rồi phần chính sharding mình đã xong, vấn đề bây giờ là seed data thôi.

INSERT INTO tenants VALUES (

‘c620f7ec-6b49-41e0-9913-08cfe81199af’,

‘ntechdevelopers.local’,

‘Buffer Overflow’,

‘Ask anything code-related!’,

now(),

now());

INSERT INTO tenants VALUES (

‘b8a83a82-bb41-4bb3-bfaa-e923faab2ca4’,

‘api.ntechdevelopers.local’,

‘Database Questions’,

‘Figure out why your connection string is broken.’,

now(),

now());

INSERT INTO questions VALUES (

‘347b7041-b421-4dc9-9e10-c64b8847fedf’,

‘c620f7ec-6b49-41e0-9913-08cfe81199af’,

‘How do you build apps in ASP.NET Core?’,

1,

now(),

now());

INSERT INTO questions VALUES (

‘a47ffcd2-635a-496e-8c65-c1cab53702a7’,

‘b8a83a82-bb41-4bb3-bfaa-e923faab2ca4’,

‘Using postgresql for multitenant data?’,

2,

now(),

now());

Sau khi có data thì tạo project, hiện tại thì mình sử dụng dot net core nên mình sẽ tạo nhanh với các câu lệnh sau để đỡ mất thời gian. Bạn cũng có thể clone source code ở cuối bài viết về chạy thử

> dotnet new mvc -o Ntech.CitusData

> cd Ntech.CitusData

> dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL

> dotnet new sln -o Ntech.CitusData

> dotnet sln Ntech.CitusData add .\Ntech.CitusData\Ntech.CitusData.csproj

> cd Ntech.CitusData

Add package cần sử dụng.

> dotnet add package Microsoft.AspNetCore.App

> dotnet add package SaasKit.Multitenancy

Nói thêm một chút vềSaasKit thì đây là một thư viện mã nguồn mở, nó được tạo ra nhằm mục đích cho những ứng dụng multi tenants can thiệp ở tầng middleware. Hiểu đơn giản thì khi bạn truy vấn mọi dữ liệu của 1 tenant thì dường như truy vấn tenant đó là không đổi, ví dụ//vietanhtran.spiderum.com/thì mọi thông tin chung trong trang tenant vietanhtran đó sẽ không đổi . Vậy tại sao mình không cache (tạo bộ nhớ đệm) cho những thử ít hoặc không bao giờ thay đổi.SaasKit.Multitenancy được sinh ra nhằm mục đích như vậy.

public class CachingTenantResolver : MemoryCacheTenantResolver<Tenant>

{

private readonly AppDbContext _context;

public CachingTenantResolver(AppDbContext context, IMemoryCache cache, ILoggerFactory loggerFactory): base(cache, loggerFactory)

{

_context = context;

}

// Resolver runs on cache misses

protected override async Task<TenantContext<Tenant>> ResolveAsync(HttpContext context)

{

var subdomain = context.Request.Host.Host.ToLower();

var tenants = await _context.Tenants.ToListAsync();

var tenant = await _context.Tenants.FirstOrDefaultAsync(t => t.Domain == subdomain);

if (tenant == null)

{

return null;

}

return new TenantContext<Tenant>(tenant);

}

protected override MemoryCacheEntryOptions CreateCacheEntryOptions()

=> new MemoryCacheEntryOptions()

.SetAbsoluteExpiration(TimeSpan.FromHours(2));

protected override string GetContextIdentifier(HttpContext context) => context.Request.Host.Host.ToLower();

protected override IEnumerable<string> GetTenantIdentifiers(TenantContext<Tenant> context) => new string[] { context.Tenant.Domain };

}

Dùng thư viện này thì nhớ 2 dòng đăng ký trong startup.cs nhé

services.AddMultitenancy<Tenant, CachingTenantResolver>();

app.UseMultitenancy<Tenant>();

Về model Question hay Tenant dùng để mapping với dữ liệu trong database hay tạo dataContext không có gì khó hiểu. Điều mình lưu ý ở đây là rule convention của postgres là snack case (kiểu viết thường nối nhau bằng _ như database_postgres_citus) vậy nên trongOnModelCreating() mình có thêm đoạn chuyểnsnack case cho table và column để mapping dữ liệu bảng cho chính xác thôi.

Vấn đề còn lại là build controller và view, 2 cái này thì sau khi mapping xong dữ liệu bảng rồi thì khá đơn giản và hiển thị như nào là tùy ý mỗi người. Vậy nên mình lướt nhanh nhé.

ModelView:

public class QuestionListViewModel

{

public IEnumerable<Question> Questions { get; set; }

}

Controller:

public async Task<IActionResult> Index()

{

var topQuestions = await _context

.Questions

.Where(q => q.Tenant.Id == _currentTenant.Id)

.OrderByDescending(q => q.UpdatedAt)

.Take(5)

.ToArrayAsync();

var viewModel = new QuestionListViewModel

{

Questions = topQuestions

};

return View(viewModel);

}

View:

@inject Tenant Tenant

@model QuestionListViewModel

@{

ViewData[“Title”] = “Home Page”;

}

<div class=”text-center”>

<h2 class=”display-4″>Welcome</h2>

<p>Learn about <a href=”//docs.microsoft.com/aspnet/core”>building Citus Data with ASP.NET Core</a>.</p>

</div>

<div class=”row”>

<div class=”col-md-12″>

<h2>Welcome to <strong>@Tenant.Name</strong></h2>

<h3>@Tenant.Description</h3>

</div>

</div>

<div class=”row”>

<div class=”col-md-12″>

<h4>Popular questions</h4>

<ul>

@foreach (var question in Model.Questions)

{

<li>@question.Title</li>

}

</ul>

</div>

</div>

Ok, đến đây là kết thúc phần code rồi. Do mình chạy local và muốn tạo domain (tên miền giả lập nên mình gắn đoạn config này vào file host “C:\Windows\System32\drivers\etc\hosts”

# Update host for domain

127.0.0.1 ntechdevelopers.local

127.0.0.1 api.ntechdevelopers.local

Nếu bạn để ý thì nó trùng khớp với domain mà mình seed data trong DB bên trên mục đích để redirect trang về đúng tenant domain tương ứng.

OK chạy thử thì nó sẽ như này

> cd src\Ntech.CitusData

> dotnet build

> dotnet run

Toàn bộ source code bạn có thể tham khảo tại:

# Github

Chúc các bạn thành công!

Facebook Twitter LinkedIn Pinterest Messenger

TagsAspNet Core Citus Citus Data DotNet Multi-tenant Postgres PostgreSQL

Khi tôi nói với sếp chuyện mình đi phỏng vấn?

Cách xây dựng một workflow thông qua masstransit saga state machine

Leave a Reply Cancel reply

You must be logged in to post a comment.

Introduction

In the Entity Framework-based applications, the DbContext / Object Context is responsible for tracking the changes done in the objects, so the correct update is done to the database when the SaveChanges() method of the context is called. When we retrieve entities using an object query, the Entity Framework puts these entities in a cache and tracks whatever changes are made on these entities until the savechanges method is called. Sometimes we do not want to track some entities because the data is only used for viewing purposes and other operations such as insert, update, and delete are not done. For example the view data in a read-only grid. The AsNoTracking() extension method returns a new query and the returned entities will not be cached by the context (DbContext or Object Context). This means that the Entity Framework does not perform any additional processing or storage of the entities that are returned by the query. Please note that we cannot update these entities without attaching to the context.

Tạo ra Visual Studio Project

Mở ứng dụng Visual Studio. Từ phầnFilemenu, chọnNew và sau đó chọnProject.

Mở rộng phầnInstalled> chọnVisual C#category. DướiVisual C#, chọn Web. Trong danh sách project templates, chọnASP.NET Web Application (.NET Framework). Đặt tên cho project là "BooksAPI".

Trong hộp thoạiNew ASP.NET Web Application, lựa chọnEmptytemplate. Dưới phần "Add folders and core references for", lựa chọnWeb APIcheckbox. ClickOK.

Vậy là chúng ta đã tạo ra một skeleton project (khung project) đã có sẵn cấu hình cho các chức năng của Web API.

Domain Models

Tiếp theo, ta sẽ viết các class cho phần models. Trong Solution Explorer, click chuột phải vào thư mục Models. ChọnAdd, sau đó chọnClass. Đặt tên class làAuthor.

Trong Author.cs viết code như sau:


dfdf

using System.ComponentModel.DataAnnotations; namespace BooksAPI.Models { public class Author { public int AuthorId { get; set; } [Required] public string Name { get; set; } } }

Sau đó add thêm class mới tên làBook. Code như sau:


using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace BooksAPI.Models { public class Book { public int BookId { get; set; } [Required] public string Title { get; set; } public decimal Price { get; set; } public string Genre { get; set; } public DateTime PublishDate { get; set; } public string Description { get; set; } public int AuthorId { get; set; } [ForeignKey("AuthorId")] public Author Author { get; set; } } }

Thêm Web API Controller

Trong bước này, chúng ta sẽ add thêm Web API controller, nó sẽ sử dụng Entity Framework đóng vai trò là data layer (lớp xử lý dữ liệu).

Nhấn phím CTRL+SHIFT+B để build dự án. Entity Framework sử dụng để reflection để khám phá các thuộc tính của model, do đó nó yêu cầu một assembly đã được biên dịch để tạo ra database schema.

Trong Solution Explorer, right-click vào Controllers folder. ChọnAdd, sau đó chọnController.

Trong hộp thoạiAdd Scaffold, chọnWeb API 2 Controller with actions, using Entity Framework.

Trong hộp thoạiAdd Controller, phầnController name, viết "BooksController". Tích chọn "Use async controller actions" checkbox. Dành choModel class, lựa chọn "Book". (Nếu bạn không nhìn thấy Bookclass được liệt kê trong dropdown, thì có lẽ bạn chưa build project, do vậy hãy rebuild lại project.) Sau đó click vào nút "+".

ClickAddtrongNew Data Contextdialog.

ClickAdd trong Add Controllerdialog. Đặt tên cho class Controller làBooksControllernó định nghĩa API controller. Nó sẽ thêm vào một class tênBooksAPIContexttrong Models folder, nó định nghĩa data context cho Entity Framework.

Seed the Database

Từ Tools menu, chọnNuGet Package Manager, và sau đó lựa chọnPackage Manager Console.

Trong cửa sổ Package Manager Console, nhập lệnh sau:


Add-Migration

Lệnh này sẽ tạo ra Migrations folder và add thêm file code mới có tên là Configuration.cs.
Mở file này và sau đó add đoạn code sau vào phương thức Configuration.Seed.


protected override void Seed(BooksAPI.Models.BooksAPIContext context) { context.Authors.AddOrUpdate(new Author[] { new Author() { AuthorId = 1, Name = "Ralls, Kim" }, new Author() { AuthorId = 2, Name = "Corets, Eva" }, new Author() { AuthorId = 3, Name = "Randall, Cynthia" }, new Author() { AuthorId = 4, Name = "Thurman, Paula" } }); context.Books.AddOrUpdate(new Book[] { new Book() { BookId = 1, Title= "Midnight Rain", Genre = "Fantasy", PublishDate = new DateTime(2000, 12, 16), AuthorId = 1, Description = "A former architect battles an evil sorceress.", Price = 14.95M }, new Book() { BookId = 2, Title = "Maeve Ascendant", Genre = "Fantasy", PublishDate = new DateTime(2000, 11, 17), AuthorId = 2, Description = "After the collapse of a nanotechnology society, the young" + "survivors lay the foundation for a new society.", Price = 12.95M }, new Book() { BookId = 3, Title = "The Sundered Grail", Genre = "Fantasy", PublishDate = new DateTime(2001, 09, 10), AuthorId = 2, Description = "The two daughters of Maeve battle for control of England.", Price = 12.95M }, new Book() { BookId = 4, Title = "Lover Birds", Genre = "Romance", PublishDate = new DateTime(2000, 09, 02), AuthorId = 3, Description = "When Carla meets Paul at an ornithology conference, tempers fly.", Price = 7.99M }, new Book() { BookId = 5, Title = "Splish Splash", Genre = "Romance", PublishDate = new DateTime(2000, 11, 02), AuthorId = 4, Description = "A deep sea diver finds true love 20,000 leagues beneath the sea.", Price = 6.99M}, }); }

Trong cửa sổ Package Manager Console, viết lệnh sau vào:


add-migration Initial update-database

Các câu lệnh này sẽ tạo ra local database và gọi phương thức Seed để xuất bản dữ liệu.

Video liên quan

Chủ đề