Chuyển đến nội dung chính

Tìm hiểu về Azure Cosmos DB

Azure Cosmos DB bắt đầu với tên gọi Dự án Florence Florence năm 2010 để giải quyết các điểm đau của nhà phát triển phải đối mặt với các ứng dụng quy mô lớn trong Microsoft. Quan sát rằng những thách thức trong việc xây dựng các ứng dụng phân tán trên toàn cầu không phải là vấn đề duy nhất đối với Microsoft, năm 2015 microsoft đã tạo ra thế hệ đầu tiên của công nghệ này cho các nhà phát triển Azure dưới dạng Azure DocumentDB. Kể từ đó, Microsoft đã thêm dần các tính năng mới và giới thiệu các khả năng mới quan trọng. Năm 2017, Microsoft đã đổi tên Azure DocumentDB thành Azure Cosmos DB và mở rộng hỗ trợ cho nhiều giao diện cơ sở dữ liệu khác nhau

Comos db có nhiều ưu điểm như:

  • Tốc độ và thông lượng cao với độ trễ thấp và phân phối dữ liệu toàn cầu.
  • Khả năng mở rộng tự động và linh hoạt theo nhu cầu của ứng dụng.
  • Hỗ trợ nhiều giao diện cơ sở dữ liệu như NoSQL, MongoDB, PostgreSQL, Apache Cassandra, Apache Gremlin và Table.
  • An ninh và tính sẵn sàng cao với sao lưu liên tục và bảo mật doanh nghiệp.

Comos db hỗ trợ nhiều API cơ sở dữ liệu như

  • SQL API: Interacts with a document database for JSON data.
  • MongoDB API: Existing MongoDB applications can leverage this API to interact with Cosmos DB.
  • Table API: Supports Azure table storage operations.
  • Gremlin API: Used to interact with graph data.
  • Cassandra API: Existing Apache Cassandra applications (for example, apps dependent on column stores) can use this API to work with Cosmos DB.

Cài đặt

Có 2 cách: 
  • Azure Subscription
  • Azure Cosmos DB Emulator

Azure Subscription

Azure Cosmos DB Emulator

Azure Cosmos Emulator là một môi trường localhost, giả lập dịch vụ Azure Cosmos DB để phát triển ứng dụng. 
Sau khi cài đặt Azure Comos DB Emulator, bạn mở lên, sẽ thấy giao diện như sau:

  • QuickStart: Bạn sẽ lấy chuỗi connection string hoặc URI - Primary Key ở đây
  • Exporer: Quản lý Database, Container

Database và container

Database trong cosmos DB là một nhóm các container. Container là nơi lưu trữ dữ liệu. 

Dữ liệu trong container được chia thành các phân vùng logic dựa trên partiton key mà bạn chọn khi tạo container.

Partiton key là một thuộc tính của dữ liệu giúp cosmos DB phân phối dữ liệu hiệu quả giữa các phân vùng. Partiton key quyết định vị trí của các tài liệu.

Để chọn partiton key tốt, bạn cần xem xét các yếu tố sau:
  • Partiton key nên có nhiều giá trị khác nhau để phân bổ dữ liệu đồng đều giữa các phân vùng logic và vật lý.
  • Partiton key nên phù hợp với các truy vấn thường xuyên của bạn để tránh truy vấn qua nhiều phân vùng.
  • Partiton key nên có khả năng mở rộng ngang khi dữ liệu tăng lên.
  • Partiton key nên được chọn trước khi tạo bảng vì không thể thay đổi sau đó.
Một số ví dụ về partiton key tốt là: Location, CustomerId, Team, Category, .. 
Bạn cũng có thể sử dụng partiton key tổng hợp (synthetic) bằng cách kết hợp nhiều thuộc tính lại với nhau. 
Ví dụ: TenantId + UserId + SessionId.
Dưới đây là đoạn code kiểm tra và tạo Database và Container nếu chưa tồn tại
/// <summary>
/// Create the database if it does not exist
/// </summary>
private async Task CreateDatabaseAsync()
{
	// Create a new database
	this.database = await this.cosmosClient.CreateDatabaseIfNotExistsAsync(databaseId);
	Console.WriteLine("Created Database: {0}\n", this.database.Id);
}
// </CreateDatabaseAsync>

// <CreateContainerAsync>
/// <summary>
/// Create the container if it does not exist. 
/// Specifiy "/LastName" as the partition key since we're storing family information, to ensure good distribution of requests and storage.
/// </summary>
/// <returns></returns>
private async Task CreateContainerAsync()
{
	// Create a new container
	this.container = await this.database.CreateContainerIfNotExistsAsync(containerId, "/LastName", 400);
	Console.WriteLine("Created Container: {0}\n", this.container.Id);
}

Primary key

Trong Cosmos DB, Primary key là sự kết hợp giữa PartitionKeyRowKey.
Primary key không ảnh hưởng đến hiệu suất của cosmos DB, trong khi partition key ảnh hưởng rất nhiều. Bạn nên chọn một partition key có nhiều giá trị khác nhau để phân bổ dữ liệu đồng đều giữa các phân vùng và tránh các truy vấn qua nhiều phân vùng

Ví dụ: nếu bạn có một container lưu trữ thông tin người dùng, bạn có thể chọn Department làm partition key và Id làm item id. Khi đó, primary key của mỗi item sẽ là Department + Id

Ví dụ

Trong ví dụ này, mình làm chức năng thêm sửa xóa sử dụng WebPI

Cài đặt package: Microsoft.Azure.Cosmos

Tạo Employee Model

public class EmployeeModel
{
	public string? id
	{
		get;
		set;
	}
	public string? Name
	{
		get;
		set;
	}
	public string? Country
	{
		get;
		set;
	}
	public string? City
	{
		get;
		set;
	}
	public string? Department
	{
		get;
		set;
	}
	public string? Designation
	{
		get;
		set;
	}
	public DateTime? JoiningDate
	{
		get;
		set;
	}
}
Delete Database

/// <summary>
/// Delete the database and dispose of the Cosmos Client instance
/// </summary>
private async Task DeleteDatabaseAndCleanupAsync()
{
	DatabaseResponse databaseResourceResponse = await this.database.DeleteAsync();
	// Also valid: await this.cosmosClient.Databases["FamilyDatabase"].DeleteAsync();

	Console.WriteLine("Deleted Database: {0}\n", this.databaseId);

	//Dispose of CosmosClient
	this.cosmosClient.Dispose();
}
// </DeleteDatabaseAndCleanupAsync>
Tạo connect với container:
Container containerClient = cosmosDbClient.GetContainer(CosmosDbName, CosmosDbContainerName);
return containerClient;
Thêm 1 item vào container
var container = ContainerClient();
var response = await container.CreateItemAsync(employee, new PartitionKey(employee.Department));
return Ok(response);
Lấy 1 item lên
var container = ContainerClient();
var response = await container.ReadItemAsync<EmployeeModel>(employeeId, new PartitionKey(partitionKey));
Update item:
var container = ContainerClient();
var res = await container.ReadItemAsync<EmployeeModel>(emp.id, new PartitionKey(partitionKey));
//Get Existing Item
var existingItem = res.Resource;
//Replace existing item values with new values
existingItem.Name = emp.Name;
existingItem.Country = emp.Country;
existingItem.City = emp.City;
existingItem.Department = emp.Department;
existingItem.Designation = emp.Designation;
var updateRes = await container.ReplaceItemAsync(existingItem, emp.id, new PartitionKey(partitionKey));
Cuối cùng là xóa 1 item
var container = ContainerClient();
var response = await container.DeleteItemAsync<EmployeeModel>(empId, new PartitionKey(partitionKey));
Tổng hợp lại, chúng ta sẽ có đoạn code như sau:
[Route("api/[controller]/[action]")]
[ApiController]
public class EmployeeController : ControllerBase
{
	// Cosmos DB details, In real use cases, these details should be configured in secure configuraion file.
	private readonly string CosmosDBAccountUri = "https://localhost:8081";
	private readonly string CosmosDBAccountPrimaryKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
	private readonly string CosmosDbName = "EmployeeManagementDB";
	private readonly string CosmosDbContainerName = "Employees";

	/// <summary>
	/// Commom Container Client, you can also pass the configuration paramter dynamically.
	/// </summary>
	/// <returns> Container Client </returns>
	private Container ContainerClient()
	{
		var cosmosDbClient = new CosmosClient(CosmosDBAccountUri, CosmosDBAccountPrimaryKey);
		Container containerClient = cosmosDbClient.GetContainer(CosmosDbName, CosmosDbContainerName);
		return containerClient;
	}

	[HttpPost]
	public async Task<IActionResult> AddEmployee(EmployeeModel employee)
	{
		try
		{
			var container = ContainerClient();
			var response = await container.CreateItemAsync(employee, new PartitionKey(employee.Department));
			return Ok(response);
		}
		catch (Exception ex)
		{
			return BadRequest(ex.Message);
		}
	}
	[HttpGet]
	public async Task<IActionResult> GetEmployeeDetails()
	{
		try
		{
			var container = ContainerClient();
			var sqlQuery = "SELECT * FROM c";
			var queryDefinition = new QueryDefinition(sqlQuery);
			var queryResultSetIterator = container.GetItemQueryIterator<EmployeeModel>(queryDefinition);
			var employees = new List<EmployeeModel>();
			while (queryResultSetIterator.HasMoreResults)
			{
				FeedResponse<EmployeeModel> currentResultSet = await queryResultSetIterator.ReadNextAsync();
				foreach (EmployeeModel employee in currentResultSet)
				{
					employees.Add(employee);
				}
			}
			return Ok(employees);
		}
		catch (Exception ex)
		{
			return BadRequest(ex.Message);
		}
	}
	[HttpGet]
	public async Task<IActionResult> GetEmployeeDetailsById(string employeeId, string partitionKey)
	{
		try
		{
			var container = ContainerClient();
			ItemResponse<EmployeeModel> response = await container.ReadItemAsync<EmployeeModel>(employeeId, new PartitionKey(partitionKey));
			return Ok(response.Resource);
		}
		catch (Exception ex)
		{
			return BadRequest(ex.Message);
		}
	}
	[HttpPut]
	public async Task<IActionResult> UpdateEmployee(EmployeeModel emp, string partitionKey)
	{
		try
		{
			var container = ContainerClient();
			var res = await container.ReadItemAsync<EmployeeModel>(emp.id, new PartitionKey(partitionKey));
			//Get Existing Item
			var existingItem = res.Resource;
			//Replace existing item values with new values
			existingItem.Name = emp.Name;
			existingItem.Country = emp.Country;
			existingItem.City = emp.City;
			existingItem.Department = emp.Department;
			existingItem.Designation = emp.Designation;
			var updateRes = await container.ReplaceItemAsync(existingItem, emp.id, new PartitionKey(partitionKey));
			return Ok(updateRes.Resource);
		}
		catch (Exception ex)
		{
			return BadRequest(ex.Message);
		}
	}
	[HttpDelete]
	public async Task<IActionResult> DeleteEmployee(string empId, string partitionKey)
	{
		try
		{
			var container = ContainerClient();
			var response = await container.DeleteItemAsync<EmployeeModel>(empId, new PartitionKey(partitionKey));
			return Ok(response.StatusCode);
		}
		catch (Exception ex)
		{
			return BadRequest(ex.Message);
		}
	}
}

Tham khảo

Tìm hiểu về Azure Cosmos DB trong Microsoft Azure. 

CRUD Operations In Azure Cosmos DB SQL API Using ASP.NET Core Web API C# 

Nhận xét

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.