Xây dựng ứng dụng Metro tương tác với dữ liệu SQLite

Về tác giả
Bài viết này được cung cấp bởi MVP Lê Hoàng Dũng. Microsoft chân thành cảm ơn những MVP đã chia xẻ những kinh nghiệm chuyên môn của mình với những người sử dụng khác. Bài viết này sẽ được đăng trên website hoặc blog của MVP sau đây. Nếu bạn muốn xem các bài viết khác được chia xẻ bởi MVP, vui lòng nháy chuột vào đây.
Ứng dụng Metro
Ứng dụng Metro là một khái niệm mới về thiết kế và phát triển các ứng dụng có giao diện tràn đầy màn hình (full screen). Người lập trình nhớ đó mà có thể kiểm soát và sử dụng mọi điểm ảnh trên màn hình, và giúp cho người dùng có thể trải nghiệm ứng dụng của họ trọn màn hình. Người lập trình có thể sử dụng các công nghệ hiện tại như HTML5, CSS, JavaScript hoặc C++, C# và VB.NET với XALM để phát triển các ứng dụng dạng Metro. Người lập trình sẽ tận dụng kiến thức về lập trình sẵn có của mình và sử dụng các tập lệnh (APIs) có tên gọi là WinRT để sử dụng các chức năng mà Windows 8 cung cấp để lập trình ứng dụng Metro.Để tìm hiểu về các ứng dụng Metro và cách lập trình chúng, bạn có thể tải bản Windows 8 Developer Preview về cài đặt và viết các ứng dụng cho hệ điều hành Windows 8 còn rất mới mẻ này.

Lập trình với CSDL trong ứng dụng Metro

Ở phiên bản Developer Preview của Windows 8, Microsoft cho biết rằng các ứng dụng Metro hoàn toàn không thể tương tác trực tiếp với các CSDL quan hệ mà chúng ta thường lập trình như SQLCE, Microsoft SQL Server..v.v mà thay vào đó nếu muốn lưu trữ dữ liệu hoặc tương tác với dữ liệu thì chúng ta chỉ có hai cách:
  • Sử dụng Local Storage (kho lưu trữ nội bộ) mà Window 8 cung cấp cho ứng dụng khi ứng dụng được triển khai. Với Local Storage, chúng ta có thể lưu các tập tin ở đó, truy xuất và xử lý chúng. Ví dụ, chúng ta có thể lưu dữ liệu trong các tập tin XML và truy xuất chúng. Cách lưu trữ này có ưu điểm là giúp cô lập các ứng dụng và khiến các ứng dụng chỉ có thể giao tiếp với nhau thông qua những cách mà Windows 8 quy định, CSDL mang tính chất biệt lập và nhớ đó mà người lập trình không phải lo lắng tới các trường hợp như một tập tin bị truy xuất đọc ghi bởi nhiều ứng dụng một lần ..v.v, nhưng đó cũng chính là nhược điểm, bởi lập trình viên phải viết quá nhiều code để xử lý dữ liệu và rõ ràng là nó không hoàn toàn được tối ưu bằng việc sử dụng CSDL quan hệ.
  • Sử dụng CSDL quan hệ bằng cách viết các Services hỗ trợ truy xuất CSDL, các ứng dụng Metro sẽ kết nối với các services lưu hoặc cập nhật dữ liệu. Cách viết ứng dụng như vậy có thể gọi là lập trình ứng dụng phân tán. Đây là một cách lập trình hay, nhưng nó khó mà phù hợp với các ứng dụng nhỏ, và rõ ràng là việc lập trình các ứng dụng như vậy sẽ chiếm rất nhiều thời gian và công sức của người lập trình.
Lập trình với SQLite trong ứng dụng Metro

Giới lập trình hiện nay vẫn thắc mắc với việc tại sao Microsoft không hỗ trợ SQLCE để giúp người lập trình dễ dàng viết các ứng dụng Metro hướng CSDL và họ hy vọng điều đó sẽ thay đổi trong bản Windows 8 Beta mà Microsoft sẽ phát hành trong tương lai gần. Hiện tại, các lập trình viên phải lập trình với XML hoặc tìm một giải pháp khả dĩ có thể thay thế cho việc lập trình với CSDL cục bộ SQLCE.Một trong những giải pháp mà tôi tìm được khi tìm hiểu về việc lập trình ứng dụng Metro đó là sử dụng SQLite bằng cách sử dụng dự án mã nguồn mở sqlite-winrt trên CodePlex. Sqlite-winrt là một dự án nhằm mục đích viết ra một thư viện .NET sử dụng lại code truy xuất sqlite (được viết bằng C++). Tuy nhiên, để sử dụng được thư viện này vào dự án của bạn cũng không phải là điều dễ dàng.

Sử dụng sqlite-winrt để truy xuất CSDL sqlite
Đầu tiên bạn hãy cài lên máy ảo hoặc máy tính của mình hệ điều hành Windows 8 có cài sẵn Visual Studio Express 2011 để có thể lập trình và chạy thử ứng dụng WinRT tại đây. Các bạn cũng có thể tải bản Windows 8 không cài đặt sẵn Visual Studio Express 2011 rồi tải về bản Visual Studio 2011 riêng về để cài, tuy nhiên các bạn sẽ gặp phải một số vướng mắc khi muốn xây dựng ứng dụng Metro (tôi sẽ đề cập về vấn đề này ở một bài viết khác).

Sau đó bạn hãy tải về thư viện sqlite-winrt để có thể bắt đầu lập trình ứng dụng Metro truy xuất dữ liệu sqlite.

Trước khi bắt đầu chúng ta cần lưu ý rằng, ứng dụng chúng ta xây dựng vẫn tuân thủ chặt chẽ các quy định về xây dựng các ứng dụng Metro, nghĩa là CSDL Sqlite sẽ được lưu trong bộ nhớ Local Storage của ứng dụng, và được truy xuất bởi sqlite-winrt, điều đó cũng có nghĩa là CSDL này chỉ được sử dụng bởi ứng dụng chúng ta viết mà thôi.

Ứng dụng chúng ta xây dựng sẽ là một ứng dụng đơn giản để lưu trữ thông tin về những cuốn sách mà người sử dụng yêu thích, các cuốn sách (Book) này sẽ được lưu trữ theo danh mục (Category), nghĩa là mỗi danh mục sẽ có nhiều cuốn sách, và mỗi cuốn sách sẽ chỉ thuộc về một danh mục nào đó.

Ứng dụng của chúng ta có các chức năng chính:
  1. Hiển thị danh sách các danh mục đã được tạo
  2. Hiển thị các cuốn sách của một danh mục được chọn
  3. Tạo danh mục mới
  4. Tạo sách cho một danh mục được chọn
Giao diện của ứng dụng như sau:




Tôi sẽ trình bày cách viết dự án theo kiểu từng bước một để bạn đọc có thể dễ dàng theo dõi:


BƯỚC 1: Tạo dự án theo có tên là MetroDbAccess và thêm tham chiếu đến thư viện sqlite-winrt bằng cách tham chiếu đến tập tin Sqlite.winmd mà bạn đã tải về



BƯỚC 2: Tạo các Model

Lớp Category

public class Category{public int Id { get; set; }public string Name { get; set; }public string Description { get; set; }}

Lớp Book

public class Book{public int Id { get; set; }public string Name { get; set; }public string Author { get; set; }public string Description { get; set; }public Category Category { get; set; }}

Lớp MainPageViewModel (nhằm lưu trữ các thông tin dùng cho các control trên Mainpage.xaml.

public class MainPageViewModel{public MainPageViewModel(){Categories = new ObservableCollection<Category>();Books = new ObservableCollection<Book>();}public ObservableCollection<Category> Categories { get; set; }public ObservableCollection<Book> Books { get; set; }}
BƯỚC 3: Thiết kế giao diện

Để có được giao diện như trên, tôi đã điều chỉnh tập tin MainPage.xaml được tạo ra khi tạo dự án như sau:

<UserControl x:Class="MetroDbAccess.MainPage"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"d:DesignHeight="768" d:DesignWidth="1366"><Grid x:Name="LayoutRoot" Background="#FF0C0C0C" ><Grid.RowDefinitions><RowDefinition Height="*"/><RowDefinition Height="*"/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition Width="*"></ColumnDefinition><ColumnDefinition Width="*"></ColumnDefinition><ColumnDefinition Width="100"></ColumnDefinition></Grid.ColumnDefinitions><StackPanel Grid.Row="0" Grid.Column="0"><TextBlock Text="Category list" FontSize="36" ></TextBlock><ListView x:Name="CategoryList" ItemsSource="{Binding Path=Categories}" SelectionChanged="CategoryList_SelectionChanged" ><ListView.ItemTemplate><DataTemplate><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding Path=Id}" FontSize="16"/><TextBlock Text=" - " FontSize="16"/><TextBlock Text="{Binding Path=Name}" FontSize="16"/><TextBlock Text=" - " FontSize="16"/><TextBlock Text="{Binding Path=Description}" FontSize="16"/></StackPanel></DataTemplate></ListView.ItemTemplate></ListView></StackPanel><StackPanel Grid.Row="0" Grid.Column="1" Margin="10, 0, 0 ,0" Width="300" HorizontalAlignment="Left" ><TextBlock Text="Create category" FontSize="32" ></TextBlock><TextBlock Text="Name:" FontSize="24" ></TextBlock><TextBox x:Name="CategoryNameTextBox"></TextBox><TextBlock Text="Description:" FontSize="24" ></TextBlock><TextBox x:Name="CategoryDescriptionTextBox"></TextBox><Button x:Name="SaveCategoryButton" FontSize="24" Margin="0, 10, 0, 0" Click="SaveCategoryButton_Click">Save</Button></StackPanel><StackPanel Grid.Row="1" Grid.Column="1" Margin="10, 0, 0 ,0" Width="300" HorizontalAlignment="Left" ><TextBlock Text="Create book" FontSize="32" ></TextBlock><TextBlock x:Name="ChosenCategoryTextBlock" Text="Category:" FontSize="24" ></TextBlock><TextBlock Text="Book name:" FontSize="24" ></TextBlock><TextBox x:Name="NameTextBox"></TextBox><TextBlock Text="Author:" FontSize="24" ></TextBlock><TextBox x:Name="AuthorTextBox"></TextBox><TextBlock Text="Description:" FontSize="24" ></TextBlock><TextBox x:Name="DescriptionTextBox"></TextBox><Button x:Name="SaveButton" FontSize="24" Margin="0, 10, 0, 0" Click="SaveButton_Click">Save</Button></StackPanel><StackPanel Grid.Row="1" Grid.Column="0"><TextBlock Text="Book list" FontSize="36" ></TextBlock><ListView x:Name="BookList" ><ListView.ItemTemplate><DataTemplate><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding Path=Id}" FontSize="16"/><TextBlock Text=" - " FontSize="16"/><TextBlock Text="{Binding Path=Name}" FontSize="16"/></StackPanel></DataTemplate></ListView.ItemTemplate></ListView></StackPanel><StackPanel Grid.Column="2" Grid.Row="0"><Button x:Name="InitDbButton" Click="InitDbButton_Click">Init Database</Button><Button x:Name="ReplaceDbButton" Click="ReplaceDbButton_Click" >Restore default DB</Button></StackPanel></Grid></UserControl>
Các bạn cần lưu ý rằng, trong này có sử dụng kỹ thuật Data Binding và có những khai báo quản lý sự kiện (Event Handler), các kỹ thuật này không khó đối với người có kinh nghiệm lập trình với Silverlight hoặc WPF, nếu bạn còn xa lạ với XAML, bạn có thể tìm hiểu các bài viết về chủ đề này.

BƯỚC 4: Cài đặt mã của phương thức InitViewModel() trong MainPage.xaml.cs. Phương thức này sẽ mở CSDL sqlite có tên là bookdb trong Local Storage hoặc tạo mới nếu chưa có. Sau đó nó sẽ tạo bảng Categories nếu bảng này chưa tồn tại. Tiếp theo nó sẽ truy vấn để lấy danh sách các Category và bind vào CategoryList (ListView Control) – các bạn xem lại mã XAML của CategoryList để xem cách trình bày.

private void InitViewModel(){SQLite.Database db = new SQLite.Database("bookdb"); // creates the database file in the local app folderif (db.Ready){var stmt = db.PrepareStatement("CREATE TABLE IF NOT EXISTS Categories (id INTEGER PRIMARY KEY,name TEXT, description TEXT)");if (stmt.Execute()){//Upadate the category listModel.Categories = new System.Collections.ObjectModel.ObservableCollection<Category>();stmt = db.PrepareStatement("SELECT id, name, description FROM Categories");while (stmt.HasMore()){//Fill the category listvar id = stmt.ColumnAsIntAt(0);var catName = stmt.ColumnAsTextAt(1);var desc = stmt.ColumnAsTextAt(2);Model.Categories.Add(new Category() { Id = id, Name = catName, Description = desc });}}CategoryList.ItemsSource = Model.Categories;}}
BƯỚC 5: Xử lý sự kiện click chuột lên một item trên CategoryList bằng cách cài đặt phương thức CategoryList_SelectionChanged, phương thức này sẽ gọi phương thức GetBookByCategory để lấy danh sách các cuốn sách thuộc Category được chọn và sau đó bind vào control BookList

private void CategoryList_SelectionChanged(object sender, SelectionChangedEventArgs e){var index = CategoryList.SelectedIndex;ChosenCategoryTextBlock.Text = "Category: " + Model.Categories[index].Name;//Show ebooks of the selected categoryGetBookByCategory(Model.Categories[index].Id);}


BƯỚC 6: Xử lý sự kiện click của nút SaveCategoryButton để tạo Category mới và cập nhật control CategoryList. Lưu ý rằng, WinRT chưa hỗ trợ cho ObservableCollection nên Binding một chiều và hai chiều cho Control chưa thể thực hiện được.

private void SaveCategoryButton_Click(object sender, RoutedEventArgs e){string name = CategoryNameTextBox.Text;string description = CategoryDescriptionTextBox.Text;if (string.IsNullOrEmpty(name)) return;SQLite.Database db = new SQLite.Database("bookdb"); // creates the database file in the local app folderif (db.Ready){var stmt = db.PrepareStatement("CREATE TABLE IF NOT EXISTS Categories (id INTEGER PRIMARY KEY,name TEXT, description TEXT)");if (stmt.Execute()){stmt = db.PrepareStatement("INSERT INTO Categories (id, name, description) VALUES (NULL, ?, ?)");stmt.BindText(1, name);stmt.BindText(2, description);if (stmt.Execute()){InitViewModel();}}}}


BƯỚC 7: Xử lý sự kiện click chuột của nút SaveButton để tạo sách mới và cập nhật danh sách Book của Category được chọn.

private void SaveButton_Click(object sender, RoutedEventArgs e){if (CategoryList.SelectedIndex < 0){ChosenCategoryTextBlock.Text = "No category is selected!";ChosenCategoryTextBlock.Foreground = new SolidColorBrush(Colors.Red);return;}SQLite.Database db = new SQLite.Database("bookdb"); // creates the database file in the local app folderif (db.Ready){var stmt = db.PrepareStatement("CREATE TABLE IF NOT EXISTS Books (id INTEGER PRIMARY KEY, name TEXT, author TEXT, description TEXT, categoryid INTEGER)");if (stmt.Execute()){stmt = db.PrepareStatement("INSERT INTO Books (id, name, author, description, categoryid) VALUES (NULL, ?, ?, ?, ?)");stmt.BindText(1, NameTextBox.Text);stmt.BindText(2, AuthorTextBox.Text);stmt.BindText(3, DescriptionTextBox.Text);var catId = Model.Categories[CategoryList.SelectedIndex].Id;stmt.BindInt(4, catId);if (stmt.Execute()){GetBookByCategory(catId);}}}}


BƯỚC 8: Sao chép CSDL bookdb có sẵn vào Local Storage trong lần chạy đầu tiên

Đến ngang đây, chúng ta đã có thể chạy được ứng dụng, tạo Category mới, tạo Book mới..v.v tuy nhiên có một vấn đề lớn đó là, khi chúng ta deploy ứng dụng, thì CSDL của ứng dụng bằng rỗng, bởi vì chúng ta không có sẵn CSDL bookdb bên trong Local Storage của ứng dụng. Để giải quyết được vấn đề này, chúng ta cần phải có CSDL bookdb trước và lưu như là một tài nguyên của ứng dụng

Sau đó chúng ta sẽ xây dựng phương thức InitDb() để thực hiện việc sao chép CSDL bookdb vào LocalStorage bằng cách sử dụng tập lệnh thao tác với LocalStorage do WinRT cung cấp

private async void InitDb(){bool searchResult = false;StorageFolder storageFolder = ApplicationData.Current.LocalFolder;try{StorageFile sampleFile = await storageFolder.GetFileAsync("bookdb");searchResult = true;}catch (FileNotFoundException){}if (!searchResult){var resourceLoader = new ResourceLoader();var file = resourceLoader.GetFile("bookdb");var operation = await file.CopyAsync(ApplicationData.Current.LocalFolder, "bookdb");}InitViewModel();DataContext = Model;}

Phương thức InitDb sẽ được gọi ở cấu tử của lớp MainPage và sẽ kiểm tra xem thử bookdb đã tồn tại chưa, nếu chưa tồn tại thì sẽ chép bookdb là csdl mặc định được gắn làm tài nguyên có sẵn trong project

Mã nguồn đầy đủ của tập tin MainPage.xaml.cs:

using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Threading.Tasks;using MetroDbAccess.Model;using Windows.ApplicationModel.Resources;using Windows.Foundation;using Windows.Storage;using Windows.UI.Xaml;using Windows.UI.Xaml.Controls;using Windows.UI.Xaml.Data;using Windows.UI.Xaml.Media;namespace MetroDbAccess{partial class MainPage{public MainPageViewModel Model { get; set; }public MainPage(){InitializeComponent();Model = new MainPageViewModel();InitDb();}private void SaveButton_Click(object sender, RoutedEventArgs e){if (CategoryList.SelectedIndex < 0){ChosenCategoryTextBlock.Text = "No category is selected!";ChosenCategoryTextBlock.Foreground = new SolidColorBrush(Colors.Red);return;}SQLite.Database db = new SQLite.Database("bookdb"); // creates the database file in the local app folderif (db.Ready){var stmt = db.PrepareStatement("CREATE TABLE IF NOT EXISTS Books (id INTEGER PRIMARY KEY, name TEXT, author TEXT, description TEXT, categoryid INTEGER)");if (stmt.Execute()){stmt = db.PrepareStatement("INSERT INTO Books (id, name, author, description, categoryid) VALUES (NULL, ?, ?, ?, ?)");stmt.BindText(1, NameTextBox.Text);stmt.BindText(2, AuthorTextBox.Text);stmt.BindText(3, DescriptionTextBox.Text);var catId = Model.Categories[CategoryList.SelectedIndex].Id;stmt.BindInt(4, catId);if (stmt.Execute()){GetBookByCategory(catId);}}}}private void SaveCategoryButton_Click(object sender, RoutedEventArgs e){string name = CategoryNameTextBox.Text;string description = CategoryDescriptionTextBox.Text;if (string.IsNullOrEmpty(name)) return;SQLite.Database db = new SQLite.Database("bookdb"); // creates the database file in the local app folderif (db.Ready){var stmt = db.PrepareStatement("CREATE TABLE IF NOT EXISTS Categories (id INTEGER PRIMARY KEY,name TEXT, description TEXT)");if (stmt.Execute()){stmt = db.PrepareStatement("INSERT INTO Categories (id, name, description) VALUES (NULL, ?, ?)");stmt.BindText(1, name);stmt.BindText(2, description);if (stmt.Execute()){InitViewModel();}}}}private void InitViewModel(){SQLite.Database db = new SQLite.Database("bookdb"); // creates the database file in the local app folderif (db.Ready){var stmt = db.PrepareStatement("CREATE TABLE IF NOT EXISTS Categories (id INTEGER PRIMARY KEY,name TEXT, description TEXT)");if (stmt.Execute()){//Upadate the category listModel.Categories = new System.Collections.ObjectModel.ObservableCollection<Category>();stmt = db.PrepareStatement("SELECT id, name, description FROM Categories");while (stmt.HasMore()){//Fill the category listvar id = stmt.ColumnAsIntAt(0);var catName = stmt.ColumnAsTextAt(1);var desc = stmt.ColumnAsTextAt(2);Model.Categories.Add(new Category() { Id = id, Name = catName, Description = desc });}}CategoryList.ItemsSource = Model.Categories;}}private void GetBookByCategory(int categoryId){SQLite.Database db = new SQLite.Database("bookdb"); // creates the database file in the local app folderif (db.Ready){var stmt = db.PrepareStatement("SELECT Books.id, Books.name, author, Books.description, categoryid, Categories.Name, Categories.Description FROM Books INNER JOIN Categories WHERE Books.categoryid = Categories.id AND Books.categoryid = " + categoryId.ToString());Model.Books = new System.Collections.ObjectModel.ObservableCollection<Book>();while (stmt.HasMore()){var book = new Book();book.Id = stmt.ColumnAsIntAt(0);book.Name = stmt.ColumnAsTextAt(1);book.Author = stmt.ColumnAsTextAt(2);book.Description = stmt.ColumnAsTextAt(3);book.Category = new Category();book.Category.Id = stmt.ColumnAsIntAt(4);book.Category.Name = stmt.ColumnAsTextAt(5);book.Category.Description = stmt.ColumnAsTextAt(6);Model.Books.Add(book);}BookList.ItemsSource = Model.Books;}}private void CategoryList_SelectionChanged(object sender, SelectionChangedEventArgs e){var index = CategoryList.SelectedIndex;ChosenCategoryTextBlock.Text = "Category: " + Model.Categories[index].Name;//Show ebooks of the selected categoryGetBookByCategory(Model.Categories[index].Id);}private async void InitDbButton_Click(object sender, RoutedEventArgs e){InitDb();}private async void InitDb(){bool searchResult = false;StorageFolder storageFolder = ApplicationData.Current.LocalFolder;try{StorageFile sampleFile = await storageFolder.GetFileAsync("bookdb");searchResult = true;}catch (FileNotFoundException){}if (!searchResult){var resourceLoader = new ResourceLoader();var file = resourceLoader.GetFile("bookdb");var operation = await file.CopyAsync(ApplicationData.Current.LocalFolder, "bookdb");}InitViewModel();DataContext = Model;}private async void ReplaceDbButton_Click(object sender, RoutedEventArgs e){bool searchResult = false;StorageFolder storageFolder = ApplicationData.Current.LocalFolder;try{StorageFile sampleFile = await storageFolder.GetFileAsync("bookdb");await sampleFile.DeleteAsync();var resourceLoader = new ResourceLoader();var file = resourceLoader.GetFile("bookdb");file.CopyAsync(ApplicationData.Current.LocalFolder, "bookdb");searchResult = true;}catch (FileNotFoundException){}if (!searchResult){var resourceLoader = new ResourceLoader();var file = resourceLoader.GetFile("bookdb");var operation = await file.CopyAsync(ApplicationData.Current.LocalFolder, "bookdb");}}}}


BƯỚC 9: Chúc mừng bạn đã xây dựng ứng dụng thành công, hãy thử chạy và kiểm tra ứng dụng. Trong trường hợp không chạy được, bạn có thể thao khảo code mẫu tại đây.

Trên đây là một bài hướng dẫn về cách lập trình CSDL với Sqlite cho ứng dụng Metro trên Windows 8, hy vọng nó sẽ hữu ích với các bạn.

Tuyên bố Không chịu trách nhiệm Nội dung Giải pháp Cộng đồng
CÔNG TY MICROSOFT VÀ/HOẶC CÁC NHÀ CUNG CẤP CỦA HỌ KHÔNG BẢO ĐẢM VỀ TÍNH PHÙ HỢP, ĐỘ TIN CẬY HOẶC TÍNH CHÍNH XÁC CỦA THÔNG TIN VÀ HÌNH ẢNH LIÊN QUAN Ở ĐÂY. MỌI THÔNG TIN VÀ HÌNH ẢNH NHƯ VẬY ĐƯỢC CUNG CẤP “NHƯ NGUYÊN MẪU” MÀ KHÔNG CÓ BẤT KỲ BẢO ĐẢM NÀO. MICROSOFT VÀ/HOẶC CÁC NHÀ CUNG CẤP CỦA HỌ KHÔNG CHỊU TRÁCH NHIỆM ĐỐI VỚI MỌI BẢO ĐẢM VÀ ĐIỀU KIỆN VỀ THÔNG TIN VÀ HÌNH ẢNH LIÊN QUAN NÀY, BAO GỒM CẢ MỌI BẢO ĐẢM VÀ ĐIỀU KIỆN LIÊN QUAN VỀ TÍNH THƯƠNG MẠI, PHÙ HỢP CHO MỘT MỤC ĐÍCH ĐẶC BIỆT, NỖ LỰC CỦA CÔNG VIỆC, TƯ CÁCH VÀ CAM KẾT KHÔNG VI PHẠM. BẠN ĐỒNG Ý MỘT CÁCH CỤ THỂ LÀ KHÔNG CÓ TRƯỜNG HỢP NÀO MÀ MICROSOFT VÀ/HOẶC CÁC NHÀ CUNG CẤP CỦA HỌ BỊ RÀNG BUỘC VÀO BẤT KỲ THIỆT HẠI TRỰC TIẾP, GIÁN TIẾP, TRỪNG PHẠT, TÌNH CỜ, ĐẶC BIỆT, HỆ QUẢ HOẶC BẤT KỲ THIỆT HẠI DẠNG NÀO, BAO GỒM NHƯNG KHÔNG GIỚI HẠN THIỆT HẠI DO MẤT MÁT, DỮ LIỆU HOẶC LỢI ÍCH, XẢY RA HOẶC TRONG MỌI CÁCH LIÊN QUAN ĐẾN VIỆC SỬ DỤNG HOẶC KHÔNG THỂ SỬ DỤNG THÔNG TIN VÀ HÌNH ẢNH LIÊN QUAN CÓ Ở ĐÂY, DÙ LÀ DỰA VÀO HỢP ĐỒNG, LỖI GÂY THIỆT HẠI, SƠ SUẤT, NGHĨA VỤ PHÁP LÝ HOẶC BẤT KỲ CƠ SỞ NÀO KHÁC, NGAY CẢ NẾU MICROSOFT HOẶC BẤT KỲ NHÀ CUNG CẤP NÀO CỦA HỌ ĐÃ ĐƯỢC TƯ VẤN VỀ KHẢ NĂNG BỊ THIỆT HẠI.
Note This is a "FAST PUBLISH" article created directly from within the Microsoft support organization. The information contained herein is provided as-is in response to emerging issues. As a result of the speed in making it available, the materials may include typographical errors and may be revised at any time without notice. See Terms of Use for other considerations.
Thuộc tính

ID Bài viết: 2663593 - Xem lại Lần cuối: 01/30/2012 07:31:00 - Bản sửa đổi: 2.0

  • kbprb kbtshoot kbstepbystep kbgraphxlink kbmvp KB2663593
Phản hồi