Thiết kế RESTful APIs

Hôm rồi thấy anh bạn share document về RESTful APIs của Shoptify, tự dưng nghĩ đến hình như chưa có bài viết nào bằng tiếng Việt về cách xây dựng RESTful APIs mặc dù vấn đề này đã khá phổ biến. Vậy nên tôi viết bài này, chia sẻ một số best practices mà chúng tôi đã thảo luận và áp dụng khi thiết kế và xây dựng APIs cho sản phẩm của mình.

Tại sao API lại quan trọng trong mỗi sản phẩm? Để hiểu đơn giản, bạn có thể tham khảo bài viết này, dù không mới nhưng có một tiêu đề và nội dung rất hay. “Trong thế giới kết nối như bây giờ, một sản phẩm không thể đứng độc lập, và sản phẩm nào không có APIs, giống như máy tính không được kết nối Internet vậy”.

RESTful

Tôi không phân tích tại sao lại là RESTful, vì chủ đề này có lẽ cần một bài viết khác, nhưng nếu có cũng thành thừa vì gần như chúng ta đều đồng ý với nhau rằng APIs trong thời đại ngày nay sử dụng tiêu chuẩn RESTful.

Nhưng nếu chúng ta đã quyết định sử dụng RESTful cho APIs, thì nên hiểu đúng và làm đúng. RESTful có thể tóm lược như sau:

  • URI như tên gọi (Uniform Resource Identifier), chỉ định địa chỉ của mỗi tài nguyên (resource). VD: api.shop.me/customer hoặc api.shop.me/order
  • Các method của HTTP được sử dụng tương ứng với những hành động CRUD trên resource tương ứng. Thông thường: Create – POST, Read – GET, Update – PUT / PATCH, Delete – DELETE

Hầu hết những webservice cũ chỉ sử dụng 2 phương thức GET và POST và để đơn giản với người sử dụng; nhưng lại sử dụng URI cho cả entity lẫn function. Đừng mang tư tưởng đó khi thiết kế RESTful.

Tại sao là entity?

Webservice theo chuẩn cũ coi resource gồm business entity và method / function nhưng thực sự thì method / function không nên là resource, chỉ business entity là resource như RESTful định nghĩa mà thôi. Nhưng tại sao lại “quy hoạch” theo business entity? Vì dễ hơn. Mọi lập trình viên đều dễ làm việc với entity hơn. Chẳng phải ai cũng bắt đầu với một nghiệp vụ với CRUD một entity đó sao? Trong một hệ thống thông thường, các entity có số lượng ít và “đơn giản”, dễ hiểu hơn các nghiệp vụ (method / function). Cung cấp danh sách các entity cùng ít hoạt động CRUD nhằm ẩn đi những nghiệp vụ phức tạp (thường là lời gọi tới hàng chục method), giúp các lập trình viên sử dụng đơn giản hơn.

Ví dụ, để cập nhật một đơn hàng, chúng ta chỉ đơn giản gọi tới API http://api.shop.me/orders/10với method PUT kèm theo những thông tin mới, thay vì phải đắn đo giữa một loạt những API nhưhttp://api.shop.me/updateOrderhttp://api.shop.me/replaceOrder… với hàng loạt những ràng buộc về nghiệp vụ.

JSON only?

Hầu hết những nhóm viết RESTful API giờ đây đều chọn JSON là format chính thức nhưng rất nhiều nhóm vẫn phân vân với câu hỏi “chỉ JSON hay hỗ trợ thêm XML?”. Tất nhiên, có hàng tá lý do để chúng ta hỗ trợ thêm những format khác, đặc biệt là XML. Nhưng theo tôi, chỉ cần JSON là đủ. Tôi không tin rằng ngày nay có lập trình viên hay nền tảng nào không biết hoặc không hỗ trợ JSON. Hỗ trợ nhiều định dạng chỉ làm cho việc kiểm thử API thêm phức tạp.

gzip và binary format?

Có chăng chúng ta nên nghĩ tới việc sử dụng những format dạng binary để tiết kiệm dữ liệu khi truyền tải qua mạng như BSON, MessagePack… Tuy vậy, hãy để chúng là optional, và người dùng có thể định nghĩa format nhận thông qua HTTP Header.

Tương tự là câu hỏi “formatted hay compressed JSON? Có nên enable gzip?” (formatted (hay pretty) JSON là JSON được thể hiện ở định dạng “đẹp”, được format bởi tab…; ngược lại compressed JSON loại bỏ hết các dấu tab, space, enter.. nhằm giảm dung lượng). Compressed JSON và gzip rất tuyệt vời vì có thểm giảm từ 30-70% dung lượng truyền tải. Đồng nghĩa với server sẽ tốn tài nguyên và thời gian để xử lý việc nén. Và với tốc độ mạng ngày càng được cải thiện như hiện nay, có lẽ đặt formatted JSON là mặc định thì tốt hơn. Tuỳ vào điều kiện network (VD trên mobile…), client chỉ định định dạng muốn nhận về thông qua HTTP Header (Content-Type hoặc Accept).

snake_case hay camelCase?

Well, câu trả lời muôn thủa, kiểu như “thế giới có 2 loại người…”. Việc sử dụng snake case hay camel case chủ yếu do sở thích của lập trình viên thôi, không có lý do gì để phân định được. Camel case tiết kiệm hơn snake case (1 ký tự, orderName so với order_name) là lý do không đủ thuyết phục.

Tôi thích snake_case. Vì có vẻ snake_case dễ đọc hơn.

dash hay underscore (- hay_)?

Hmm, tương tự như trên. Theo bạn thì http://api.shop.me/orders/last-updatedhay http://api.shop.me/orders/last_updated sẽ tốt hơn? Tất nhiên, http://api.shop.me/orders/lastUpdated thì quá tệ, đừng đưa nó vào danh sách lựa chọn của bạn.

Số ít hay số nhiều?

Tương tự như trên. Để liệt kê các đơn đặt hàng, chúng ta sẽ đặt URI là http://api.shop.me/orderhayhttp://api.shop.me/orders?

Chuyện này giống như chúng ta cãi nhau về cách đặt tên bảng trong CSDL quan hệ vậy, OrderhayOrders? Best practice cho việc đặt tên bảng trong CSDL quan hệ là sử dụng số ít: Order; nhưng trong api là số nhiều: /orders. Lý do là nó tường minh và gợi nhớ hơn cho người dùng rằng đây là “danh sách đơn hàng”. Rõ ràng, nó tường minh hơn /order (danh sách đơn hàng) và /profile (thông tin người dùng); cùng là dạng số ít nhưng cấu trúc dữ liệu trả về lại khác nhau.

Câu chuyện phức tạp hơn một chút với /persons hay /people :). Hãy sử dụng /persons.

Quan hệ

Đây là vấn đề đau đầu nhất. Bài toán thế này: Một đơn hàng thuộc về một khách hàng và có nhiều sản phẩm. Vậy thông tin đơn hàng trả về sẽ có những gì? Nếu bạn sử dụng CSDL quan hệ thì chắc chắn là bạn đang lưu những thông tin này trong 3 bảng: OrderCustomer và Product. Trả về một entity có những thông tin giống hệt như bảng Order là điều ngu ngốc. Trả về một entity có  những thông tin được JOIN từ 3 bảng trên còn ngu ngốc hơn. Tại sao? Bởi vì nếu thể hiện đúng data model thì chúng ta cần API để làm gì? Thử tưởng tượng dạng đơn giản nhất của entity Order thế này:

Và để hiển thị thông tin đơn hàng có tên và địa chỉ khách hàng, tên sản phẩm, client (APIs consumer) cần gọi thêm hàng chục API nữa. Bạn cũng hiểu tại sao không nên trả về thông tin đơn hàng được JOIN từ 3 bảng: có hàng trăm thông tin không cần thiết. Việc cân bằng, tìm ra những thông tin nào nên được trả về trong tình huống này không thực sự đơn giản. Ý tưởng ở đây là API nên trả về thông tin “thiết yếu, liên quan trực tiếp tới nghiệp vụ”. Ví dụ trong tình huống này là:

http://api.shop.me/orders/10

Nếu client cần thêm thông tin về khách hàng, gọi tới API http://api.shop.me/customers/105

Nếu client cần thêm thông tin về sản phẩm cho order này, gọi tới APIhttp://api.shop.me/orders/10/products

Và theo trí tưởng tượng phong phú, bạn có thể nghĩ tới việc để client gọi tới APIhttp://api.shop.me/orders/10/products/101 để lấy thông tin chi tiết của sản phẩm có mã 101. Câu trả lời là không. Nếu cần thông tin đầy đủ, client hãy gọi tới API http://api.shop.me/products/101.

Ở đây tôi chỉ dám đưa ra ý tưởng và ví dụ cơ bản, bởi “thông tin bổ sung” trong quan hệ của những entity là vấn đề thực sự không đơn giản và phụ thuộc rất nhiều vào từng nghiệp vụ cụ thể.

Pagination

Hãy luôn sử dụng pagination. Trả về đầy đủ danh sách khách hàng qua APIhttp://api.shop.me/customers là việc tốn kém tài nguyên, đồng thời không hữu dụng. Bởi client cũng sẽ giới hạn lại danh sách này nhằm đáp ứng một giao diện dễ nhìn cho người dùng.

Hãy để thêm những param cố định trong mỗi API trả về một danh sách dữ liệu như /customers,/orderspage, page_size… để client có thể chỉ định http://api.shop.me/orders?page=4&page_size=10. Luôn sử dụng page_size mặc định để giới hạn dữ liệu trả về ngay cả khi người dùng không chỉ định rõ trong lời gọi API.

Tất nhiên, pagination thì có hàng chục cách, trên đây chỉ là một ví dụ.

Filter và dynamic field, dynamic query

Có một cách để giải quyết vấn đề trả về những dữ liệu quan hệ như trên là sử dụng dynamic field. Ví dụ http://api.shop.me/orders?fields=id,time,customer.fullname,product.id

Tương tự với dynamic query để thực hiện filter với những điều kiện truy vấn xác định. Trước đây, chúng tôi sử dụng OData và nó khá hiệu quả.

Ngoài ra, còn 1 khái niệm nữa, là embed: gắn thêm những thông tin liên quan. Ví dụ:

http://api.shop.me/order/10

http://api.shop.me/orders/10?embed=customer

Nhưng embed là một vấn đề khó, đừng chú tâm vào nó nếu không cần thiết.

Metadata

Luôn trả về metadata với dữ liệu dạng danh sách, dựa vào đó client cũng biết được các truy xuất vào những thông tin khác. Ví dụ: http://api.shop.me/orders

Partial update?

Một đơn hàng (entity Order) chứa 10 field, điều gì xảy ra nếu chúng ta chỉ muốn chỉnh sửa 2 field? Có 2 cách thông thường:

  1. Giữ nguyên dữ liệu được trả về từ API gọi qua GET, với 2 field được chỉnh sửa và gọi API qua PUT
  2. Set  dữ liệu đúng cho 2 field này, set các field còn lại thành null (hoặc zero, empty… – dấu hiệu nhận biết là field này không thay đổi) và gọi API qua PUT

Cả 2 cách này đều cần gửi lên đầy đủ format của entity. Một cách khác, chỉ cần gửi lên 1 object với đúng 2 field trên, gọi là partial update, thường sử dụng method PATCH.

Partial update rất hữu dụng, nhưng thường có 2 vấn đề:

  1. Việc deserialise ở phía server để ra đúng object không quá đơn giản
  2. Đôi khi gây bối rối cho người sử dụng API, đặc biệt với các required field.

Do đó, partial update nên được coi là phần advanced của API và nên xuất hiện ở version 2, 3.. Trước đây chúng tôi sử dụng OData khá tốt (nhưng không thực sự hoàn hảo) cho partial update. Quan tâm tới vấn đề này ngay từ ngày đầu thiết kế API khiến chúng tôi mất khá nhiều thời gian.

Status code

Luôn sử dụng HTTP code cho catched exception. Thay vì trả về message “Unauthorised” với HTTP code = 200, hãy trả về 401. Bạn có thể tìm hiểu thêm ở đây: http://www.restapitutorial.com/httpstatuscodes.html

Nói chung, RESTful APIs chỉ nên dùng những HTTP code 4xx cho những catched exception (unauthorised, validation…). Nếu những lỗi này là không đủ, hãy mở rộng chúng và đặt tham chiếu trong tài liệu API. Ví dụ, lỗi validation thường sử dụng HTTP code là 400 (bad request) hoặc 409 (conflict), cho lời gọi PUT http://api.shop.me/orders/10 trả về HTTP code = 409 cùng message (10xx dùng để validate order, tương tự 11xx cho customer, 24xx cho product…)

Authorization

Bởi RESTful là stateless HTTP nên đừng làm nóng server và client bởi session hay cookies. Cách đúng đắn nhất là sử dụng Authorization Header. OAuth 2 là chuẩn được khuyến nghị cho RESTful APIs.

Versioning

Vấn đề này với RESTful APIs phải nói là “hay dữ dội”. Sự phức tạp này đến từ việc khác nhau giữa client-side APIs và server-side APIs: khi sử dụng thư viện trên client, chúng ta hoàn toàn chủ động trong việc chọn version “gắn chặt” nó với chương trình, nhưng gọi qua RESTful thì không phải như vậy. Khi một version mới của RESTful API ra đời, version cũ sẽ sớm “chết”.

Có 3 cách thường dùng để versioning RESTful API: 2 trong số đó là versioning qua URI: qua sub-domain (http://v1.api.shop.me/orders) hoặc resource path (http://api.shop.me/orders/v1). Cả 2 cách đều dở như nhau vì nó khiến phía client bị hard-code. Cách versioning qua resource path tệ hơn, vì /v1 vốn không phải là 1 property của /orders. Còn nếu bạn dùng http://api.shop.me/v1/orders thì hãy dùng sub-domain, nó còn tuyệt hơn.

Cách thứ 3 là versioning thông qua HTTP Header (VD: API-Version=v1.1) sẽ tốt hơn nhưng ít người sử dụng, chỉ bởi việc phải đặt thêm HTTP header khiến người dùng không dễ copy-paste URI vào web browser để test.

Một cách tiếp cận khác là “không sử dụng versioning”, nghĩa là cố gắng hỗ trợ việc tương thích ngược hoặc xác định rõ thời điểm cung cấp API mới và yêu cầu người dùng phải migrate lên version mới trong một khoảng thời gian xác định. Đây là cách Google, Facebook.. đang làm. Nhưng phải nói thật là, chỉ khi nào “chúng ta lớn” hoặc có sản phẩm cùng RESTful APIs là trung tâm cuộc chơi về kết nối, tích hợp thì mới dễ thực hiện theo phương pháp này.

Tuy nhiên cũng chẳng có vấn đề gì nếu bạn bỏ qua việc versioning từ phiên bản APIs đầu tiên, hoặc versioning thông qua HTTP Header (nếu API-Version không được chỉ định, sử dụng version mới nhất). Nếu chúng ta không làm tốt version đầu tiên thì chắc gì đã cần tới v2, v3… thậm chí là v1.1 🙂

HATEOAS

HATEOAS là một trong những chuẩn được khuyến nghị cho RESTful APIs và bạn nên tận dụng. Ví dụ một bài toán: Khi lấy thông tin chi tiết của 1 đơn hàng (entity Order), người dùng hiện tại (nhận biết qua Access Token) chỉ được xem và cập nhật, không được xoá; làm sao client nhận biết được những action này? Cách đơn giản là sử dụng HATEOAS, gắn những hyperlink trong giá trị trả về tương ứng với những hành động có thể thực hiện trên entity.

http://api.shop.me/orders/10

HATEOAS rất hữu dụng nhưng như bạn thấy, bandwidth dành cho nó cũng không dễ chịu chút nào; tương tự với việc xử lý để tính toán ra những hành động, hyperlink tương ứng. Nếu không cần thiết, hãy hỗ trợ HATEOAS là optional thông qua header.

Overriding

Đừng ngần ngại overriding nếu cần thiết. HATEOAS không được liệt kê trong default HTTP header, hãy tự định nghĩa. Tương tự với HTTP method nếu thấy cần thiết. Nhưng hãy nhớ là cần thiết, đừng override Authorization thành shop.me-Authorization nếu bạn không biết mình đang làm gì.

Tài liệu

RESTful APIs phải có khả năng self-described hay self-documented; nghĩa là người dùng nhìn vào là biết cách dùng ngay. Tuy vậy, vẫn có những thứ bạn cần viết ra thành tài liệu như: authorization, HTTP header, filter, pagination… và nếu gói gọn trong Getting Started là tốt nhất. Đẹp nhất là người dùng có thể gọi thành công 1 API trong dưới 15 phút thông qua những công cụ đơn giản như curl, Fiddler, REST Console… ngay khi đọc xong Getting Started.

Có những ngôn ngữ / công cụ rất hay như Swagger để viết APIs document, thậm chí là generate document từ code và cho phép người dùng hand-on với APIs. Nhưng hãy cẩn thận, có lẽ bạn cần sandbox environment để tránh người dùng quá “hứng thú” trong việc khám phá APIs của mình.

Cuối cùng

Trên đây tôi chỉ điểm qua một số best practices bạn nên lưu ý khi thiết kế RESTful APIs. Bạn có thể đọc thêm cuốn sách này, không quá hay đâu nhưng có nhiều góc nhìn hơn.

Tuy vậy, trên tất cả, hãy nhớ rằng “Với một lập trình viên backend, thiết kế APIs chính là thiết kế giao diện. Đừng đi ngược xu thế UX, hãy thiết kế chúng thật đơn giản, hữu dụng và mạnh mẽ”.Những lập trình viên có thể kiên trì hơn người dùng cuối, nhưng bạn cũng chỉ có không quá 30 phút để cho họ hiểu cách sử dụng APIs của mình.

 

Link this post: https://techtalk.vn/thiet-ke-restful-apis.html

C# – Kĩ thuật Reflection trong .Net

Đối với nhiều người, reflection là một thuật ngữ lạ và ít khi được nhắc tới. Nhưng thực tế, thuật ngữ này đã xuất hiện và được áp dụng vào khá nhiều ngôn ngữ bậc cao phổ biến như C#, Java, Perl, PHP,…Vậy reflection là gì, và nó có công dụng gì trong việc lập trình hiện nay?

Để định nghĩa, trước tiên hãy thử hình dung một trường hợp là làm sao để thay đổi giá trị của 1 biến khi người dùng nhập tên biến vào lúc chương trình đang thực thi, hoặc làm sao để tạo một instance của form nếu chỉ sử dụng tên form?

Nếu chưa từng nghe qua về các khái niệm như reflection, assembly, disassembly có thể bạn cho rằng đây là một điều không thể. Tất nhiên trong .Net điều này là có thể nhờ chức năng reflection.

Đây là ví dụ dùng reflection để hiển thị thông tin các assembly, namespace, type,… trong C#. Bạn hoàn toàn có thể tự viết được chương trình dạng này sau khi nắm vững hết những vấn đề mà tôi trình bày bên dưới.

Download Demo (14KB)

Y2 ReflectorDemo1.0

I.  Định nghĩa

Reflection được hiểu là một chức năng trong .Net cho phép đọc thông tin từ các siêu dữ liệu (metadata) của assembly để tạo ra một đối tượng (có kiểu là Type) bao gói các thông tin đó lại. Với reflection, bạn có thể trích xuất để gọi và tạo ra các phương thức, truy cập và thay đổi các thuộc tính của đối tượng một cách linh động trong quá trình runtime.

II.  Lớp System.Type

Lớp System.Type là một abstract class đại diện cho các kiểu dữ liệu (kiểu lớp, interface, mảng, giá trị,…), đây là lớp chính để thực hiện các cơ chế Reflection đại diện cho các kiểu dữ liệu trong .Net. Bằng cách sử dụng lớp này, bạn có thể lấy về tất cả thông tin về các kiểu dữ liệu, phương thức, thuộc tính, sự kiện,… Ngoài ra ta còn có thể tạo ra các instance của kiểu dữ liệu và thực thi các phương thức của chúng, kĩ thuật này còn được gọi với thuật ngữ Late Binding.

Khi sử dụng lớp này bạn hãy thêm namespace System.Reflection vào vì các thành viên của System.Type hầu hết đều liên quan tới các lớp trong namespace này.

1. Phương thức GetType() và toán tử typeof:

Bạn có thể dùng phương thức GetType() của lớp Object để trả về đối tượng kiểu Type mô tả kiểu dữ liệu của đối tượng. Có được đối tượng Type này rồi, ta sẽ lấy thông tin của kiểu dữ liệu qua các thuộc tính của lớp Type.

Sau đây là một ví dụ đơn giản minh họa cách in ra tên kiểu dữ liệu của các biến qua thuộc tính FullName của lớp Type:

1
2
3
4
5
6
7
8
9
10
static void Main(string[] args)
{
    var number = 100;
    var text = "abc";
    Console.WriteLine(number.GetType().FullName);
    Console.WriteLine(text.GetType().FullName);
    Console.Read();
}

Phương thức GetType() chỉ có thể lấy được thông tin từ các biến đối tượng. Trong trường hợp muốn lấy thông tin của lớp thông qua tên lớp, bạn phải sử dụng phương thức tĩnh Type.GetType(), tuy nhiên tham số truyền vào cần phải ghi đầy đủ cả namespace.

1
2
3
4
5
6
7
8
9
10
11
static void Main()
{
    // Không được ghi tham số là string, int hoặc Int32
    Type mType1 = Type.GetType("System.Int32");
    Type mType2 = Type.GetType("System.String");
    Console.WriteLine(mType1.FullName);
    Console.WriteLine(mType2.FullName);
    Console.Read();
}

Sử dụng toán tử typeof, bạn có thể lấy về đối tượng kiểu System.Type của bất kì kiểu nào với cú pháp typeof(type).

1
2
3
4
5
6
7
8
static void Main()
{
    Console.WriteLine(typeof(Int32).FullName);
    Console.WriteLine(typeof(String).FullName);
    Console.Read();
}

Cả ba ví dụ trên đều cho ra cùng kết quả khi được thực thi:

System.Int32
System.String

2. Lấy các thông tin từ Type

Lớp Type cung cấp đầy đủ các phương thức cho phép lấy các thông tin của kiểu dữ liệu. Các phương thức này có dạng GetXXX(), mỗi phương thức trả về một hay một mảng đối tượng lưu trữ thông tin của mỗi thành viên trong kiểu dữ  liệu (bạn có thể nhận biết điều này thông qua cách đặt tên của các phương thức ở dạng số nhiều hay số ít). Các kiểu trả về của các phương thức này đều có hậu tố là Info: ConstructorInfo, EventInfo, FieldInfo, InterfaceInfo, MemberInfo, MethodInfo, PropertyInfo.

Ví dụ để lấy tất cả thành viên public của một lớp ta dùng phương thức GetMembers() như minh họa dưới đây:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Program
{
    class MyClass
    {
        // Chỉ có mục đích minh họa
        public string Name { get; set; }
        public static int theValue;
        public void SayHello() {}
    }
    static void Main()
    {
        Type mType = typeof(MyClass);
        MemberInfo[] members = mType.GetMembers();
        Array.ForEach(members,mem =>
            Console.WriteLine(mem.MemberType.ToString().PadRight(12) + ": " + mem)
        );
        Console.Read();
    }
}

Khi chạy đoạn mã này bạn sẽ nhận được kết quả như sau:

GetMembersResult

3. Ví dụ về MethodInfo: thực thi một phương thức

Ta sẽ trở lại với phần mở đầu của bài viết mà tôi nói về trường hợp thực thi một phương thức dựa vào tên mà người dùng truyền vào. Hãy xem các phương thức mà lớp Type cung cấp, có một phương thức tên là GetMethod(string methodName). Giá trị trả về của phương thức này là một đối tượng kiểu System.Reflection.MethodInfo chứa các thông tin về phương thức. Và đây là một ví dụ đơn giản minh họa cách dùng phương thức này:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Program
{
    class MyClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello");
        }
    }
    static void Main()
    {
        MyClass myClass = new MyClass();
        Type myType = myClass.GetType();
        // Lấy về phương thức SayHello
        MethodInfo myMethodInfo = myType.GetMethod("SayHello");
        // Thực thi phương thức SayHello của myClass với tham số là null
        myMethodInfo.Invoke(myClass, null);
        Console.Read();
    }
}

Kết quả xuất ra là:

Hello

Bởi vì phương thức SayHello() trên không yêu cầu tham số nên ta sẽ truyền null vào phương thức Invoke() của đối tượng MethodInfo. Đối với các phương thức yêu cầu tham số, ta phải tạo một mảng object[] để truyền giá trị vào. Hãy xem ví dụ sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Program
{
    class MyClass
    {
        public void SayHello(string name)
        {
            Console.WriteLine("Hello, {0}", name);
        }
    }
    static void Main()
    {
        MyClass mClass = new MyClass();
        Type mType = mClass.GetType();
        // Lấy về phương thức SayHello
        MethodInfo mMethodInfo = mType.GetMethod("SayHello");
        object[] mParams = new object[] { " Yin Yang" };
        // Thực thi phương thức SayHello với tham số là mParam
        mMethodInfo.Invoke(mClass, mParams);
        Console.Read();
    }
}

Kết quả xuất ra:

Hello, Yin Yang

Trong trường hợp có nhiều overload của phương thức SayHello(), nếu bạn sử dụng phương thức GetMethod() trên thì sẽ nhận một exception với thông báo “Ambiguous match found” trong lúc runtime. Nguyên nhân là do chương trình không thể biết được phải lấy phương thức SayHello() nào. Lúc đó bạn phải dùng một overload khác của GetMethod() để xác định các tham số sẽ truyền vào phương thức SayHello(), và đây là ví dụ:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Program
{
    class MyClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello");
        }
        public void SayHello(string name)
        {
            Console.WriteLine("Hello, {0}", name);
        }
    }
    static void Main()
    {
        MyClass myClass = new MyClass();
        Type myType = myClass.GetType();
        // Lấy về phương thức SayHello có 1 tham số kiểu string
        MethodInfo myMethodInfo = myType.GetMethod("SayHello", new Type[] { typeof(string) });
        // Thực thi phương thức SayHello của myClass với tham số
        myMethodInfo.Invoke(myClass, new object[] { "Yin Yang" });
        Console.Read();
    }
}

Kết quả xuất ra:

Hello, Yin Yang

Thay vì dùng phương thức GetMethod(), bạn có thể dùng trực tiếp phương thức instance của lớp Type là InvokeMember() với tham số thứ hai là BindingFlags. InvokeMethod.:

1
2
3
4
5
6
7
public object InvokeMember(
 string name,
 BindingFlags invokeAttr,
 Binder binder,
 object target,
 object[] args
);

Ví dụ trên có thể viết lại như sau:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Program
{
    class MyClass
    {
        public void SayHello()
        {
            Console.WriteLine("Hello");
        }
    }
    static void Main()
    {
        MyClass mClass = new MyClass();
        Type mType = mClass.GetType();
        mType.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, mClass, null);
        Console.Read();
    }
}

4. Ví dụ về ConstructorInfo: Tạo instance của đối tượng

Đây là vấn đề khá thực tế trong .Net, trước đây cũng có nhiều bạn thắc mắc về cách viết một phương thức tạo ra một instance của Form với tham số truyền vào là tên của Form đó ở dạng chuỗi. Vấn đề này không khó nếu như bạn đã biết cách dùng Reflection.

  • Sử dụng

Khám phá các thành viên của lớp Type, bạn có thể đoán ra là có thể sử dụng lớp ConstructorInfo để làm điều này.

Phương thức GetConstructor(Type[] types) của lớp Type yêu cầu một mảng kiểu Type theo đúng kiểu, trình tự và số lượng tham số mà constructor cần lấy về yêu cầu. Trong trường hợp không có tham số, lớp Type cung cấp sẵn một mảng Type rỗng là Type.EmptyTypes. Hãy xem hai ví dụ sau:

–     Constructor không có tham số (dùng default constructor):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
namespace ConsoleApplication1
{
    class Program
    {
        class MyClass
        {
            public void SayHello()
            {
                Console.WriteLine("Hello");
            }
        }
        static void Main()
        {
            Type mType = Type.GetType("ConsoleApplication1.Program+MyClass");
            ConstructorInfo conInfo = mType.GetConstructor(Type.EmptyTypes);
            object obj = conInfo.Invoke(null);
            mType.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, obj, null);
            Console.ReadLine();
        }
    }
}

–    Constructor có tham số:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace ConsoleApplication1
{
    class Program
    {
        class MyClass
        {
            string _name;
            public MyClass(string name)
            {
                _name = name;
            }
            public void SayHello()
            {
                Console.WriteLine("Hello, "+_name);
            }
        }
        static void Main()
        {
            Type mType = Type.GetType("ConsoleApplication1.Program+MyClass");
            ConstructorInfo conInfo = mType.GetConstructor(new Type[] { typeof(string) });
            object obj = conInfo.Invoke(new object[] { "Yin Yang" });
            mType.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, obj, null);
            Console.ReadLine();
        }
    }
}

Một chút thay đổi và bạn có thể dễ dàng hiểu được. Tuy nhiên có thể bạn thắc mắc về chuỗi tham số mà tôi sử dụng trong phương thức Type.GetType(). Nếu để ý bạn có thể thấy MyClass là một lớp nằm bên trong lớp Program (inner class), và để truy xuất đến lớp con ta phải dùng dấu ‘+’ để ko bị nhầm lẫn coi lớp Program là tên một namespace.

III. Lớp System.Activator

Bạn cảm thấy cách tạo instance như trên khá rắc rối, khó nhớ, và thực tế là có một cách khác để làm điều này, mặc dù về căn bản nó vẫn là một. Như đã nói trong phần trước, bởi vì yêu cầu tạo một instance từ tên kiểu khá phổ biến, .Net cung cấp sẵn cho ta lớp System.Activator để làm được điều này. Một giải pháp “nhỏ gọn” và giúp lập trình viên không phải dính tới những lí thuyết rườm rà mà ít khi được ứng dụng trong công việc.

Lớp System.Activator này sử dụng một phần của System.Type để tạo ra những phương thức tĩnh cho phép người dùng sử dụng bất cứ lúc nào, đơn giản và tiện lợi.

Hãy xem những gì mà System.Activator thể hiện và so sánh với System.Type, bạn có thể nhận thấy sự tương đồng của 2 lớp riêng biệt này. Tôi sử dụng hai lớp MyClass1 và MyClass2 để minh họa cho việc tạo đối tượng với constructor không và có tham số.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
namespace ConsoleApplication1
{
    class Program
    {
        class MyClass1
        {
            public void SayHello()
            {
                Console.WriteLine("Hello");
            }
        }
        class MyClass2
        {
            string _name;
            public MyClass2(string name)
            {
                _name = name;
            }
            public void SayHello()
            {
                Console.WriteLine("Hello, "+_name);
            }
        }
        static void Main(string[] args)
        {
            Type mType1 = Type.GetType("ConsoleApplication1.Program+MyClass1");
            Type mType2 = Type.GetType("ConsoleApplication1.Program+MyClass2");
            object obj1 = Activator.CreateInstance(mType1);
            object obj2 = Activator.CreateInstance(mType2,"Yin Yang");
            mType1.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, obj1, null);
            mType2.InvokeMember("SayHello", BindingFlags.InvokeMethod, null, obj2, null);
            Console.Read();
        }
    }
}

IV.  Lớp System.Reflection.Assembly

Trong bài viết này tôi cũng có vài lần nhắc đến từ assembly, đây là một khái niệm cơ bản trong .Net. Cũng xin nhắc lại một chút về khái niệm này nếu như bạn đã bỏ lỡ nó khi bắt đầu tìm hiểu về .Net.

–   Định nghĩa:.Net Assembly có thể được hiểu là kết quả của quá trình biên dịch từ mã nguồn sang tập tin nhị phân dựa trên .Net framework. Là thành phần cơ bản nhất .Net framework, assemly là thành phần không thể thiếu trong bất kì ứng dụng .Net nào và được thể hiện dưới hai dạng tập tin là EXE (process assembly) và DLL (library assembly). Assembly có thể được lưu trữ dưới dạng single-file hoặc multi-file, tùy theo kiểu dự án mà bạn làm việc.

Lớp System.Reflection.Assembly có thể được coi là một kiểu mô phỏng chi tiết về các assembly. Lớp Assembly chứa đầy đủ các thông tin cho phép chúng ta truy xuất thông tin và thực thi các phương thức lấy được từ các assembly. Có thể hiểu một cách tương tự: Lớp Type đại diện cho các kiểu dữ liệu, lớp Assembly đại diện cho các assembly.

Lớp Assembly cung cấp các phương thức tĩnh để nạp một assembly thông qua AssemblyName, đường dẫn tập tin hoặc từ tiến trình đang chạy. Bạn cũng có thể lấy Assembly dễ dàng từ property của lớp Type, ví dụ typeof(int).Assembly.

Từ Assemly lấy được, bạn có thể dùng phương thức GetTypes() lấy về các kiểu dữ liệu và thực hiện các kĩ thuật giống như trong phần về lớp System.Type tôi đã nói tới.

Sau đây là một ví dụ đơn giản sử dụng phương thức Assembly.GetExecutingAssembly() để lấy về assembly hiện tại và in ra màn hình các kiểu dữ liệu của nó:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Program
{
    class MyClass { }
    static void Main()
    {
        Assembly ass = Assembly.GetExecutingAssembly();
        Type[] mTypes = ass.GetTypes();
        Array.ForEach(mTypes,type => Console.WriteLine(type.Name));
        Console.Read();
    }
}

Kết quả xuất ra:

Program
MyClass

V.  Phần kết

Vậy là bạn đã được giới thiệu sơ lược về kĩ thuật Reflection cùng cách sử dụng nó trong lập trình. Khả năng của Reflection chưa dừng lại ở đây mà còn liên quan đến những kĩ thuật khác như Attribute, Reflection Emit,… đặc biệt bạn có thể ứng dụng trong việc tạo một IDE để lập trình .Net đơn giản.

Đây chỉ là ví dụ minh họa nên không chương trình Y2 RefectorDemo ở phần trên còn thiếu sót, tôi sẽ cung cấp source code đầy đủ trong phần sau khi giới thiệu về kĩ thuật Reflection Emit trong .Net.

Link this post: https://yinyangit.wordpress.com/2011/01/12/c-ki-thuat-reflection-trong-net/

RANKING in SQL (ROW_NUMBER, RANK, DENSE_RANK,NTILE)

tháng 3 28, 2016

Hàm Ranking là gì?

Các hàm Ranking cho phép bạn có thể đánh số liên tục (xếp loại) cho các tập hợp kết quả. Các hàm này có thể được sử dụng để cung cấp số thứ tự trong hệ thống đánh số tuần tự khác nhau. Có thể hiểu đơn giản như sau: bạn có từng con số nằm trên từng dòng liên tục, tại dòng thứ nhất xếp loại số 1, dòng thứ 2 xếp loại số là 2… Bạn có thể sử dụng hàm ranking theo các nhóm số tuần tự, mỗi một nhóm sẽ được đánh số theo lược đồ 1,2,3 và nhóm tiếp theo lại bắt đầu bằng 1,2,3…
Chúng ta bắt đầu xem xét cách hàm trong sql hổ trợ từ 2005

Dữ liệu thử:
CREATE TABLE Person(
FirstName VARCHAR(10),
Age INT,
Gender CHAR(1)
)

INSERT INTO Person VALUES (‘Ted’, 23, ‘M’)
INSERT INTO Person VALUES (‘John’, 40, ‘M’)
INSERT INTO Person VALUES (‘George’, 6, ‘M’)
INSERT INTO Person VALUES (‘Mary’, 11, ‘F’)
INSERT INTO Person VALUES (‘Sam’, 17, ‘M’)
INSERT INTO Person VALUES (‘Doris’, 6, ‘F’)
INSERT INTO Person VALUES (‘Frank’, 38, ‘M’)
INSERT INTO Person VALUES (‘Larry’, 5, ‘M’)
INSERT INTO Person VALUES (‘Sue’, 29, ‘F’)
INSERT INTO Person VALUES (‘Sherry’, 11, ‘F’)
INSERT INTO Person VALUES (‘Marty’, 23, ‘F’)

1. Hàm ROW_NUMBER

Hàm này trả lại một dãy số tuần tự bắt đầu từ 1 cho mỗi dòng hay nhóm trong tập hợp kết quả.
cú pháp:

ROW_NUMBER () OVER ( [<partition_by_clause> ] <order_by_clause>)

<partition_by_clause>: đánh số theo thứ tự sắp xếp này. cú pháp giống order by

<order_by_clause>: Dùng phân nhóm để đánh số thứ tự. cú pháp

PARTITION BY <Tên cột>

Để hiểu thêm về cách sử dụng hàm ROW_NUMBER, các ví dụ dưới sẽ đánh số liên tục cho tất cả các dòng trong bảng Person và sắp xếp chúng theo trường Age

SELECT ROW_NUMBER() OVER (ORDER BY Age) AS [Row Number by Age],FirstName,Age FROM Person

Và đây là tập hợp kết quả mã T-SQL trên:

Row Number by Age FirstName Age
——————– ———- ——
1 Larry 5
2 Doris 6
3 George 6
4 Mary 11
5 Sherry 11
6 Sam 17
7 Ted 23
8 Marty 23
9 Sue 29
10 Frank 38
11 John 40

———- ———– —————

SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [Row Number by Record Set],FirstName,Age
FROM PersonĐây là tập hợp kết quả khi chạy hàm truy vấn trên: (với SELECT 1 ta sẽ đánh số theo thứ tự insert vào).

Row Number by Record FirstName Age
——————– ———- ——
1 Ted 23
2 John 40
3 George 6
4 Mary 11
5 Sam 17
6 Doris 6
7 Frank 38
8 Larry 5
9 Sue 29
10 Sherry 11
11 Marty 23

———- ———– —————

SELECT ROW_NUMBER() OVER (PARTITION BY Gender ORDER BY Age) AS [Partition by Gender],FirstName,Age,Gender
FROM PersonKhi chạy truy vấn trên, tập hợp kết quả sẽ ra như sau, đánh số theo nhóm Gender:

Partition by Gender FirstName Age Gender
——————– ———- ———–

1 Doris 6 F
2 Mary 11 F
3 Sherry 11 F

4 Marty 23 F
5 Sue 29 F
1 Larry 5 M
2 George 6 M
3 Sam 17 M
4 Ted 23 M

5 Frank 38 M
6 John 40 M

———- ———– ——————–

2. Hàm RANK

Đây là một hàm dùng để xếp hạng, trong ROW_NUMBER nếu 2 giá trị (trong cột sắp xếp) bằng nhau nhưng vẫn có số thứ tự khác nhau, trong RANK thì hai giá trị trùng nhau sẽ được xếp hạng giống nhau. Theo cách xếp hạng thông thường.
Hàm RANK có cú pháp như sau:

RANK() OVER ([<partition_by_clause>] <order_by_clause>)

Trong đó:
<partition_by_clause> : là một cột hay tập hợp các cột được sử dụng để quyết đinh việc đánh số liên tục trong hàm RANK, phân nhóm.

<order_by_clause> : là một cột hay tập hợp các cột được sử dụng để sắp xếp tập hợp kết quả trong nhóm (partition)

Hàm RANK sẽ đánh số liên tục một tập hợp bản ghi nhưng khi có 2 dòng có cùng giá trị sắp xếp thì hàm sẽ đánh giá là cùng bậc giá trị. Giá trị xếp loại vẫn sẽ tăng kể cả khi có 2 dòng cùng giá trị, vì vậy khi đánh giá một giá trị sắp xếp tiếp theo thì số thứ tự vẫn tiếp tục được đánh nhưng sẽ tăng thêm 1 giá trị vào các dòng tiếp theo trong tập hợp.

Đây là ví dụ của hàm rank trong tập hợp bản ghi sắp xếp theo Age:

SELECT RANK() OVER (ORDER BY Age) AS [Rank by Age],FirstName,Age
FROM PersonVà kết quả trả về (lưu ý các mẩu tin trùng):

Rank by Age FirstName Age
——————– ———- ——
1 Larry 52 Doris 6
2 George 64 Mary 11
4 Sherry 11
6 Sam 177 Ted 23
7 Marty 239 Sue 29
10 Frank 38
11 John 40

———- ———– ——————–
Khi bạn phân nhóm theo Gender để xếp hạng theo Age

SELECT RANK() OVER (PARTITION BY Gender ORDER BY Age) AS [Partition by Gender],FirstName,Age,Gender
FROM Person

Đây là kết quả khi chạy các hàm truy vấn trên:

Partition by Gender FirstName Age Gender
——————– ———- ———– ——
1 Doris 6 F
2 Mary 11 F
2 Sherry 11 F

4 Marty 23 F
5 Sue 29 F
1 Larry 5 M
2 George 6 M
3 Sam 17 M
4 Ted 23 M
5 Frank 38 M
6 John 40 M

———- ———– ——————–

3. Hàm DENSE_RANK
Hàm DENSE_RANK cũng giống như hàm RANK, tuy vậy, hàm này không cung cấp khoảng cách giữa các số xếp loại. Thay vào đó, hàm này sẽ xếp loại liên tục cho từng giá trị ORDER BY cụ thể. Với hàm DENSE_RANK, kể cả khi có hai dòng có cùng giá trị xếp loại thì dòng tiếp theo vẫn chỉ tăng thêm một giá trị so với dòng trên. Hàm DENSE_RANK có cú pháp như hàm RANK.

SELECT DENSE_RANK() OVER (ORDER BY Age) AS [Dense Rank by Age],FirstName,Age
FROM Person

Đoạn mã trên sẽ xuất ra như sau:

Dense Rank by Age FirstName Age
——————– ———- ———–
1 Larry 5
2 Doris 6
2 George 6
3 Mary 11
3 Sherry 11
4 Sam 17
5 Ted 23
5 Marty 23
6 Sue 29
7 Frank 38
8 John 40———- ———– ——————–

Như bạn thấy các số trong cột “Dense Rank By Age” vẫn đảm bảo tính liên tục, không hề bị ngắt quãng kể cả khi có hai dòng cùng giá trị ORDER BY và giá trị xếp loại như Ted và Marty.
4. Hàm NTILE
Hàm cuối cùng là hàm NTILE. Đây là hàm được sử dụng để chia các mẩu tin ra theo một số nhóm nhất định . Hàm NTILE cũng sử dụng cú pháp như các hàm ranking khác.

NTILE (integer_expression) OVER ([<partition_by_clause>] <order_by_clause>)

integer_expression: là số nhóm cần phải chia, lưu ý số nhóm này khổng thể lấy từ FROM
Trong ví dụ đầu của hàm này, tôi sẽ nhóm các bản ghi trong bảng Person thành 3 nhóm khác nhau. Tôi muốn các nhóm này dựa trên cột Age. Để làm được điều này, tôi sẽ chạy T-SQL sau:

SELECT FirstName,Age,NTILE(3) OVER (ORDER BY Age) AS [Age Groups]
FROM PersonĐây là tập hợp kết quả của tôi từ câu lệnh T-SQL trên:

Age Groups FirstName Age
———- ———– ——————–
1 Larry 5
1 Doris 6
1 George 6
1 Mary 11
2 Sherry 11
2 Sam 17
2 Ted 23
2 Marty 23
3 Sue 29
3 Frank 38
3 John 40———- ———– ——————–

Trong tập hợp kết quả đã có ở trên với 3 nhóm Age khác nhau. Hàm NTILE chỉ có tác dụng chia đều số lượng các bản ghi và đưa vào từng nhóm số. Sử dụng hàm NTILE cho từng bản ghi trong một nhóm sẽ đưa gia các xếp loại giống nhau.

SELECT NTILE(3) OVER (PARTITION BY Gender ORDER BY Age) AS [Age Groups], FirstName,Age, Gender
FROM Person

Partition by Gender FirstName Age Gender
——————– ———- ———– ——
1 Doris 6 F
1 Mary 11 F
2 Sherry 11 F

2 Marty 23 F
3 Sue 29 F
1 Larry 5 M
1 George 6 M
2 Sam 17 M
2 Ted 23 M

3 Frank 38 M
3 John 40 M
———- ———– ——————–

Link this post: https://handsomeroot.blogspot.com/2016/03/ranking-in-sql-rownumber-rank.html

NHỮNG YẾU TỐ CẦN BIẾT CỦA NHIẾP ẢNH CĂN BẢN

Người mới chơi thường  bị lạc trong một mê cung kiến thức. Một số “giảng viên nhiếp ảnh” thường tung hoả mù các kiến thức nhiếp ảnh căn bản thành một cái gì đó ghê gớm vĩ đại đến nổi xem xong, nghe xong chả hiểu nói cái gì.  Bow xin tóm tắt một số yếu cần nhớ và quên của nhiếp ảnh căn bản. Chi tiết cho từng phần sẽ được viết thành 1 bài riêng khi có thời gian.

 1. Chỉ có 3 yếu tố ảnh hưởng đến quá trình phơi sáng 1 tấm ảnh , đó chính là TỐC ĐỘ, KHẨU ĐỘ và ISO . Chỉ có 3 yếu tố này mà thôi . Nhiều người mới chơi thường bỏ qua ISO nhưng khi đụng đến Flash bạn sẽ thấy nó cần thiết.

2. Khi bạn tăng giảm  (+/-) EV có nghĩa là bạn đang thay đổi 1 trong 2 giá trị tốc độ hoặc khẩu độ. Hoàn toàn không có chuyện set cố định tốc độ, khẩu độ, ISO  rồi mà khi tăng giảm Ev thì hình sẽ sáng hoặc tối hơn. Hoàn toàn không có chuyên đó.

3. Khi chụp ảnh bạn hoặc máy ảnh tự động đưa ra 1 thời chụp duy nhất có tốc độ khẩu độ và iso duy nhất. Và thời chụp đó tương ứng cho việc đúng sáng vật thể có sự chiếu sáng nhất địnhtrong khuôn hình. Bạn chụp ai đó đứng trong nhà cạnh cái cửa sổ. Bạn muốn chụp 1 tấm ảnh đúng sáng mặt nhưng đúng sáng cả background ngoài trời. Hoàn toàn không thể. Trừ khi ánh sáng trong nhà mạnh bằng ngoài trời hoặc bạn phải can thiệp bên ngoài camera để có được điều đó. Bạn phải quyết định muốn đúng sáng phần nào của ảnh bằng cách đo sáng, các phần còn lại nếu sáng hơn thì trong ảnh sẽ sáng hơn, nếu tối hơn thì trong ảnh sẽ tối hơn.

4. Các chế độ đo sáng như Center weight, evaluative , Spot, Matrix gì gì đó ….  bạn nên quên nó luôn đi cho rồi. Chỉ cần hiểu ở mỗi chế độ nó đang đo cái gì .và làm quen với 1 chế độ là đủ. Sau đó sẽ tăng giảm giá trị tốc dộ khẩu độ Iso bằng nhiều cách. Chả ai trước mỗi tấm ảnh lại chỉnh cái chế độ đo sáng cho phù hợp cả.

5. Đo sáng và lấy nét là 2 khái niệm khác nhau. bạn hoàn toàn có thể đo sáng một đằng nhưng lấy nét và chụp 1 nẻo. Không nhất thiết lấy nét cái nào thì máy đo sáng và phải chụp cái đó

6.  Tập làm quen với ánh sáng tự nhiên, ánh sáng flash, làm chủ camera để hạn chế phụ thuộc nhiều vào chế độ đo sáng của Camera hay của Flash.

7.  Bạn nên quên hoặc bỏ qua các chế độ WB như Daylight, Tungsten , Shade, Florescent, Flash.v.v.v.vì nó  cũng chỉ dại diện cho một con số độ K nhất định. AW rất tốt, nên sử dụng AW còn nếu muốn thay thay đổi WB thì hãy dùng dộ K .  Custom WB cũng hữu ích trong nhiều trường hợp như Studio chẳng hạn.

8. File Raw là file thô chứa các thông tin seting của máy ảnh bao gồm cả color space. File raw là file Untage RGB nên khi chụp với file Raw thì khỏi cần phải quan tâm mình đang chỉnh Adobe RGB hay sRGB làm gì cho mất công vì nó chả có tác dụng gì cả … Khi Convert từ Raw sáng Jpeg thì nhớ set theo ý muốn là được.

9. Một số bạn mới chơi máy ảnh thường mua Flash , rồi mua thêm ba cái cục Omni bounce hay mấy cái cục nhỏ nhỏ  gì đó gắn lên đầu flash. Những cái đó chả có tác  dụng gì để ánh sáng soft hơn vì Chất Lựơng Ánh Sáng phụ thuộc chủ yếu vào diện tích phủ sáng chứ không phải chất liệu bọc lên cái flash đó.

10. Phân biệt các con số chỉ giá trị của tốc độ, khẩu độ, ISO, Flash Power. Con số nào chỉ 1 stop con số nào chỉ 1/3 stop.

11. 1 Stop hay 1 EV là ánh sáng vào sensor 1 lương gấp đối hoặc giảm đi một nữa . Hoàn toàn không chuyện có sáng dần hay tối dần.

12. Tấm ảnh mở trong Photoshop dù nó có đuôi là gì thì như CR2, hay NEF thì nó cũng chỉ là 1 file JPEG với các thông số được sang định bằng phần mềm convert RAW- Jpeg như ACR hay LR . Đừng nghĩ mình mở tấm file RAW rồi bấm Open để vào PS thì có nghĩ mình đang chỉnh 1 tấm Raw. Hoàn toàn không có chuyện đó trừ khi bạn Open Object.

13. Không hề có cái Trigger hay Receiver  nào giúp cho bạn chụp ở Hi Sync Speed như lời quảng cáo của người bán hàng trừ khi bạn sử dụng hệ thống Flash và Camera có hỗ trợ Hi- Sync Speed ( thường là cùng 1 hãng cảu hãng máy ảnh )

14. Phân biệt Hi Sync Speed của Camera với Flash Duration để khỏi phải đi mua Trigger / Receiver bậy bạ vì tưởng mình có thể chụp flash với tốc độ 1/8000 .

15. Đừng quá tin những ai khuyên bạn rằng nên máy ảnh hiệu này hiệu kia vì màu sắc hãng này hơn màu sắc hãng nọ. Dù đó là máy hãng nào hiệu gì thì bạn cũng cần phải biết mình muốn ảnh màu sắc thế nào và phải can thiệp vào máy mình bằng cách tinh chỉnh nó trước hoặc sau khi chụp.

Link this post: http://bow101.com/nhiep-anh-can-ban/

CHỤP CHÂN DUNG BẰNG LENS NÀO ?

Mấy nghệ sĩ nhiếp ảnh lâu năm và những người mới chơi ảnh thì thường ra rả rằng chụp chân dung phải chụp tiêu cự từ 85mm tới 135mm vì ở tiêu cự đó tỷ lệ người mẫu sẽ cân đối nhất rồi “xoá phông” tốt nhất v.v..v  . Rồi đến lượt các “tệ nạn gia teen xoá phông” bắt đầu với chiến dịch săn tìm 135mm F2 để chụp “phông xoá luôn cả teen”. Vậy đâu là tiêu cự chụp chân dung đẹp nhất? Có phải 85mm tới 135 hay không ?

Nhiều người chắc sẽ dị ứng khi nghe câu trả lời muôn thuở rằng ” vấn đề là bạn đang chụp cái gì và như thế nào ?”  Vậy chụp gì và như thế nào là sao ?

Thứ nhất cần phải xác định 1 điểm quan trọng:   Chả có cái gì gọi là tele thì chụp được xa hơn normal rồi normal thì chụp được xa hơn wide. Không có khái niệm gần-xa gì ở  đây cả.  Chỉ có 1 khái niệm duy nhất là tele càng dài thì góc nhìn sẽ càng hẹp. Chỉ thế thôi. Nếu bạn muốn làm chủ tiêu cự, bố cục, thì hãy nhớ khái niệm “rộng-hẹp” và nên quên đi khái niệm “gần-xa” càng sớm càng tốt. Seriously !

Angle-of-view1

Vậy làm chủ điều gì ở tiêu cự giúp người chup làm chủ được bố cục , góc máy  và định hình phong cách hay trường phái . Bow tạm gọi nó là Hiệu Ứng Tiêu Cự. Hiệu Ứng Tiêu Cự là gì

 1-Perpective : gần to xa nhỏ => tỷ lệ mặt mập hơn ốm hơn => chính vì vậy các bác cả hay khuyên chụp 85mm tới 135mm => cái này nghe riết chán lắm rồi …

 2-Khi góc nhìn hẹp hơn => nghĩa là tele càng dài thì khoảng cách từ chủ thể đến backgound ( hoặc đến chủ thể thứ 2 đứng phía sau) sẽ gần hơn => cái này mới quan trọng.

3- Khi  góc nhìn hẹp hơn => nghĩa là tele càng dài thì  background ( hoặc chủ thể thứ 2 sẽ lớn hơn )

lens-comparison5pt6-1-600x4591
Cũng là chụp nữa người con bé . nhưng tele càng dài thì con bé càng ngồi gần cái cửa hơn (2), và tele càng dài thì  cái cửa càng lớn hơn (3). Tiệu cự ngắn hơn thì con bé gần gũi và sinh động hơn, tiêu cự càng dài thì con bé càng lạnh lùng và ” có khoảng cách” hơn ( 1 ). Vấn đề là bạn muốn hình ảnh của mình có thêm thông tin ( cái cửa, cái ghế … ), hay muốn hạn chế thông tin (teen xoá phông) , bạn muốn cảm xúc con bé gần gủi hơn hay ” xa lạ ” hơn

day_after_session_005

Với khoảng cách giữa hai nhân vật này thì với tiêu cự này cho làm cho chú rễ cao bằng đầu gối cô dâu

houston wedding photos

Cũng với khoảng cách tương đương giữa cô dâu chú rễ, tiêu cự dài hơn sẽ làm cho mẫu nam lớn hơn và gần mẫu nữ hơn. Nếu chụp với tiêu cự dài hơn nữa như 200mm chẳng hạn, thì trông chú rễ sẽ cao bằng cô dâu ( hoặc hơn ) và cả hai người này sẽ như đứng tựa vào vai nhau.

Ngày xưa mấy bác “bề trên” hay bảo không có tiền mua ống zoom để chụp thì cứ zoom chân đi. ý nói đi lùi lại vài bước. Đây là quan điểm cực kỳ sai lầm cho cho người mới tập chụp vì những người chuyên nghiệp khi chụp ống fixed góc rộng không phải vì để có view được nhiều hơn cũng không hẳn vì chất lượng ống kính tốt hơn mà chủ yếu là vì cái Hiệu Ứng Tiêu Cự của cái ống kính đó.  Nếu muốn view nhiều hơn thì dùng ống 300mm  chạy lên đồi chụp xuống cũng được vậy.

Giả sử bạn đi vào 1 bối cảnh như dưới đây và trong đầu bạn muốn chụp 1 tấm như vầy thì bạn sẽ phải biết dùng tiêu cự nào để có được tỷ lệ của backgound tương ứng với mẫu như vậy. và bạn cũng sẽ biết tiêu cự mình đang gắn trên máy có chụp được như vậy hay không. Chứ không phải loay hoay chụp hết góc này góc nọ coi có góc nào đẹp hay không , nếu không thì về xoá … . Đó là điểm khác biệt giữa người chụp có định hướngkhông có định hướng .

uk.love_

 

Nếu bạn cảm thấy thích hình ảnh của một ai đó, hãy tập phân tích xem họ đang chụp ở tiêu cự gì và vì sao họ chọn tiêu cự đó trong bối cảnh đó.  Ví dụ bạn cảm thấy thích ảnh cưới  của anh Liebehuman Nguyen vì nhẹ nhàng cảm xúc như những tấm ảnh dưới đây. Bạn sẽ phải hiểu rằng anh chụp ống 35mm nhưng không phải để “lấy hết cảnh” mà anh luôn bám sát vào chủ thể để có được hiệu ứng tiêu cự gần to xa nhỏ giúp ảnh có cảm xúc hơn, thần thái người mẫu được nhấn mạnh hơn và backgound có nhiều thông tin hơn là dùng 135mm hay 70-200mm như các “nghệ sĩ nhiếp ảnh” bây giờ . Đó cũng là 1 phần nhỏ để nhận diện phong cách nhiếp ảnh của anh.

bow10112

 

bow10111

Đây chỉ mới là vài cái cơ bản về hiệu ứng tiêu cự , chưa bàn tới những cái khác như ống fixed ống zoom ống mở lớn mở nhỏ gì cả . Nắm được nó sẽ giúp người chụp có định hướng hơn.  Tức là khi tới location thì chỉ việc liếc 1 vòng đã biết được với bối cảnh này mình sẽ chụp được những gì , góc nào, vì sao chụp với góc đó, và chụp góc đó với tiêu cự nào sẽ có hình ảnh như ý . Chứ không cầm cái ống thụt ra thụt vào chạy lăn tăn kiếm góc hay chụp nữa tiếng đồng hồ vẫn chưa có tấm có bố cục ưng ý.  Đó là tầm quan trọng của việc chụp có định hướng khi hiểu rõ hiệu ứng tiêu cự .

Vậy thì cơ bản bạn có thể biết những câu trả lời như  “tuỳ xem bạn chụp cái gì” không phải là câu trả lời trịch thượng, ra vẻ hay dấu nghề gì cả nhé.  Và để trả lời câu hỏi chính, chụp chân dung bằng lens gì thì  bạn sẽ phải hỏi chính mình về quan niệm ảnh chân dung của bạn là gì? Ảnh có nhiều thông tin về thần thái, tính cách, bối cảnh môi trường trường xung quanh.v.v.v hay chỉ đơn thuần là diện mạo ngây thơ, cân đối, thân hình hấp dẫn của mấy em ” teen xoá phông” là đủ.

Các vấn đề khác liên quan đến hiệu ứng tiêu cự như  chụp Fixed, ống Zoom , máy Crop máy FullFrame, ống mở lớn mở nhỏ, góc cao góc thấp, chụp gần chụp xa,bố cục hiện đại, cổ điểnv. v.v.  ảnh hưởng  trực tiếp đến cảm xúc hình ảnh như thế nào thì sẽ được trình bày trong một bài khác nhé.

Link this post: http://bow101.com/chup-chan-dung-bang-lens-nao/

Một số kỹ thuật trong Unit Test

1. AAA Testing pattern

AAA

Đây là cấu trúc test sử dụng “AAA” testing pattern – (Arrange, Act, Assert)

–          Arrange: – Setup đối tượng cần test

–          Act: Thực thi và capture lại kết quả

–          Assert: Verify kết quả

2. Kết hợp Dependency Injection pattern và kỹ thuật Fake để Testing

Dependency Injection là một kỹ thuật để giảm thiểu sự phụ thuộc của các đối tượng trong một project của mình:

Giả sử có 2 người xây dựng ứng dụng. Người A implement phần Data và các repository để bóc tách dữ liệu. B  sử dụng các repository để tương tác với dữ liệu từ controller. Nếu A chưa xong thì B không thể tương tác dữ liệu được. Trong lúc testing để giảm sự phụ thuốc này thì có nhiều cách và một trong đó là sử dụng Dependency Injection.

Xây dựng :

Giả dụ mình có 1 DinnerRepository và Controller của mình đang phụ thuộc vào DinnerRepository

Reposritory

Chúng ta sẽ xây dựng một Interface để giảm sự phụ thuộc repository đó từ Controller

Mình sẽ Extract Interface là IDinnerRepository

IRepository

Và trong controller mình sẽ không sử dụng trực tiếp DinnerRepository mà sử dụng IDinnerRepository để truy vấn dữ liệu

DinnerController

Bây giờ mình sẽ tạo một FakeDinnerRepository  dùng để fake dữ liệu trong lúc testing

Fake Repository

Tiếp đến chúng ta sẽ Testing với dữ liệu giả mình tự định nghĩa

Test Fake

Như chúng ta đã thấy thì Dinner Controller đã không còn phụ thuộc vào DinnerRepository, chúng ta có thể sử dụng các FakeRespository kế thừa từ interface đó để tiến hành testing một cách dễ dàng

Phân tích:

Về hướng sử dụng Dependency Injection ở trên là khá ổn nhưng sẽ rất khó khăn để maintain nếu số lượng dependencies và components trong ứng dụng của mình lớn.

Có một số Dependency Injection framework để quản lý sự phụ thuộc được linh hoạt. Ví dụ như Inversion of Control (IOC) sử dụng các constructor injection để xác định và giảm sử phụ thuộc trong lúc runtime. Có một số OSS Dependency Injection/IOC frameworks phổ biến  trong .NET như AutoFac, Nnject, Spring.NET, StructureMap hay Windsor

3. Kết  hợp kỹ thuật Mock (Moq) để Testing một các hiệu quả hơn

Mocking Framkwork (Moq) giúp cho quá trình testing trở nên dễ dàng hơn bằng cách tạo ra các đối tượng giả (FakeObject) ví dụ như khi sửa thì mình cần có Authencation, với Moq mình có thể dễ dàng get quyền và trả về giá trị mong muốn (True/False – có quyền hay không)

Moq

Ví dụ trên mình đã SetupGet để lấy username và SetupGet để có quyền truy xuất là TRUE.

Từ đó mình có thể pass qua lớp Authencation làm các tác vụ tiếp theo.

4. KẾT

Việc kết hợp sử dụng các kỹ thuật, thư viện Unit Test là rất quan trọng.

Ví dụ trong một project nhỏ, số lượng Dependencies và components ít thì chúng ta có thể dùng kỹ thuật Dependency Injection kết hợp với Fake. Hay với các project lớn thì cần kết hợp các kỹ thuật để có thể tiết kiệm thời gian, dễ dàng sửa chữa nâng cấp.

 

Link this post: https://duyphuong13.wordpress.com/2013/12/15/mot-so-ky-thuat-trong-unit-test/

Riêng tư và ẩn danh trên Internet

Mỗi khi lên mạng, ta đều để lại dấu vết. Khi bật modem, nhà mạng (ISP) cấp cho ta một địa chỉ IP. Chỉ cần có được IP này, bất kỳ ai có thẩm quyền có thể truy ra ta là ai. IP cũng giống như địa chỉ nhà, có địa chỉ rồi thì ai cũng dễ dàng lần ra vị trí nơi ở. Trong môi trường công ty, IP dùng để xác định máy tính ta đang dùng. Mọi lưu lượng đều được quản lý bởi phần mềm theo dõi chuyên dụng. Nhờ đó, quản trị viên có thể biết được ta đang làm gì trên máy.

Bạn lên Facebook quá nhiều trong giờ làm việc? Bạn sẽ nhận được email nhắc nhở từ sếp trong nay mai. Bạn thường hay chat chit trên máy tính của công ty? Rất có thể cuộc trò chuyện đó đã được lưu lại và đang chờ sếp “duyệt”. Thậm chí các chương trình theo dõi có thể quay phim màn hình của bạn, hoặc chụp màn hình theo chu kỳ định trước. Bạn nên nhớ rằng việc theo dõi nhân viên rất phổ biến, dễ dàng và hoàn toàn hợp pháp. Máy tính bạn đang dùng ở công ty chính là tài sản của công ty, do đó họ làm gì với nó là quyền của họ, kể cả việc âm thầm theo dõi bạn.

Thông tin là thứ vô hình nên rất ít người coi việc bảo vệ nó là điều quan trọng. Những rủi ro về xâm phạm thông tin riêng tư là có thật, và nó diễn ra hàng ngày như cơm bữa. Nó hoạt động âm thầm nên ta không hề hay biết. Khi không nhìn thấy rủi ro trước mắt, ta cứ nghĩ nó không tồn tại. Đôi khi, cái tư duy “thấy rồi mới tin” là một sai lầm. Hầu hết mọi người nghĩ mình sẽ là một ngoại lệ, “chắc nó chừa mình ra”. Trên thực tế, rủi ro không chừa một ai.

Trong thế giới thực, ta cũng thường vô ý để lộ thông tin cá nhân. Chắc ai cũng đã từng photocopy giấy tờ cá nhân để làm một thủ tục pháp lý nào đấy. Hành động tưởng chừng như vô hại như photocopy nhưng lại tạo ra một rủi ro rất lớn. Điều tệ hại hơn cả là hầu như chẳng ai quan tâm đến vấn đề này. Trước đây, máy photocopy không được trang bị ổ cứng. Cho nên, khi muốn photo 50 bản tài liệu, máy photocopy phải quét tài liệu gốc 50 lần. Nhưng giờ đây, máy photocopy được trang bị thêm ổ cứng. Nhờ đó, nó chỉ cần quét tài liệu một lần duy nhất và có thể in ra bao nhiêu bản tùy ý. Hình ảnh tài liệu được quét sẽ lưu xuống ổ cứng, và nó sẽ nằm ở đó cho tới khi người chủ ra lệnh xóa. Tuy nhiên, chẳng người chủ nào làm việc này, bởi lẽ sức chứa của ổ cứng thường rất lớn, chẳng có lý do gì phải xóa khi dung lượng trống còn nhiều.

Thế rồi chiếc máy photocopy được bán lại cho người khác, và mọi thông tin trên ổ cứng cũng được chuyển sang chủ mới. Chỉ có trời mới biết người chủ này làm gì với đống dữ liệu đó. Các công ty lớn thường trang bị máy photocopy để tránh bị lộ tài liệu nhạy cảm ra ngoài. Liệu chủ tiệm photocopy có thông đồng với công ty đối thủ để “chôm” những tài liệu nội bộ? Giáo viên cũng hiểu rất rõ vấn đề này nên họ thường photo đề thi ngay sát giờ thi hoặc dùng máy photocopy trong trường.

Trong thế giới ảo, rủi ro này càng lớn hơn. Người trẻ ngày nay thích thể hiện, cái gì cũng đăng lên Facebook. Họ không biết rằng chỉ cần dựa vào những thứ được đăng, kẻ ma mãnh có thể xâu chuỗi chúng lại để có được thông tin hoàn chỉnh về họ: thói quen, sở thích, nơi ở, chỗ thường lui tới… Khi chụp một bức ảnh rồi đăng lên mạng, họ không biết rằng kèm theo bức ảnh đó là các thông tin phụ (metadata) như ngày chụp, thiết bị dùng để chụp, nơi đã chụp, và nhiều thông tin khác. Khi chat bằng các ứng dụng trên điện thoại, thông tin vị trí địa lý được công khai nếu không biết cách tắt. Công nghệ càng phát triển bao nhiêu thì danh sách rủi ro càng dài bấy nhiêu.

Kiến thức an toàn trên mạng cũng cần thiết như kiến thức an toàn giao thông. Bất kỳ ai tham gia Internet phải hiểu rõ rủi ro khi “lưu thông” trên mạng. Không ít trường hợp để lộ thông tin như email, số điện thoại, và ít lâu sau, chủ nhân bị quấy rối bởi những kẻ lạ. Hầu hết mọi người thường để lộ thông tin cá nhân khi thực hiện các giao dịch qua mạng, đơn cử là hình thức mua hàng trên Facebook bằng cách để lại tên và số điện thoại trong phần bình luận. Đây là phương pháp tệ nhất để mua hàng online. Khi giao dịch, ta cần một kênh an toàn, đảm bảo mọi thông tin đều được bảo mật. Cung cấp công khai thông tin cá nhân trên Facebook hay diễn đàn là điên rồ vì có rất nhiều con mắt lướt qua những dòng thông tin đó, kể cả con mắt của kẻ theo dõi.

Đôi khi có những kẻ thích chõ vào chuyện riêng của ta. Chúng muốn biết ta đang làm gì, nói chuyện với ai, và về cái gì. Chúng muốn biết mọi thứ về ta. Đây là những kẻ theo dõi chuyên nghiệp, được tài trợ, đào tạo bài bản, nên chúng biết mọi chiêu thức cần thiết để moi ra thông tin. Để đề phòng bọn này, ta phải có những phương pháp cao cấp. Từ những tài liệu NSA được tiết lộ bởi Edward Snowden, ta có thể thấy công nghệ gián điệp đã lên tới tầm cao mới. Thay vì phải đi săn lùng thông tin, kẻ theo dõi chỉ ngồi một chỗ, ngay tại nơi dữ liệu đi qua. Chúng dùng kĩ thuật khai phá dữ liệu (data mining) để thu thập và phân tích dữ liệu tự động. Chiêu thức này giúp tăng tính hiệu quả của quá trình theo dõi gấp nhiều lần.

Riêng tư và ẩn danh

Trước khi hướng dẫn bạn sử dụng Tor, tôi muốn đề cập một chút về hai thuật ngữ quan trọng mà nhiều người hay nhầm lẫn: Riêng tư (Privacy) và Ẩn danh (Anonymity).

  • Riêng tư (Privacy) là che giấu việc ta đang làm. Không ai biết ta đang làm gì, nhưng họ có thể biết ta là ai. Khi ta vào nhà vệ sinh và đóng cửa lại, chẳng ai biết chắc ta làm gì trong đó. Có thể ta làm “điều mà ai cũng biết là điều gì đấy”, mà cũng có thể không. VPN (Virtual Private Network) là giải pháp hiệu quả cho sự riêng tư. Nó tạo ra một đường ống (tunnel) xuyên Internet để truyền lưu lượng đến đích. Đường ống này được mã hóa và không ai phá vỡ được nó nếu không có khóa (key) để giải mã (khác một trời một vực so với đường ống nước sông Đà).
  • Ẩn danh, nặc danh (Anonymity) là che giấu danh tính. Không ai biết ta là ai, nhưng họ có thể biết ta đang làm gì. Ẩn danh rất hữu ích trong trường hợp ta muốn tố cáo sai phạm của tổ chức hay cá nhân nào đó, nhưng lại sợ việc này ảnh hưởng đến tính mạng của ta (kẻ bị tố cáo có “thế lực rất lớn”). Do đó, ta dùng phương pháp che giấu danh tính để tuồn bằng chứng phạm tội ra bên ngoài, thường là cho phóng viên. Và Tor chính là giải pháp hiệu quả cho sự ẩn danh.

Nếu muốn phát huy tối đa tính bảo mật, ta nên phối hợp cả hai. Kết nối đến mạng lưới Tor thông qua VPN (VPN trước, Tor sau) hoặc dùng Tor để kết nối VPN (Tor trước, VPN sau). Đáng buồn là cả hai phương pháp này có cách thức cấu hình và cài đặt khá công phu. Thông thường, chỉ dùng Tor và các dịch vụ nó cung cấp là khá đủ cho nhu cầu ẩn danh của bạn.

Tại sao dùng VPN lại không ẩn danh? Khi sử dụng VPN, nhà cung cấp dịch vụ này biết IP thực của người dùng, và như thế là ta không còn ẩn danh nữa. Nếu nhà cung cấp bảo mật kém, hoặc không đáng tin cậy (cấu kết với kẻ theo dõi) thì chẳng khác nào ta giao trứng cho ác.

Giới thiệu Tor

Những dấu vết ta để lại trên mạng tiết lộ quá nhiều thông tin nhạy cảm và điều đó rất bất lợi. Tuy có nhiều phương thức che đậy những vết tích này, nhưng giải pháp hiệu quả nhất hiện nay là Tor, một mạng lưới proxy giúp ta ẩn nấp những kẻ theo dõi. Proxy là một máy chủ trung gian giúp qua mặt tường lửa để truy cập những trang web “cấm”. Proxy sẽ truy xuất địa chỉ cần đến, sau đó chuyển nội dung lại cho ta.

Tor là một dạng proxy cao cấp. Nhưng thay vì chỉ qua một máy chủ, Tor chuyển tiếp lưu lượng qua nhiều máy chủ khác nhau (gọi là relay), và những kết nối này đều được mã hóa. Nhờ vậy, chỉ có máy chủ đầu tiên (nút nhập – entry node) của Tor biết IP của ta. Sau khi máy chủ cuối (nút xuất – exit node) nhận lưu lượng, nó sẽ chuyển đến đích và nhận hồi đáp (response) rồi chuyển ngược lại cho máy chủ đầu tiên theo một đường đi mới. Cuối cùng dữ liệu sẽ được gửi về máy ta. Các máy chủ trung gian của Tor được gọi là nút trung chuyển (transit node). Phương pháp đi vòng vèo giúp tăng tính bảo mật cho người dùng vì khả năng theo dõi lưu lượng cực kỳ khó khi đường di chuyển khác nhau và ngẫu nhiên, chưa kể chúng đã được mã hóa nhiều lớp như vỏ củ hành. Do vậy, cách vận chuyển lưu lượng trong mạng lưới Tor được gọi là định tuyến củ hành (onion routing).

Cách Tor hoạt động

Khi quan sát những công nghệ như Tor, Bitcoin, và BitTorrent, ta có thể nhận thấy một điều: cấu trúc phi tập trung (decentralized), phân tán (distributed) và ngang hàng (peer-to-peer) là nền tảng của sự bền vững. Không một tổ chức hay cá nhân nào có thể hạ được Tor, Bitcoin hay BitTorrent. Những công nghệ này như quái vật Hydra, chặt một đầu nó sẽ mọc lại hai đầu mới. Với những công nghệ theo nguyên tắc tập trung, điểm tập trung trở thành tử huyệt (single point of failure). Chỉ cần một cuộc tấn công, bố ráp vào tử huyệt thì cả hệ thống sụp đổ.

Sử dụng Tor Browser

Sử dụng Tor rất đơn giản. Đầu tiên ta vào trang torproject.org để tải Tor Browser và tiến hành cài đặt. Đây thực chất là trình duyệt Firefox nhưng đã được chế biến, tích hợp thêm Tor và những công cụ bảo mật (gỡ mã JavaScript, chặn quảng cáo…). Sau khi cài đặt, ta chạy Tor Browser. Mọi lưu lượng trong trình duyệt sẽ đi qua mạng lưới Tor.

Tor Browser

Khi dùng trình duyệt Tor, ta không nên mở chế độ cửa sổ toàn màn hình (maximize). Kẻ theo dõi có thể nắm thông số kích thước cửa sổ để truy lùng bạn. Tốt nhất, theo Tor khuyến cáo, ta nên dùng kích thước cửa sổ mặc định của trình duyệt Tor.

Sử dụng Tails

Tor không phải giải pháp hoàn hảo. Nếu máy tính bị cài chương trình ghi phím (keylogger), Tor không thể cứu bạn. Nếu máy tính bị sếp cài phần mềm theo dõi, Tor cũng bó tay. Do đó, để tránh những rủi ro trên, tôi thích dùng Tails, phiên bản Linux tích hợp sẵn Tor và có thể chạy trực tiếp từ DVD hoặc USB mà không cần cài đặt.

Một lợi thế khi dùng Tails đó là mọi lưu lượng ra khỏi máy đều thông qua Tor. Do đó, ta không phải lo về việc vô tình để lộ tung tích IP vì mở một file có truy xuất Internet. Với Tails, tôi có thể dùng bất kỳ máy tính và bất kỳ kết nối Internet nào, vì mọi thứ đều nằm trong chiếc USB. Khi cần dùng Tails, tôi cắm nó vào máy, và boot từ USB. Khi dùng xong, tôi shut down và rút USB khỏi máy, không để lại một dấu vết nào trên ổ cứng máy tính. Dù hệ điều hành gốc có bị virus, bị cài phần mềm gián điệp, tôi cũng không lo lắng khi đã có Tails. Đây chính là cách dùng Tor hiệu quả nhất.

Để dùng Tails, ta tải về file ISO và dùng chương trình Universal USB Installer để tạo ổ đĩa boot USB. Nếu đã từng cài Linux thì chắc bạn đã quen với cách này. Sau khi cài xong, ta boot từ USB để vào giao diện của Tails. Sử dụng Tails cũng đơn giản như dùng Linux nên tôi không hướng dẫn chi tiết ở đây để bạn đỡ nhàm chán.

Vì Tails dùng giao diện GNOME nên trông nó rất khác so với Windows. Do đó, để ngụy trang, Tails cho phép ta thay đổi giao diện thành Windows 8, giúp tránh những ánh mắt tò mò nơi công cộng. Hơn nữa, ta có thể tắt máy nhanh bằng cách rút USB ra khỏi máy. Dùng cách này trong trường hợp nghi ngờ có ai đó đang cố xâm nhập máy của ta, hoặc khi dùng máy của người khác và họ đang cần ta trả máy gấp.

Tập thói quen an toàn

Để sử dụng Tor hiệu quả, ta phải học cách loại bỏ những thói quen xấu khi dùng Internet. Dù có dùng Tor đi chăng nữa, mà ta vẫn theo thói cũ thì sẽ có nguy cơ bị lộ danh tính, mất dữ liệu, dính virus… Dưới đây là một vài thói quen ta nên bỏ:

  • Mở file vừa tải về: Nếu file này sử dụng kết nối Internet, nó sẽ không dùng Tor, và ta sẽ bị lộ IP. Nếu muốn mở file này, ta nên ngắt kết nối mạng.
  • Làm quen với người lạ trên mạng: Không bao giờ cung cấp thông tin cá nhân như địa chỉ nhà, số điện thoại,… cho người lạ. Không được mở file do người lạ gửi qua email, dù đó là định dạng file có vẻ vô hại như .jpg hay .txt. Nếu người này có dấu hiệu quá hăm hở để làm quen, kết bạn thì ta nên cẩn trọng hơn nữa. Chắc chắn kẻ này có ý đồ khác. Kẻ lừa đảo chuyên nghiệp thường sử dụng thành thạo các đòn tâm lý được gọi là kỹ nghệ xã hội (social engineering) để dụ ta vô ý nói ra những điều lẽ ra phải giữ kín.
  • Dùng máy tính công cộng: Ta chẳng biết trong máy chứa gì, có thể là chương trình ghi phím (keylogger) để đánh cắp tài khoản email hay Facebook. Các máy tính này thường không có ai bảo dưỡng hoặc nếu có thì cũng qua loa, sơ sài theo kiểu “cha chung không ai khóc”. Rất nhiều người bị mất tài khoản Facebook chỉ vì dùng máy tính công cộng.
  • Dùng USB: Một trong những nguồn phát tán virus phổ biến nhất là USB. Để đề phòng, ta nên tắt tính năng autorun của Windows khi cắm USB vào máy. Nếu được, ta nên chia sẻ tài liệu qua file đính kèm trong email, hoặc upload lên dịch vụ lưu trữ trực tuyến rồi gửi liên kết cho người nhận.
  • Dùng kết nối WiFi miễn phí: Ở đời không có gì miễn phí. Chẳng có ai khờ đến mức để WiFi cho thiên hạ dùng thoải mái. Có thể đây là mồi nhử để dụ con mồi nhẹ dạ kết nối vào. Lúc đó, kẻ theo dõi sẽ “hứng” mọi dữ liệu ra vào máy tính. Do vậy, nếu thấy WiFi không cài passphrase, tốt nhất ta nên xóa nó khỏi danh sách để tránh click nhầm.

Các thói quen xấu cần bỏ thì rất nhiều. Trên đây chỉ là một phần nhỏ trong số đó. Tóm lại, ta phải cẩn thận với người “lạ” và của “lạ” hay bất cứ cái gì “lạ” (kể cả tàu “lạ”).

Dùng email ẩn danh

Kể từ vụ NSA được phép truy cập máy chủ Google để theo dõi các tài khoản, người dùng bắt đầu quan tâm hơn về vấn đề riêng tư trên mạng. Dù đã ra đời khá lâu, email vẫn là phương tiện trao đổi thông tin phổ biến nhất. Không có công ty nào trong thời đại Internet mà lại không dùng email. Đó cũng chính là món mồi ngon béo bở mà tin tặc luôn muốn khai thác vì email chứa nhiều thông tin quan trọng về một người, các giao dịch họ thực hiện, danh sách đối tác làm ăn… Những người làm công việc điều tra số (digital forensics) luôn ra sức xâm nhập email của đối tượng nhằm tìm ra những manh mối mới cho cuộc điều tra. Có thể nói, tài khoản email chính là mỏ vàng thông tin.

Để thực sự ẩn danh, ta không bao giờ cho lộ IP hay bất kỳ thông tin nào khác về ta. Nếu dịch vụ email đòi xác thực bằng số điện thoại thì ta không còn ẩn danh nữa. Khi duyệt email, ta phải làm mọi thứ qua Tor. Dù chỉ một lần đăng nhập không qua Tor, ta sẽ bị lộ IP. Ngoài ra, ta nên tắt tính năng tải hình tự động của dịch vụ email. Kẻ theo dõi có thể chèn một hình có kích thước rất nhỏ (khoảng 1 pixel) vào nội dung email. Khi mở email có hình này, nó sẽ được tải từ máy chủ của kẻ theo dõi, đồng thời máy chủ cũng lưu lại IP của ta. Do vậy, ta phải luôn cảnh giác với email từ người lạ nếu muốn bảo mật danh tính của mình.

Khi đăng ký tài khoản email mới, ta nên dùng tên giả. Trong mục chọn quốc gia, hãy chọn quốc gia đã phát triển và có cơ sở hạ tầng Internet phát triển mạnh (Mỹ, Úc hoặc các quốc gia châu Âu). Các nút xuất (exit node) của Tor thường nằm ở các nước này nên khi truy cập tài khoản email, ta ít bị nghi ngờ.

Lời kết

Thông tin cá nhân cũng là một loại tài sản. Tuy nhiên, do thứ tài sản này vô hình, không thể chạm tay vào, nên nhiều người không quan tâm đến chúng như những loại tài sản khác. Trong thời đại công nghệ thông tin thì thông tin là tài sản quý nhất. Do vậy, cách tốt nhất để phòng chống việc thông tin cá nhân bị phát tán trên mạng đó là tự giáo dục bản thân thực hiện thói quen an toàn. Lỗ hổng bảo mật lớn nhất nằm ở con người, không phải máy móc. Thông tin của ai thì người đó có trách nhiệm tự bảo vệ. Không ai có khả năng gỡ bỏ nó khi ta, hoặc người khác, lỡ dại công khai lên mạng.

Những gì tôi trình bày trong bài viết này có thể không giúp bạn an toàn 100%, nhưng ít ra nó cũng làm cho việc theo dõi bạn trở nên khó khăn gấp bội lần. Bạn hãy tập những thói quen an toàn khi dùng Internet, và đừng quên dùng Tor khi có nhu cầu.

 

Link this post: https://hieusensei.com/rieng-tu-va-an-danh-tren-internet

Khám phá C# 6.0

10 tính năng mới của C# 6.0.

1. Gán giá trị ban đầu cho thuộc tính

Trong những phiên bản trước, để gán giá trị ban đầu cho property, ta phải gán thông qua constructor. Hãy xem qua ví dụ sau:

 1 class Student
 2 {
 3     public int Id { get; set; }
 4     public string Name { get; set; }
 5 
 6     public Student()
 7     {
 8         Id = 0;
 9         Name = "John Doe";
10     }
11 }

Trong C# 6.0, ta có thể gán giá trị trực tiếp vào property khi khai báo chúng giống như gán giá trị cho biến.

1 class Student
2 {
3     public int Id { get; set; } = 0;
4     public string Name { get; set; } = "John Doe";
5 }

2. Thuộc tính chỉ đọc

C# không cho phép dùng từ khóa const đối với property để biến nó thành thuộc tính chỉ đọc. Ta cũng không thể khai báo property chỉ có get mà không có set. Do vậy, cách duy nhất để tạo một thuộc tính chỉ đọc là cho set thành private hoặc protected.

 1 class Student
 2 {
 3     public int Id { get; private set; }
 4     public string Name { get; private set; }
 5 
 6     public Student(int id, string name)
 7     {
 8         Id = id;
 9         Name = name;
10     }
11 }

Trong C# 6.0, ta được phép khai báo property mà chỉ có get và gán giá trị cho nó ngay khi khai báo:

1 class Student
2 {
3     public int Id { get; } = 0;
4     public string Name { get; } = "John Doe";
5 }

Hoặc ta gán giá trị trong constructor:

 1 class Student
 2 {
 3     public int Id { get; }
 4     public string Name { get; }
 5 
 6     public Student(int id, string name)
 7     {
 8         Id = id;
 9         Name = name;
10     }
11 }

3. Thay phần thân method và property bằng biểu thức

Biểu thức lambda là một bước tiến quan trọng của C#. Trong C# 6.0, ta có thể dùng cú pháp của biểu thức lambda để rút gọn phần thân method hoặc property xuống chỉ còn một dòng duy nhất. Hãy xem qua đoạn code sau:

 1 public class Student
 2 {
 3     public string Name { get; set; }
 4     public int BirthYear { get; set; }
 5     public int Age
 6     {
 7         get
 8         {
 9             return DateTime.Now.Year - BirthYear;
10         }
11     }
12 
13     public override string ToString()
14     {
15         return "Name: " + this.Name;
16     }
17 }

Trong phiên bản 6.0, đoạn code trên có thể rút gọn hơn:

1 public class Student
2 {
3     public string Name { get; set; }
4     public int BirthYear { get; set; }
5     public int Age => DateTime.Now.Year - BirthYear;
6     public override string ToString() => "Name: " + this.Name;
7 }

Ở dòng 5, Age đã được chuyển sang cú pháp lambda. Thay vì cung cấp get, ta dùng mũi tên mập (fat arrow). Thậm chí ta không cần dùng từ khóa return, C# sẽ tự động trả về giá trị của biểu thức sau dấu =>. Tương tự, tại dòng 6, ToString() cũng sử dụng cú pháp lambda.

4. Câu lệnh using static

Câu lệnh using chỉ dành cho namespace. Tuy nhiên, C# 6.0 mở rộng câu lệnh này để dùng cho cả lớp static.

1 using static System.Console;
2 
3 public class Program
4 {
5     public static void Main()
6     {
7         WriteLine("Hello world");
8     }
9 }

Ta dùng using static để chỉ ra lớp static cần dùng, ở đây là lớp Console. Trong Main(), ta không cần gõ lại tên lớp Console mà dùng ngay WriteLine().

5. Toán tử điều kiện null

Trước đây, khi cần kiểm tra một đối tượng có phải null hay không, ta dùng câu lệnh if dài dòng:

1 string name = "John Doe";
2 if (customer != null)
3 {
4     name = customer.Name;
5 }

Với C# 6.0, ta có thể dùng cú pháp đặc biệt để đơn giản hóa thao tác kiểm tra giá trị null.

1 string name = customer?.Name ?? "John Doe";

Kí hiệu ?. dùng để kiểm tra giá trị bên trái nó (customer) có phải null hay không. Nếu là null thì nó trả về null. Còn không, nó sẽ đi tiếp sang giá trị bên phải (Name) và kiểm tra null. Nếu giá trị này không phải null thì trả về giá trị đó.

Dấu ?? là toán tử null-coalescing. Nếu biểu thức bên trái có giá trị khác null thì trả về giá trị đó, nếu là null thì trả về giá trị bên phải. Toán tử này có từ các phiên bản trước của C#, và khi phối hợp với ?., ta có một dòng code thực hiện chức năng của 5 dòng code ở trên.

6. Chèn giá trị vào trong chuỗi

Để định dạng chuỗi, ta phải dùng string.Format(). Cách làm này dài dòng nên dễ dẫn đến sai sót.

1 public override string ToString() => string.Format("Name: {0}", this.Name);

Trong C# 6.0, ta dùng cú pháp đặc biệt để chèn trực tiếp giá trị vào trong chuỗi mà không cần các con số đánh dấu vị trí.

1 public override string ToString() => $"Name: {this.Name}";

Ta bắt đầu chuỗi bằng dấu $. Tiếp theo, ta chèn tên biến vào chuỗi và bao quanh bằng cặp dấu ngoặc nhọn. Kết quả của hai dòng lệnh trên là giống nhau, nhưng dòng lệnh dưới trông dễ đọc hơn.

7. Biểu thức nameof

Khi xử lý ngoại lệ, ta thường kèm tên biến vào trong thông báo lỗi để sau này dễ dàng truy lùng chúng. Tuy nhiên, vấn đề nảy sinh khi ta thay đổi tên biến. Lúc này, thông báo lỗi không còn chỉ ra đúng tên biến gây lỗi. Do vậy, C# 6.0 giới thiệu toán tử nameof nhằm lấy ra tên biến mà không phải gõ trực tiếp vào chuỗi.

1 public void Add(Student student)
2 {
3     if (student == null)
4     {
5         throw new ArgumentException("Error: " + nameof(student));
6     }
7 }

8. Gán giá trị ban đầu cho collection bằng cú pháp chỉ số index

Trong phiên bản trước của C#, ta hay dùng cú pháp sau để gán giá trị ban đầu cho một collection:

1 Dictionary<string, User> users = new Dictionary<string, User>()
2 {
3     { "manager", new User("John Doe") },
4     { "receptionist", new User("Jane Doe") }
5 };

Như ta thấy, dòng lệnh 3 và 4 rất mơ hồ. Cú pháp này không chỉ ra rõ ràng chuỗi manager là key và new User() là value. Trong C# 6.0, ta có thể dùng cú pháp chỉ số index để gán giá trị. Cú pháp này thể hiện rõ đâu là key và đâu là value.

1 Dictionary<string, User> users = new Dictionary<string, User>()
2 {
3 	["manager"] = new User("John Doe"),
4 	["receptionist"] = new User("Jane Doe")
5 };

9. Lọc ngoại lệ

Bắt ngoại lệ bằng cú pháp try...catch là thao tác quen thuộc của lập trình viên. Với cú pháp này, ta chỉ bắt được ngoại lệ dựa trên kiểu của nó. Trong C# 6.0, Microsoft bổ sung thêm tính năng lọc ngoại lệ bằng cách kiểm tra một điều kiện cho trước.

1 try
2 {
3     // Statements
4 }
5 catch (Exception ex) when (ex.Data.Count > 0)
6 {
7     // Handle exception
8 }

Ta khai báo điều kiện lọc bằng từ khóa when và theo sau là một biểu thức điều kiện (trả về true hoặc false). Nếu thỏa điều kiện, khối lệnh của catch sẽ được thực thi.

10. Dùng từ khóa await trong khối lệnh catch và finally

Nếu dùng từ khóa await trong khối lệnh catch ở các phiên bản C# trước, ta sẽ nhận thông báo lỗi bảo rằng await không thể dùng trong catch. Vì kĩ thuật lập trình bất đồng bộ đang ngày càng trở nên cần thiết trong ứng dụng hiện đại, nên việc không cho dùng await trong catch là một trở ngại lớn. Do đó, phiên bản C# 6.0 đã cho phép dùng await trong khối catch.

1 try
2 {
3     // Statements
4 }
5 catch (Exception ex)
6 {
7     await FileHelper.WriteAsync(ex.Message);
8 }

Lời kết

Những tính năng trình bày ở trên tuy không đột phá nhưng nó làm cho code C# gọn nhẹ và rõ ràng. Điều này rất quan trọng vì code càng dễ đọc thì khả năng sai sót càng ít. C# là một ngôn ngữ đã trưởng thành, các tính năng chính của nó đã ổn định nên ở bản 6.0 này, Microsoft chỉ tút lại những chỗ còn gai góc để nó mịn hơn. Trong khi viết bài này, tôi nghe phong thanh rằng đã có bản preview các tính năng C# 7.0. Trong suốt 15 năm qua, C# không ngừng cải tiến, và trong bản 7.0, chắc chắn nó sẽ có thêm nhiều tính năng mới thú vị.

 

Link this post: https://hieusensei.com/kham-pha-c-sharp-6

Sự khác nhau giữa biến Constant, ReadOnly và Static trong C#

Các biến Constant và ReadOnly (const và readonly) trong C# làm cho 1 biến mà giá trị không thể bị thay đổi. Từ khóa static thì được sử dụng để tạo ra biến tĩnh chia sẻ giá trị cho tất cả các thể hiện của class đó. Trong bài viết này chúng ta sẽ cùng tìm hiểu về sự khác nhau giữa chúng.

Constant

Trường constant hay biến constant phải được gán giá trị lúc khai báo sau đó thì chúng không thể thay đổi giá trị. Mặc định constant là static nên không thể khai báo thêm từ khóa static cho biến constant.

public const int X = 10;

Một biến constant là một hằng số từ khi biên dịch (complie-time constant). Một biến constant có thể được khởi tạo bởi một biểu thức nhưng phải đảm bảo các toán hạng trong biểu thức cũng phải là constant.

void Calculate(int Z)
{
 const int X = 10, X1 = 50;
 const int Y = X + X1; //no error, since its evaluated a compile time
 const int Y1 = X + Z; //gives error, since its evaluated at run time
}

Bạn có thể áp dụng từ khóa const vào các kiểu nguyên thủy (byte, short, int, long, char, float, double, decimal, bool), enum, một chuỗi, hoặc một kiểu tham chiếu và có thể gán với giá trị null.

const MyClass obj1 = null;//không có lỗi
const MyClass obj2 = new MyClass();//lỗi ở lúc runtime

Biến constant có thể được gán tất cả các access modifiers như public, private, protected, internal. Bạn sử dụng biến constant trong trường hợp chắc chắn giá trị của chúng không thay đổi.

ReadOnly

Một biến Readonly có thể được khởi tạo vào thời điểm khai báo hoặc trong constructor của class đó. Vì vậy các biến readonly có thể được sử dụng như là các hằng số lúc thực thi (run-time constants)

class MyClass
{
 readonly int X = 10; // Khởi tạo lúc khai báo
 readonly int X1;

 public MyClass(int x1)
 {
 X1 = x1; // khởi tạo lúc chạy
 }
}

Readonly có thể được áp dụng cho cả tham giá trị và kiểu tham chiếu trừ delegate và event. Sử dụng readonly khi bạn muốn tạo ra biến constant ở lúc runtime.

Static

Từ khóa static được sử  dụng để tạo một biến hoặc một thành phần tĩnh, nghĩa là giá trị của nó sẽ được chia sẻ cho toàn bộ các đối tượng và không gắn vào một đối tượng cụ thể nào. Từ khóa static có thể được áp dụng cho cả class, fields, properties, operators, events, constructor nhưng không dùng được cho index, destructors, hay kiểu nào hơn các classs.

class MyClass
{
 static int X = 10;
 int Y = 20;
 public static void Show()
 {
 Console.WriteLine(X);
 Console.WriteLine(Y); //lỗi bởi vì static method chỉ truy cập được biến static
 }
}

Kiến thức về từ khóa static

  • Nếu từ khóa static được áp dụng cho 1 class tất cả các thành phần trong class cũng phải static
  • Phương thức static chỉ có thể truy cập các thành phần static khác  trong class. Các static properties được sử dụng để set và get giá trị cho các giá trị của biến static trong class.

Static constructor không thể có tham số. Không thể áp dụng các access modifiers cho static constructor, luôn có một default constructor public để khởi tạo các biến static cho class

 

Link this post: https://tedu.com.vn/lap-trinh-c/su-khac-nhau-giua-bien-constant-readonly-va-static-trong-c-33.html

Sự khác nhau giữa IEnumerable và IQueryable

LINQ dùng để truy vấn dữ liệu từ cơ sở dữ liệu và các collection, chúng ta sử dụng IEnumerable và IQueryable để thao tác dữ liệu nhưng một số bạn vẫn chưa hiểu được sự khác nhau giữa 2 đối tượng này. IQueryable kế thừa IEnumerable, vì thế IQueryable có tất cả các đặc tính của IEnumerable và có thêm các đặc tính của riêng nó. Cả hai đều mang tính quan trọng khi truy vấn và thao tác dữ liệu. Chúng ta hãy cùng xem các tính năng của cả hai và so sánh chúng để dùng trong trường hợp nào cho hợp lý nhất nhé.

IEnumerable

  1. IEnumerable nằm trong namespace System.Collections
  2. IEnumerable có thể duyệt cac phần tử chỉ 1 chiều tiến lên, nó không thể duyệt ngược lại giữa các phần tử.
  3. IEnumerable tốt nhất khi truy vấn từ một collection in-memory tức là trong bộ nhớ RAM như List, Array…
  4. Khi truy vấn dữ liệu từ database, IEnumerable thực thi câu lệnh select trên server sau đó tải toàn bộ dữ liệu về client rồi mới lọc dữ liệu.
  5. IEnumerable phù hợp với Linq to Object và Linq to XML
  6. IEnumerable không hỗ trợ custom query
  7. IEnumerable không hỗ trợ lazy loading vì thế không phù hợp với trường hợp phân trang.

Ví dụ về IEnumerable

MyDataContext dc = new MyDataContext ();
IEnumerable<Employee> list = dc.Employees.Where(p => p.Name.StartsWith("S"));
list = list.Take<Employee>(10);

Câu lệnh của đoạn code trên sẽ gen ra như sau:

SELECT [t0].[EmpID], [t0].[EmpName], [t0].[Salary] FROM [Employee] AS [t0]
WHERE [t0].[EmpName] LIKE @p0

Chú ý là trong câu lệnh này “top 10” sẽ không có vì IEnumerable lọc các bản ghi ở dưới client. Nên toàn bộ số bản ghi trước khi load ra top 10 sẽ dc tải về client.

IQueryable

  1. IQueryable nằm trong namespace System.Linq
  2. IQueryable cũng chỉ có thể di chuyển 1 chiều tiến lên trong collection, nó không thể move back lại.
  3. IQueryable tốt nhất cho truy vấn dữ liệu out-memory như là database.
  4. Khi truy vấn, IQueryable thực thi câu lệnh truy vấn và lọc dữ liệu trên Server luôn
  5. IQueryable phù hợp cho Linq to SQL
  6. IQueryable hỗ trợ custom query sử dụng phương thức CreateQuery và Execute.
  7. IQueryable hỗ trợ lazy loading. Vì thế nó phù hợp cho trường hợp phân trang.Ví dụ về IQueryable

Ví dụ về IQueryable

MyDataContext dc = new MyDataContext ();
IQueryable<Employee> list = dc.Employees.Where(p => p.Name.StartsWith("S"));
list = list.Take<Employee>(10);

Câu lệnh gen ra sẽ như sau:

SELECT TOP 10 [t0].[EmpID], [t0].[EmpName], [t0].[Salary] FROM [Employee] AS [t0]
WHERE [t0].[EmpName] LIKE @p0

Chú ý: Câu lệnh trên có chữa “top 10” vì IQueryable thực  thi câu lệnh và lọc dữ liệu trên server hoàn toàn. Chỉ khi gọi ToList() hoặc đưa vào duyệt thì nó mới thực thi.

Tổng kết

Trong bài viết này mình cố gắng giải thích sự khác nhau giữa IEnumerable và IQueryable. Iqueryable giúp cho các bạn build câu lệnh và thực thi 1 lần trên server để trả về số bản ghi nhỏ nhất có thể. Còn IEnumerable giúp các bạn thao tác với các collection in-memory sẽ tốt hơn. Mình hy vọng sau khi đọc xong bài viết này các bạn có thể tăng khả năng sử dụng 2 đối tượng này giúp tăng performance.

 

Link this post: https://tedu.com.vn/lap-trinh-c/su-khac-nhau-giua-ienumerable-va-iqueryable-35.html