diff --git a/PacticeSolution/MSSQL_MVVM_Sample/App.xaml b/PacticeSolution/MSSQL_MVVM_Sample/App.xaml
new file mode 100644
index 0000000..7364a49
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/App.xaml.cs b/PacticeSolution/MSSQL_MVVM_Sample/App.xaml.cs
new file mode 100644
index 0000000..9a80667
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace MSSQL_MVVM_Sample
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/AssemblyInfo.cs b/PacticeSolution/MSSQL_MVVM_Sample/AssemblyInfo.cs
new file mode 100644
index 0000000..8b5504e
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/Database/SqlManager.cs b/PacticeSolution/MSSQL_MVVM_Sample/Database/SqlManager.cs
new file mode 100644
index 0000000..6287f14
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/Database/SqlManager.cs
@@ -0,0 +1,177 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Data.SqlClient;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using MSSQL_MVVM_Sample.Structure;
+
+namespace MSSQL_MVVM_Sample.Database
+{
+ public sealed class SqlManager : Singleton
+ {
+ public event ExceptionEventHandler ExceptionEvent;
+
+ public SqlConnection Connection { get; set; }
+ public string ConnectionString { get; set; }
+
+ private SqlCommand _lastExecutedCommand;
+ private int _retryCount;
+
+ public bool IsRunning { get { return CheckDBConnected(); } }
+
+ public static SqlManager GetNewInstanceConnection()
+ {
+ if (Instance == null)
+ return null;
+
+ SqlManager sqlManager = new SqlManager()
+ {
+ ConnectionString = Instance.ConnectionString
+ };
+ sqlManager.GetConnection();
+ sqlManager.ExceptionEvent = Instance.ExceptionEvent;
+
+ return sqlManager;
+ }
+
+ public void SetConnectionString(string connectionString)
+ {
+ ConnectionString = connectionString;
+ }
+
+ public bool GetConnection()
+ {
+ try
+ {
+ if (Connection != null)
+ {
+ Connection.Close();
+ Connection.Dispose();
+ Connection = null;
+ }
+
+ Connection = new SqlConnection(ConnectionString);
+ Connection.Open();
+
+ if (Connection.State != ConnectionState.Open)
+ return false;
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ HandleExceptionEvent("GetConnection", ex);
+ return false;
+ }
+ }
+
+ private bool CheckDBConnected()
+ {
+ try
+ {
+ string query = "SELECT GETDATE() Date";
+ SqlCommand cmd = new SqlCommand()
+ {
+ Connection = Connection,
+ CommandText = query,
+ };
+
+ SqlDataReader result = cmd.ExecuteReader();
+ if (result == null || !result.HasRows)
+ return false;
+
+ return true;
+ }
+ catch (Exception ex)
+ {
+ HandleExceptionEvent("CheckDBConnected", ex);
+ return false;
+ }
+ }
+
+ public int ExecuteNonQuery(string query)
+ {
+ try
+ {
+ lock (this)
+ {
+ SqlCommand cmd = new SqlCommand()
+ {
+ Connection = Connection,
+ CommandText = query,
+ };
+
+ return cmd.ExecuteNonQuery();
+ }
+ }
+ catch (Exception ex)
+ {
+ HandleExceptionEvent("ExecuteNonQuery", ex);
+ return -1;
+ }
+ }
+
+ public int ExecuteNonQuery(string query, SqlParameter[] sqlParams)
+ {
+ try
+ {
+ lock (this)
+ {
+ SqlCommand cmd = new SqlCommand()
+ {
+ Connection = Connection,
+ CommandText = query,
+ };
+
+ for (int i = 0; i < sqlParams.Length; i++)
+ {
+ cmd.Parameters.Add(sqlParams);
+ }
+
+ return cmd.ExecuteNonQuery();
+ }
+ }
+ catch (Exception ex)
+ {
+ HandleExceptionEvent("ExecuteNonQuery with sqlParams", ex);
+ return -1;
+ }
+ }
+
+ public DataSet ExecuteSelectQuery(string query)
+ {
+ try
+ {
+ lock (this)
+ {
+ SqlCommand cmd = new SqlCommand()
+ {
+ Connection = Connection,
+ CommandText = query,
+ };
+ SqlDataAdapter adt = new SqlDataAdapter()
+ {
+ SelectCommand = cmd,
+ };
+
+ DataSet ds = new DataSet();
+ adt.Fill(ds);
+ return ds;
+ }
+ }
+ catch (Exception ex)
+ {
+ HandleExceptionEvent("ExecuteNonQuery with sqlParams", ex);
+ return null;
+ }
+ }
+
+ private void HandleExceptionEvent(string id, Exception ex)
+ {
+ if (ExceptionEvent != null)
+ ExceptionEvent(id, ex);
+ }
+ }
+}
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/MSSQL_MVVM_Sample.csproj b/PacticeSolution/MSSQL_MVVM_Sample/MSSQL_MVVM_Sample.csproj
new file mode 100644
index 0000000..eda450c
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/MSSQL_MVVM_Sample.csproj
@@ -0,0 +1,14 @@
+
+
+
+ WinExe
+ net6.0-windows
+ enable
+ true
+
+
+
+
+
+
+
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/MainWindow.xaml b/PacticeSolution/MSSQL_MVVM_Sample/MainWindow.xaml
new file mode 100644
index 0000000..676d121
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/MainWindow.xaml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/MainWindow.xaml.cs b/PacticeSolution/MSSQL_MVVM_Sample/MainWindow.xaml.cs
new file mode 100644
index 0000000..2ab2de4
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/MainWindow.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace MSSQL_MVVM_Sample
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/Model/Student.cs b/PacticeSolution/MSSQL_MVVM_Sample/Model/Student.cs
new file mode 100644
index 0000000..8530452
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/Model/Student.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MSSQL_MVVM_Sample.Model
+{
+ internal class Student
+ {
+ public string Name { get; set; }
+
+ public int Age { get; set; }
+
+ public string Grade {get; set; }
+
+ public int Score { get; set; }
+ }
+}
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/Structure/DelegateCommand.cs b/PacticeSolution/MSSQL_MVVM_Sample/Structure/DelegateCommand.cs
new file mode 100644
index 0000000..0013968
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/Structure/DelegateCommand.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace MSSQL_MVVM_Sample.Structure
+{
+ internal class DelegateCommand : ICommand
+ {
+ private readonly Action _execute;
+ private readonly Func _canExecute;
+
+ public event EventHandler? CanExecuteChanged;
+
+ public DelegateCommand(Action execute) : this(execute, null)
+ {
+
+ }
+
+ public DelegateCommand(Action execute, Func canExecute)
+ {
+ _execute = execute;
+ _canExecute = canExecute;
+ }
+
+ public bool CanExecute(object? parameter)
+ {
+ if (_canExecute == null)
+ return true;
+
+ return _canExecute();
+ }
+
+ public void Execute(object? parameter)
+ {
+ _execute();
+ }
+
+ public void RaiseCanExecuteChanged()
+ {
+ if (this.CanExecuteChanged == null)
+ return;
+
+ this.CanExecuteChanged(this, EventArgs.Empty);
+ }
+ }
+}
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/Structure/Singleton.cs b/PacticeSolution/MSSQL_MVVM_Sample/Structure/Singleton.cs
new file mode 100644
index 0000000..a102f16
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/Structure/Singleton.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MSSQL_MVVM_Sample.Structure
+{
+ public class Singleton where T : class, new()
+ {
+ private static readonly Lazy _instnace = new Lazy(() => new T());
+
+ public delegate void ExceptionEventHandler(string id, Exception ex);
+
+ public static T Instance { get { return _instnace.Value; } }
+ }
+}
diff --git a/PacticeSolution/MSSQL_MVVM_Sample/ViewModel/MainViewModel.cs b/PacticeSolution/MSSQL_MVVM_Sample/ViewModel/MainViewModel.cs
new file mode 100644
index 0000000..f21638c
--- /dev/null
+++ b/PacticeSolution/MSSQL_MVVM_Sample/ViewModel/MainViewModel.cs
@@ -0,0 +1,140 @@
+using MSSQL_MVVM_Sample.Database;
+using MSSQL_MVVM_Sample.Model;
+using MSSQL_MVVM_Sample.Structure;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Data;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Interop;
+
+namespace MSSQL_MVVM_Sample.ViewModel
+{
+ internal class MainViewModel : INotifyPropertyChanged
+ {
+ private Student _student;
+
+ public event PropertyChangedEventHandler? PropertyChanged;
+
+ public MainViewModel()
+ {
+ _student = new Student();
+ SqlManager.Instance.ConnectionString = "Data Source=peacecloud.synology.me,21433;Initial Catalog=Study;User ID=study;Password=Study1234";
+ }
+
+ public string Name
+ {
+ get { return _student.Name; }
+ set
+ {
+ _student.Name = value;
+ OnPropertyChanged(nameof(this.Name));
+ }
+ }
+
+ public int Age
+ {
+ get { return _student.Age; }
+ set
+ {
+ _student.Age = value;
+ OnPropertyChanged(nameof(this.Age));
+ }
+ }
+
+ public string Grade
+ {
+ get { return _student.Grade; }
+ set
+ {
+ _student.Grade = value;
+ OnPropertyChanged(nameof(this.Grade));
+ }
+ }
+
+ public int Score
+ {
+ get { return _student.Score; }
+ set
+ {
+ _student.Score = value;
+ OnPropertyChanged(nameof(this.Score));
+ }
+ }
+
+ private ObservableCollection _sampleData;
+ public ObservableCollection SampleData
+ {
+ get
+ {
+ if (_sampleData == null)
+ _sampleData = new ObservableCollection();
+
+ return _sampleData;
+ }
+ set { _sampleData = value; }
+ }
+
+ private ICommand _connectCommand;
+ public ICommand ConnectCommand
+ {
+ get { return _connectCommand ?? (_connectCommand = new DelegateCommand(ConnectDatabase)); }
+ }
+
+ private ICommand _searchCommand;
+ public ICommand SearchCommnad
+ {
+ get { return _searchCommand ?? (_searchCommand = new DelegateCommand(SearchData)); }
+ }
+
+ private void ConnectDatabase()
+ {
+ if (!SqlManager.Instance.GetConnection())
+ {
+ MessageBox.Show("Fail to connect to database", "Error");
+ return;
+ }
+
+ MessageBox.Show("Success to connect to database", "Inform");
+ }
+
+ private void SearchData()
+ {
+ if (this.SampleData != null)
+ this.SampleData.Clear();
+
+ string query = @"SELECT * FROM Student";
+
+ DataSet ds = SqlManager.Instance.ExecuteSelectQuery(query);
+ if (ds == null || ds.Tables.Count < 1 || ds.Tables[0].Rows == null || ds.Tables[0].Rows.Count < 1)
+ {
+ MessageBox.Show("No data in table", "Alert");
+ return;
+ }
+
+ foreach (DataRow row in ds.Tables[0].Rows)
+ {
+ Student student = new Student()
+ {
+ Name = row["Name"].ToString(),
+ Age = (int)row["Age"],
+ Grade = row["Grade"].ToString(),
+ Score = (int)row["Score"]
+ };
+
+ this.SampleData.Add(student);
+ }
+ }
+
+ private void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+}
diff --git a/PacticeSolution/PacticeSolution.sln b/PacticeSolution/PacticeSolution.sln
index f3584b8..52af3b2 100644
--- a/PacticeSolution/PacticeSolution.sln
+++ b/PacticeSolution/PacticeSolution.sln
@@ -89,7 +89,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DashedFigure", "DashedFigur
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddressBook_MVVMSample", "AddressBook_MVVMSample\AddressBook_MVVMSample.csproj", "{6A452509-3DEB-41A3-8AA3-8DB4BE948D28}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddressBook_MVVMSampleV2", "AddressBook_MVVMSampleV2\AddressBook_MVVMSampleV2.csproj", "{0CE2A390-5A8B-4F13-9F60-A4AF77F0B1F3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AddressBook_MVVMSampleV2", "AddressBook_MVVMSampleV2\AddressBook_MVVMSampleV2.csproj", "{0CE2A390-5A8B-4F13-9F60-A4AF77F0B1F3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MSSQL_MVVM_Sample", "MSSQL_MVVM_Sample\MSSQL_MVVM_Sample.csproj", "{BAAF8254-74D1-406D-9765-43DCA9F939C7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -273,6 +275,10 @@ Global
{0CE2A390-5A8B-4F13-9F60-A4AF77F0B1F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CE2A390-5A8B-4F13-9F60-A4AF77F0B1F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CE2A390-5A8B-4F13-9F60-A4AF77F0B1F3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BAAF8254-74D1-406D-9765-43DCA9F939C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BAAF8254-74D1-406D-9765-43DCA9F939C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BAAF8254-74D1-406D-9765-43DCA9F939C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BAAF8254-74D1-406D-9765-43DCA9F939C7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE