TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
1
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Bài 9: Serialization
• Kỹ thuật chuyển đổi với BinaryFormatter• Kỹ th ật h ể đổi ới X lS i li• Kỹ thuật chuyển đổi với XmlSerializer• Kỹ thuật chuyển đổi lớp đối tượng thông qua
lớp giao tiếp ISerializablelớp giao tiếp ISerializable
2
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Serialization
• Nhiều ứng dụng cần lưu trữ và trao đổi dữliệu được lưu trong các đối tượng với nhau
• Serialization (chuyển đổi) : là tiến trình biếnđổi và tái tạo các đối tượng để chúng có thểđ l t ữ à t đổi iữ á ứ dđược lưu trữ và trao đổi giữa các ứng dụng.
• .NET framework cung cấp nhiều kỹ thuậtchuyển đổi để đơn giản hóa tác vụ nàychuyển đổi để đơn giản hóa tác vụ này
3
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Serialize• Deserialize• Deserialize
4
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Serialize− Tiến trình chuyển một đối tượng thành chuỗi tuần tựTiến trình chuyển một đối tượng thành chuỗi tuần tự
các byte để có thể lưu trữ hoặc trao đổi.
− Các bước thực hiện• Tạo đối tượng Stream lưu kết quả chuyển đổi• Tạo đối tượng BinaryFormatter• Gọi phương thức BinaryFormatter.Serialize để chuyển
đổi, lưu kết quả vào Stream
5
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Demo serialize− Tạo đối tượng
string data=“This must be store in a file”;Tạo đối tượnglưu kết quảchuyển đổi
ố
FileStream fs=new FileStream(“SerializedString.Data”,FileMode.Create);
− Tạo đối tượngBinaryFormatter
Chuyển đổi và
BinaryFormatter bf=new BinaryFormatter();
bf Serialize(fs data);− Chuyển đổi vàlưu kết quả
bf.Serialize(fs,data);
fs.Close();
6
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Deserialize− Tiến trình chuyển chuỗi tuần tự các byte thu được từTiến trình chuyển chuỗi tuần tự các byte thu được từ
quá trình serialize thành đối tượng ban đầu
− Các bước thực hiện• Tạo Stream đọc kết quả của quá trình serialize• Tạo đối tượng BinaryFormatter• Tạo đối tượng lưu dữ liệu sau khi chuyển đổi• Gọi phương thức BinaryFormatter.Deserialize để chuyển
đổi lại và ép kiểu phù hợp với kiểu của đối tượng ban đầu
7
ạ p p ợp ợ g
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Demo deserialize− Tạo stream đọc kết
ả h đ ừ áFileStream fs=new
quả thu được từ quátrình serialize
− Tạo đối tượng
FileStream(“SerializedString.Data”,FileMode.Create);
BinaryFormatter bf=newTạo đối tượngBinaryFormatter
− Tạo đối tượng lưu kếtể ổ
BinaryFormatter bf=new BinaryFormatter();
string data=“”;gquả chuyển đổi
− Chuyển đổi và lưu kếtquả
data=(string)bf.Deserialize(fs);
fs Close();
8
quả fs.Close();
Console.WriteLine(data);
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Tạo lớp có thể serialize− Thêm thuộc tính Serializable vào lớp cần chuyển đổiThêm thuộc tính Serializable vào lớp cần chuyển đổi,
.NET framework sẽ tự động serialize.
− Có thể kiểm soát quá trình serialize của các lớp đểầ
[Serializable]q ptăng hiệu quả / đáp ứng các yêu cầu của ứng dụng.public class ShoppingCartItem{
public int productId;public decimal price;public int quantity;public decimal total;
9
…}
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter• Vô hiệu hóa chuyển [Serializable]Vô hiệu hóa chuyển
đổi các thành phần của lớp
[ ]public class ShoppingCartItem{
public int productId;public decimal price;− Những giá trị tạm,
thuộc tính tính toán
Thêm th ộc tính
public decimal price;public int quantity;public decimal total;NonSer[ialized]− Thêm thuộc tính
NonSerialized trước khai báo
public ShoppingCartItem(int _procId,decimal _price,int _quan){
productId=_procId;
− Thành phần NonSerialized không được khởi tạo khi
price=_price;quantity=_quan;total=price+quantity;
10
được khởi tạo khi deserialize. }
}
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Tự động khởi tạo các thành phần NonSerialized khi deserialize:− Thực thi interface IDeserializationCallback
− Thực thi phương thức IDeserializationCallback OnDeSerializationIDeserializationCallback.OnDeSerialization
11
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter• Tự động khởi tạo các thành phần NonSerializedTự động khởi tạo các thành phần NonSerialized
khi deserialize:[Serializable]public class ShoppingCartItem : IDeserializationCallback{
….[NonSerialized] public decimal total;public ShoppingCartItem(int _procId,decimal _price,int _quan){
…total=price+quantity;
}void IDeserializationCallback.OnDeserialization(object sender){
total=price*quantity;
12
p q y}
}
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• T thí h hiê bả• Tương thích phiên bản− Phát sinh ngoại lệ khi deserialize đối tượng được
serialize ở phiên bản trước của ứng dụngserialize ở phiên bản trước của ứng dụng• Thêm thành phần mới vào lớp, deserialize đối tượng được
serialize trước đó mà không có thành phần mới
− Giải pháp:• Thực thi custom serialization
ầ• Thêm thuộc tính OptionalField trước thành phần mới có thể gây không tương thích phiên bản
− Thành phần OptionalField không được khởi tạo khi
13
Thành phần OptionalField không được khởi tạo khi deserialize
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Tương thích phiên bản[Seriali able][Serializable]public class ShoppingCartItem : IDeserializationCallback{
public int productId;public decimal price;public int quantity;[NonSerialized] public decimal total;[ ] p ;[OptionalField] public bool taxable;…
}
14
}
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter• Lưu ý khi xử lý tương thích phiên bản• Lưu ý khi xử lý tương thích phiên bản− Không bỏ thành phần serialize
Không áp d ng th ộc tính NonSeriali ed cho thành− Không áp dụng thuộc tính NonSerialized cho thành phần không áp dụng thuộc tính này ở phiên bản trước.
− Không đổi tên/ kiểu của thành phần serializeg p− Áp dụng OptionalField khi khi thêm mới thành phần
serializeKhi bỏ h ộ í h N S i li d h hà h hầ khô− Khi bỏ thuộc tính NonSerialized cho thành phần không áp dụng thuộc tính này ở phiên bản trước, dùng OptionalField
15
− Với các thành phần OptionalField, thực thi IDeserializationCallback để khởi tạo giá trị ban đầu
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với BinaryFormatter
• Chọn định dạng chuyển đổi− NET Framework cung cấp 2 phương thức định dạng.NET Framework cung cấp 2 phương thức định dạng
dữ liệu chuyển đổi:• BinaryFormatter: định dạng hiệu quả nhất để serialize các
đối t ẽ hỉ đ hiể bởi á ứ d NETđối tượng sẽ chỉ được hiểu bởi các ứng dụng .NET• SoapFormatter:
–– định dạng XML, là cách thức đáng tin cậy để serialize định dạng XML, là cách thức đáng tin cậy để serialize ị ạ g , g ậyị ạ g , g ậycác đối tượng được trao đổi trên môi trường mạng/ các đối tượng được trao đổi trên môi trường mạng/ được hiểu bởi các ứng dụng ngoài .NETđược hiểu bởi các ứng dụng ngoài .NET
–– Có khả năng vượt tường lửa tốt hơn BinaryFormatterCó khả năng vượt tường lửa tốt hơn BinaryFormatter
16
g ợ g yg ợ g y
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Serialize• Deserialize• Deserialize
17
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• XML : định dạng tài liệu văn bản chuẩn cho việclưu trữ và trao đổi thông tin.
• .NET Framework cung cấp nhiều thư viện hỗ trợđọc, ghi file XML, chuyển đổi các đối tượng sangđịnh dạng XML và ngược lại.định dạng XML và ngược lại.
• Tại sao sử dụng XML Serialization− Khả năng giao tiếp rộngKhả năng giao tiếp rộng
− Thân thiện với người dùng, dễ dàng đọc và hiệu chỉnh
− Khả năng tương thích phiên bản cao
18
Khả năng tương thích phiên bản cao
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Hạn chế của XML Serialization− Chỉ có thể chuyển đổi các dữ liệu publicChỉ có thể chuyển đổi các dữ liệu public
− Không thể chuyển đổi đối tượng đồ thị, biểu đồ
19
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Serialize− Các bước thực hiệnCác bước thực hiện
• Tạo đối tượng Stream/ TextWriter/ XmlWriter để lưu kết quả chuyển đổi
• Tạo đối tượng XmlSerializer với kiểu của đối tượng cần chuyển đổi
• Gọi phương thức XmlSerializer.Serialize để chuyển đổi và ọ p g ylưu kết quả.
20
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Demo Serialize− Tạo đối tượng lưu
FileStream fs=new FileStream(“SerializedDate xml” FileModeTạo đối tượng lưu
kết quả chuyển đổi
− Tạo đối tượng
FileStream( SerializedDate.xml ,FileMode.Create);
XmlSerializer xs=new gXmlSerializer
− Thực thi chuyển đổi
XmlSerializer(typeof(Date));
xs.Serialize(fs,System.DateTime.Now);fs.Close();
21
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Deserialize− Các bước thực hiệnCác bước thực hiện
• Tạo đối tượng Stream/ TextWriter/ XmlWriter để đọc kết quả chuyển đổi của quá trình serialize
• Tạo đối tượng XmlSerializer với kiểu của đối tượng cần chuyển đổi
• Gọi phương thức XmlSerializer.Deserialize để tái tạo đối ọ p g ạtượng ban đầu, ép kiểu dữ liệu cho phù hợp
22
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Demo Deserialize− Tạo đối tượng đọcTạo đối tượng đọc
kết quả quá trình serialize
ố
FileStream fs=new FileStream(“SerializedDate.xml”,FileMode.Open);
− Tạo đối tượng XmlSerializer
Thực thi tái tạo đối
XmlSerializer xs=new XmlSerializer(typeof(Date));
− Thực thi tái tạo đối tượng
Datetime time=(DateTime)xs.Deserialize(fs);fs.Close();Console.WriteLine(“Day: “+time.DayOfWeek +”,
Time: “ +time TimeOfday ToString());
23
Time: “ +time.TimeOfday.ToString());
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Tạo lớp có thể serialize− Khi chuyển đổi các lớp đáp ứng yêu cầu XmlKhi chuyển đổi các lớp đáp ứng yêu cầu Xml
serialization nhưng không có bất kỳ thuộc tính XmlSerialization nào, .NET sẽ dùng định dạng mặc địnhcó để đáp ứng yêu cầu của nhiều người dùngcó để đáp ứng yêu cầu của nhiều người dùng.• Tên của Xml element : phụ thuộc vào tên lớp và tên thành
phần• Mỗi thành phần được chuyển đổi thành một Xml element
riêng biệt.
24
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Tạo lớp có thể chuyển đổi
public class ShoppingCartItem{public int productId;public decimal price;
<?xml version=“1.0” ?><ShoppingCartItem>
<productId>100</productId>Serializep ppublic int quantity;public decimal total;public ShoppingCartItem(){
p p<price>12.0</price><quantity>5</quantity>
</ShoppingCartItem>
Serialize
public ShoppingCartItem(){}
}
</ShoppingCartItem>
25
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• Tạo lớp có thể chuyển đổi− Nếu chỉ tạo tài liệu XML mô tả lớp kết quả quá trìnhNếu chỉ tạo tài liệu XML mô tả lớp, kết quả quá trình
chuyển đổi được coi là đủ
− Nếu muốn tạo tài liệu XML đáp ứng những yêu cầu cụ ể ầ ể ổ ể
p g g ythể : cần can thiệp vào quá trình chuyển đổi để tài liệu XML có định dạng theo yêu cầu.
26
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer
• T lớ ó thể h ể đổi• Tạo lớp có thể chuyển đổi
[XmlRoot (“CartItem”)] <?xml version=“1.0” ?>public class ShoppingCartItem{
[XmlAttribute] public int productId;public decimal price;
<CartItem productId=“100”><price>12.0</price><quantity>5</quantity>public decimal price;
public int quantity;public decimal total;[X lI ] bli d i l t t l
<quantity>5</quantity></CartItem>
[XmlIgnore] public decimal total;public ShoppingCartItem(){}
27
}
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Kỹ thuật chuyển đổi với XmlSerializer• Chuyển đổi 1 đối tượng DataSety ợ g
private void SerializeDataSet(string filename){XmlSerializer ser=new XmlSerializer(typeof(DataSet));DataSet ds=new DataSet(“myDataSet”);DataSet ds=new DataSet(“myDataSet”);DataTable dt=new DataTable(“table1”);dt.Columns.Add(new DataColumn(“thing”));d T bl Add(dt)ds.Tables.Add(dt);for (int i=0;i<5;i++){
DataRow r=dt.NewRow();r[0]=“Thing “+i;dt.Rows.Add(r); }
TextWriter writer=new StreamWriter(filename);
28
ser.Serialize(writer,ds);writer.Close(); }
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Custom Serialization
• Kỹ thuật chuyển đổi lớp đối tượng thông qua lớp giao tiếp ISerializablep g p
• Tiến trình điều khiển việc chuyển đổi và tái tạo đối tượng, đảm bảo tương thích phiên ạ ợ g g pbản
29
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Custom Serialization
• Thực thi custom serialization− Serialize trong NET rất uyển chuyển và có thể tùySerialize trong .NET rất uyển chuyển và có thể tùy
biến để đáp ứng yêu cầu phát triển ứng dụng.
− Có thể override quá trình serialize xây dựng sẵn trongằ
q y g g.NET bằng cách thực thi interface Iserializable và khaibáo thuộc tính Serializable cho lớp.
Thực thi interface Iserializable sẽ gọi− Thực thi interface Iserializable sẽ gọi• Phương thức GetObjectData trong quá trình serialize• Phương thức khởi tạo đặc biệt trong quá trình deserialize
30
Phương thức khởi tạo đặc biệt trong quá trình deserialize
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Custom Serialization• Ví dụ:
[Serializable]public class ShoppingCartItem : ISerializable {
protected ShoppingCartItem(SerializationInfo info, StreamingContext context){
productId = info.GetInt32("Product ID");price = info GetDecimal("Price");public Int32 productId;
public decimal price;public Int32 quantity;[NonSerialized] public decimal total;
price info.GetDecimal( Price );quantity = info.GetInt32("Quantity");total = price * quantity;
}[NonSerialized] public decimal total;
public ShoppingCartItem(Int32 _productId, decimal _price, int _quantity){
[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormat=true)]
public virtual void productId = _productId;price = _price;quantity = _quantity;total = price * quantity;
GetObjectData(SerializationInfoinfo,StreamingContext context){
info.AddValue("Product ID", productId);info.AddValue("Price", price);
31
total = price * quantity;}
info.AddValue("Quantity", quantity);}
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Custom Serialization
• Các sự kiện trong Serialization− Serializing: phát sinh trước khi serialize bắt đầu
• Thêm thuộc tính OnSerializing trước phương thức
− Serialized: phát sinh sau khi serialize hoàn tất• Thêm thuộc tính OnSerialized trước phương thức
− Deserializing: phát sinh trước khi deserialize bắt đầu• Thêm thuộc tính OnDeserializing trước phương thức
− Deserialized: phát sinh sau khi serialize kết thúc
32
• Thêm thuộc tính OnDeserialized trước phương thức
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Custom Serialization• Các sự kiện trong SerializationCác sự kiện trong Serialization
Serialization begins Deserialization beginsSerialize Deserialize
[OnSerializing]
Serialization occurs
[OnDeserializing]
Deserialization occursSerialization occurs
[OnSerialized]
Deserialization occurs
IDeserializationCallback,OnDeserialization
Serialization completed[OnSerialized]
33
Serialization completed
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Custom Serialization
• Các sự kiện trong Serialization− Các sự kiện này là cách tốt nhất và dễ dàng nhất để
điều khiển tiến trình chuyển đổi
[Serializable]public class ShoppingCartItem : ISerializable {
public Int32 productId;điều khiển tiến trình chuyển đổi.• Không can thiệp vào serialization stream• Cho phép hiệu chỉnh đối tượng trước và sau serialize
public decimal price;public Int32 quantity;public decimal total;
Cho phép hiệu chỉnh đối tượng trước và sau serialize(deserialize)
− Yêu cầu cho các phương thức xử lý các sự kiện
[OnSerializing] void CalculateTotal(StreamingContext sc){total=price*quantity;
}[OnDeserialization] void CheckTotal(StreamingContext sc){• Có tham số là đối tượng StreamingContext
• Không trả về kết quả• Có th ộ tí h t thí h ới kiệ ầ thiệ
[OnDeserialization] void CheckTotal(StreamingContext sc){if (total==0){
CalculateTotal(sc);}
34
• Có thuộc tính tương thích với sự kiện cần can thiệp}}
}
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Custom Serialization• Thay đổi ngữ cảnh chuyển đổiThay đổi ngữ cảnh chuyển đổi− Khi serialize 1 đối tượng: không cần quan tâm đích đến
− Trong vài trường hợp: serialize và deserialize sẽ khác− Trong vài trường hợp: serialize và deserialize sẽ khácnhau phụ thuộc vào đích đến
− Cấu trúc StreamingContext cung cấp thông tin đíchg g p gđến của đối tượng được serialize cho lớp xử lý thực thiinterface Iserializable• StreamingContext có 2 thuộc tính:• StreamingContext có 2 thuộc tính:
–– ContextContext:: thamtham chiếuchiếu đếnđến đốiđối tượngtượng chứachứa thôngthông tintin ngữngữcảnhcảnhSt tSt t 11 tậtậ ờờ hiệhiệ hỉhỉ ồ /ồ / đí hđí h ủủ đốiđối tượtượ
35
–– StateState:: 11 tậptập cờcờ hiệuhiệu chỉchỉ rara nguồn/nguồn/ đíchđích củacủa đốiđối tượngtượngđangđang đượcđược serialize/serialize/ deserializedeserialize
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
Custom Serialization• Các cờ hiệu của thuộc tính StateCác cờ hiệu của thuộc tính State
− CrossProcess
− CrossMachine
− File
− Persistence
− Remoting
− Other
− CloseClose
− CrossAppDomain
− All
36
TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
TRUNG TÂM TIN HỌC
End
Thanks
37