diff --git a/.gitignore b/.gitignore
index dfcfd56..30fbdce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,9 @@
*.userosscache
*.sln.docstates
+# directories
+.idea
+
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
diff --git a/BankingSoftware/BankingSoftware.csproj b/BankingSoftware/BankingSoftware.csproj
new file mode 100644
index 0000000..d7fa852
--- /dev/null
+++ b/BankingSoftware/BankingSoftware.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net7.0
+ disable
+ enable
+ BankingSoftware
+
+
+
+
+
+
+
+
diff --git a/BankingSoftware/Common/AddSymbol.cs b/BankingSoftware/Common/AddSymbol.cs
new file mode 100644
index 0000000..a727510
--- /dev/null
+++ b/BankingSoftware/Common/AddSymbol.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace BankingSoftware.Common;
+
+public static class AddSymbol
+{
+ public static void AddBreakLine(int symbolLength = 80, string symbol = "-")
+ {
+ for (var i = 0; i < symbolLength; i++)
+ Console.Write(symbol);
+
+ Console.WriteLine("");
+ }
+
+ public static void AddBreakLines(int symbolLength = 80, int lineLength = 3, string symbol = "-")
+ {
+ for (var k = 0; k < lineLength; k++)
+ AddBreakLine(symbolLength, symbol);
+
+ Console.WriteLine("\n");
+ }
+}
\ No newline at end of file
diff --git a/BankingSoftware/Common/Hash.cs b/BankingSoftware/Common/Hash.cs
new file mode 100644
index 0000000..c5cd96a
--- /dev/null
+++ b/BankingSoftware/Common/Hash.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace BankingSoftware.Common;
+
+// to hash input values
+public class Hash
+{
+ private const int keySize = 16;
+ private const int iterations = 350000;
+ private readonly HashAlgorithmName hashAlgorithm = HashAlgorithmName.SHA256;
+
+ public string HashValues(string[] values)
+ {
+ var joinedValues = string.Join("", values);
+
+ var salt = new UTF8Encoding(true).GetBytes(joinedValues);
+
+ var hash = Rfc2898DeriveBytes.Pbkdf2(
+ Encoding.UTF8.GetBytes(joinedValues),
+ salt,
+ iterations,
+ hashAlgorithm,
+ keySize);
+
+ return Convert.ToHexString(hash);
+ }
+}
\ No newline at end of file
diff --git a/BankingSoftware/Common/VerifyInput.cs b/BankingSoftware/Common/VerifyInput.cs
new file mode 100644
index 0000000..2d7a1cb
--- /dev/null
+++ b/BankingSoftware/Common/VerifyInput.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Linq;
+using System.Text.RegularExpressions;
+
+namespace BankingSoftware.Common;
+
+// verify input by user
+public static partial class VerifyInput
+{
+ public static string VerifyForStringOnly(string input)
+ {
+ while (
+ string.IsNullOrEmpty(input)
+ || string.IsNullOrWhiteSpace(input)
+ || input.Any(char.IsNumber)
+ || input.Any(c =>
+ {
+ char.IsNumber(c);
+ char.IsSymbol(c);
+ char.IsPunctuation(c);
+ char.IsSeparator(c);
+
+ return false;
+ })
+ )
+ {
+ Console.WriteLine("Input must not contain any number or symbol");
+ Console.Write("Please input new value: ");
+ input = Console.ReadLine();
+ }
+
+ Console.WriteLine("Input is valid");
+ return input;
+ }
+
+ public static string VerifyForStringAndNumber(string stringNumber)
+ {
+ while (
+ string.IsNullOrEmpty(stringNumber)
+ || string.IsNullOrWhiteSpace(stringNumber)
+ || stringNumber.Any(c =>
+ {
+ char.IsLetter(c);
+ char.IsPunctuation(c);
+ char.IsSymbol(c);
+
+ return false;
+ })
+ )
+ {
+ Console.WriteLine("Input must not contain any number or symbol");
+ Console.Write("Please input new value: ");
+ stringNumber = Console.ReadLine();
+ }
+
+ Console.WriteLine("Input is valid");
+ return stringNumber;
+ }
+
+ public static int VerifyNumber(int number)
+ {
+ while (
+ string.IsNullOrEmpty(number.ToString())
+ || string.IsNullOrWhiteSpace(number.ToString())
+ || number.ToString().Any(c =>
+ {
+ char.IsSymbol(c);
+ char.IsPunctuation(c);
+ char.IsLetter(c);
+
+
+ return false;
+ })
+ )
+ {
+ Console.WriteLine("Input must not contain any letter or symbol");
+ Console.Write("Please input your value: ");
+ number = int.Parse(Console.ReadLine());
+ }
+
+ Console.WriteLine("Number is valid");
+ return number;
+ }
+
+ public static string VerifyPassword(string password)
+ {
+ while (
+ string.IsNullOrEmpty(password)
+ || string.IsNullOrWhiteSpace(password)
+ )
+ {
+ Console.WriteLine("Input must not contain any space");
+ Console.Write("Please input new value: ");
+ password = Console.ReadLine();
+ }
+
+ Console.WriteLine("Password is valid");
+ return password;
+ }
+
+ [GeneratedRegex("^\\(?([0-9]{3})\\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$")]
+ private static partial Regex MyRegex();
+
+ public static string VerifyPhoneNumber(string phoneNumber)
+ {
+ while (!MyRegex().IsMatch(phoneNumber))
+ {
+ Console.WriteLine("Input must not contain any letter or symbol");
+ Console.Write("Please input new value: ");
+ phoneNumber = Console.ReadLine();
+ }
+
+ Console.WriteLine("Phone number is valid");
+ return phoneNumber;
+ }
+
+ public static string VerifyEmail(string email)
+ {
+ var regex = @"^[^@\s]+@[^@\s]+\.(com|net|org|gov)$";
+
+ while (!Regex.IsMatch(email, regex, RegexOptions.IgnoreCase))
+ {
+ Console.Write("Input a valid email: ");
+ email = Console.ReadLine();
+ }
+
+ Console.WriteLine("Email is valid.");
+ return email;
+ }
+}
\ No newline at end of file
diff --git a/BankingSoftware/Common/WriteFile.cs b/BankingSoftware/Common/WriteFile.cs
new file mode 100644
index 0000000..faf9e37
--- /dev/null
+++ b/BankingSoftware/Common/WriteFile.cs
@@ -0,0 +1,24 @@
+using System.IO;
+using System.Text;
+
+namespace BankingSoftware.Common;
+
+// for writing to text files
+public static class WriteFile
+{
+ public static void WriteLine(string lineText, string fileName)
+ {
+ using var fs = File.AppendText(fileName);
+ fs.Write(lineText);
+ }
+
+ public static void WriteLines(string[] linesTexts, string fileName)
+ {
+ using var fs = File.OpenWrite(fileName);
+ for (var i = 0; i < linesTexts.Length; i++)
+ {
+ var info = new UTF8Encoding(true).GetBytes(linesTexts[i] + "\n");
+ fs.Write(info, 0, info.Length);
+ }
+ }
+}
\ No newline at end of file
diff --git a/BankingSoftware/Modules/AccountModule.cs b/BankingSoftware/Modules/AccountModule.cs
new file mode 100644
index 0000000..f5bd39d
--- /dev/null
+++ b/BankingSoftware/Modules/AccountModule.cs
@@ -0,0 +1,120 @@
+using System;
+using System.IO;
+using BankingSoftware.Common;
+
+namespace BankingSoftware.Modules;
+
+// Module to handle account operations
+public class AccountModule
+{
+ private static string GetAccountFile(in string userLogin, string accountStoragePath)
+ {
+ var charFileName = userLogin.Split();
+ var accountFileName = new Hash().HashValues(charFileName);
+ var accountFilenameWithPath = accountStoragePath + accountFileName + ".txt";
+
+ return accountFilenameWithPath;
+ }
+
+ private static string[] GetAccountSummary(string accountFilename)
+ {
+ var accountInfo = File.ReadAllLines(accountFilename);
+
+ return accountInfo;
+ }
+
+ public static void Deposit(in string userLogin, string accountStoragePath)
+ {
+ var accountFilename = GetAccountFile(userLogin, accountStoragePath);
+
+ var accountInfo = GetAccountSummary(accountFilename);
+ foreach (var info in accountInfo) Console.WriteLine(info);
+
+ Console.Write("Please enter the amount you want to deposit: ");
+ var depositAmount = int.Parse(Console.ReadLine());
+
+ while (depositAmount is string && depositAmount < 0) depositAmount = int.Parse(Console.ReadLine());
+
+ var depositAmountLine = accountInfo[2].Split(": $");
+ var depositTimeLine = accountInfo[3].Split(": ");
+ depositAmountLine[1] = depositAmount.ToString();
+ depositTimeLine[1] = DateTime.Now.ToString();
+
+ accountInfo[2] = string.Join(": $", depositAmountLine);
+ accountInfo[3] = string.Join(": ", depositTimeLine);
+
+ var totalLine = accountInfo[4].Split(": $");
+
+ var totalAmount = int.Parse(totalLine[1]) + depositAmount;
+ totalLine[1] = totalAmount.ToString();
+ accountInfo[4] = string.Join(": $", totalLine);
+
+ Console.WriteLine("You've deposited ${0} to your account", depositAmount);
+
+ Console.WriteLine("\nYour total amount is ${0}", totalAmount);
+
+ WriteFile.WriteLines(accountInfo, accountFilename);
+ }
+
+ public static void Withdraw(in string userLogin, string accountStoragePath)
+ {
+ var accountFilename = GetAccountFile(userLogin, accountStoragePath);
+
+ var accountInfo = GetAccountSummary(accountFilename);
+ var totalLine = accountInfo[4].Split(": $");
+
+ Console.Write("Please enter the amount you want to withdraw: ");
+ var withdrawAmount = int.Parse(Console.ReadLine());
+ var currentTotal = int.Parse(totalLine[1]);
+ var finalTotalAmount = currentTotal - withdrawAmount;
+
+ while (withdrawAmount is string && withdrawAmount < 0)
+ {
+ Console.WriteLine("Your withdraw amount should be greater than Zero");
+ withdrawAmount = int.Parse(Console.ReadLine());
+ finalTotalAmount = currentTotal - withdrawAmount;
+ }
+
+ if (finalTotalAmount < 0)
+ {
+ Console.WriteLine("Insufficient balance.");
+ }
+ else
+ {
+ var withdrawAmountLine = accountInfo[0].Split(": $");
+ var withdrawTimeLine = accountInfo[1].Split(": ");
+ withdrawAmountLine[1] = withdrawAmount.ToString();
+ withdrawTimeLine[1] = DateTime.Now.ToString();
+
+ accountInfo[0] = string.Join(": ", withdrawAmountLine);
+ accountInfo[1] = string.Join(": ", withdrawTimeLine);
+
+ totalLine[1] = finalTotalAmount.ToString();
+ accountInfo[4] = string.Join(": $", totalLine);
+
+ Console.WriteLine("You've withdrawn ${0} from your account", withdrawAmount);
+
+ Console.WriteLine("\nYour have ${0} left in your account", finalTotalAmount);
+
+ WriteFile.WriteLines(accountInfo, accountFilename);
+ }
+ }
+
+ public static void ViewAccountBalance(in string userLogin, string accountStoragePath)
+ {
+ var accountFilename = GetAccountFile(userLogin, accountStoragePath);
+
+ var accountBalance = GetAccountSummary(accountFilename)[4];
+
+ Console.WriteLine("Your account balance is {0}", accountBalance);
+ }
+
+ public static void ViewAccountSummary(in string userLogin, string accountStoragePath)
+ {
+ var accountFilename = GetAccountFile(userLogin, accountStoragePath);
+
+ var accountInfo = GetAccountSummary(accountFilename);
+
+ foreach (var info in accountInfo) Console.WriteLine(info);
+ }
+}
\ No newline at end of file
diff --git a/BankingSoftware/Modules/AuthModule.cs b/BankingSoftware/Modules/AuthModule.cs
new file mode 100644
index 0000000..46193c3
--- /dev/null
+++ b/BankingSoftware/Modules/AuthModule.cs
@@ -0,0 +1,42 @@
+using System;
+using System.IO;
+using BankingSoftware.Common;
+
+namespace BankingSoftware.Modules;
+
+// Module to handle authentication
+public static class AuthModule
+{
+ public static string SignIn(string userStoragePath)
+ {
+ Console.Write("Please enter your username: ");
+ var username = Console.ReadLine();
+
+ Console.Write("Please enter your password: ");
+ var password = Console.ReadLine();
+ var hashedPassword = new Hash().HashValues(password.Split());
+
+ var userLogin = new Hash().HashValues(new[] { username, hashedPassword });
+ var hashedUsername = new Hash().HashValues(username.Split());
+ var userExist = File.Exists(userStoragePath + hashedUsername + ".txt");
+
+ while (!userExist)
+ {
+ Console.WriteLine("Incorrect username or password, please try again");
+ Console.Write("Please enter your username: ");
+ username = Console.ReadLine();
+
+ Console.Write("Please enter your password: ");
+ password = Console.ReadLine();
+ hashedPassword = new Hash().HashValues(password.Split());
+
+ userLogin = new Hash().HashValues(new[] { username, hashedPassword });
+ hashedUsername = new Hash().HashValues(username.Split());
+ userExist = File.Exists(userStoragePath + hashedUsername + ".txt");
+ }
+
+ Console.WriteLine("Welcome {0}", username);
+
+ return userLogin;
+ }
+}
\ No newline at end of file
diff --git a/BankingSoftware/Modules/SignupModule.cs b/BankingSoftware/Modules/SignupModule.cs
new file mode 100644
index 0000000..695f0d2
--- /dev/null
+++ b/BankingSoftware/Modules/SignupModule.cs
@@ -0,0 +1,99 @@
+using System;
+using BankingSoftware.Common;
+
+namespace BankingSoftware.Modules;
+
+// Module to handle signin and creation of account
+public class SignupModule
+{
+ //const string StoragePath =
+
+ public static string Signup(string userStoragePath)
+ {
+ string[] userKeys =
+ {
+ "First Name: ", "Last Name: ", "Email: ", "Username: ", "Age: ", "Phone Number: ",
+ "HashedPassword: "
+ };
+ var userValues = new string[userKeys.Length + 1];
+
+ Console.WriteLine("Please answer the questions below to signup...");
+ AddSymbol.AddBreakLine(symbol: " ");
+
+ Console.Write("Enter your first name: ");
+ var firstName = Console.ReadLine();
+ VerifyInput.VerifyForStringOnly(firstName);
+ userValues[0] = userKeys[0] + firstName;
+ AddSymbol.AddBreakLine();
+
+ Console.Write("Enter your last name: ");
+ var lastName = Console.ReadLine();
+ VerifyInput.VerifyForStringOnly(lastName);
+ userValues[1] = userKeys[1] + lastName;
+ AddSymbol.AddBreakLine();
+
+ Console.Write("Enter your email: ");
+ var email = Console.ReadLine();
+ email = VerifyInput.VerifyEmail(email);
+ userValues[2] = userKeys[2] + email;
+ AddSymbol.AddBreakLine();
+
+ Console.Write("Enter your username: ");
+ var username = Console.ReadLine();
+ username = VerifyInput.VerifyForStringAndNumber(username);
+ userValues[3] = userKeys[3] + username;
+ AddSymbol.AddBreakLine();
+
+ Console.Write("Enter your age: ");
+ var age = int.Parse(Console.ReadLine());
+ age = VerifyInput.VerifyNumber(age);
+ while (age < 18 || age > 100)
+ {
+ Console.WriteLine("Age should be between 18 and 100");
+ Console.Write("Input new value");
+ Console.ReadLine();
+
+ age = int.Parse(Console.ReadLine());
+ VerifyInput.VerifyNumber(age);
+ }
+
+ userValues[4] = userKeys[4] + age;
+ AddSymbol.AddBreakLine();
+
+ Console.Write("Enter your phone number: ");
+ var phoneNumber = Console.ReadLine();
+ phoneNumber = VerifyInput.VerifyPhoneNumber(phoneNumber);
+ userValues[5] = userKeys[5] + phoneNumber;
+ AddSymbol.AddBreakLine();
+
+ Console.Write("Enter your password: ");
+ var password = Console.ReadLine();
+ password = VerifyInput.VerifyPassword(password);
+ var hashedPassword = new Hash().HashValues(password.Split());
+ userValues[6] = userKeys[6] + hashedPassword;
+ AddSymbol.AddBreakLines(lineLength: 2);
+
+ var hashedUsername = new Hash().HashValues(username.Split());
+ userStoragePath += "\\" + hashedUsername + ".txt";
+ WriteFile.WriteLines(userValues, userStoragePath);
+
+ var credentials = new Hash().HashValues(new[] { username, hashedPassword });
+
+ return credentials;
+ }
+
+ public static void CreateAccount(in string userLogin, string accountStoragePath)
+ {
+ string[] accountValues =
+ {
+ "Last withdrawal amount: $0", "Last withdrawal time: ", "Last deposit amount: $0",
+ "Last deposit time: ", "Total: $0"
+ };
+
+ var charFileName = userLogin.Split();
+ var hashedFileName = new Hash().HashValues(charFileName);
+
+ var accountFilename = accountStoragePath + hashedFileName + ".txt";
+ WriteFile.WriteLines(accountValues, accountFilename);
+ }
+}
\ No newline at end of file
diff --git a/BankingSoftware/Program.cs b/BankingSoftware/Program.cs
new file mode 100644
index 0000000..41a5e87
--- /dev/null
+++ b/BankingSoftware/Program.cs
@@ -0,0 +1,110 @@
+using System;
+using BankingSoftware.Common;
+using BankingSoftware.Modules;
+
+namespace BankingSoftware;
+
+internal class Program
+{
+ private const string userStoragePath =
+ @"C:\Users\rexxr\Documents\projectss\CodeBenders\BankingSoftware\Storage\Users\";
+
+ private const string accountStoragePath =
+ @"C:\Users\rexxr\Documents\projectss\CodeBenders\BankingSoftware\Storage\Accounts\";
+
+ public static void Auth(out string userLogin)
+ {
+ userLogin = "";
+ Console.WriteLine("\nWelcome to our console banking app");
+ AddSymbol.AddBreakLines(symbol: "*");
+
+ Console.WriteLine("Please login or signup. \n");
+ Console.WriteLine("Press 1 to login or 2 to signup");
+ Console.Write(">>>>> ");
+ var loginSignup = int.Parse(Console.ReadLine());
+ Console.WriteLine("\n");
+
+ while (loginSignup is string || loginSignup > 2)
+ {
+ Console.WriteLine("Input the correct value...");
+ Console.Write(">>>>> ");
+ loginSignup = int.Parse(Console.ReadLine());
+ }
+
+ switch (loginSignup)
+ {
+ case 1:
+ userLogin = AuthModule.SignIn(userStoragePath);
+ break;
+ case 2:
+ userLogin = SignupModule.Signup(userStoragePath);
+ SignupModule.CreateAccount(userLogin, accountStoragePath);
+ break;
+ }
+
+ AddSymbol.AddBreakLine();
+ }
+
+ public static void Account(in string userLogin)
+ {
+ Console.WriteLine("What action do you want to perform");
+ Console.WriteLine("Press 1 to VIEW ACCOUNT SUMMARY");
+ Console.WriteLine("Press 2 to VIEW BALANCE");
+ Console.WriteLine("Press 3 to DEPOSIT");
+ Console.WriteLine("Press 4 to WITHDRAW");
+
+ Console.Write(">>>>> ");
+ var action = int.Parse(Console.ReadLine());
+
+ switch (action)
+ {
+ case 1:
+ AccountModule.ViewAccountSummary(in userLogin, accountStoragePath);
+ break;
+ case 2:
+ AccountModule.ViewAccountBalance(in userLogin, accountStoragePath);
+ break;
+ case 3:
+ AccountModule.Deposit(in userLogin, accountStoragePath);
+ break;
+ case 4:
+ AccountModule.Withdraw(in userLogin, accountStoragePath);
+ break;
+ default:
+ Console.WriteLine("Input the correct value");
+ break;
+ }
+
+ Console.WriteLine("Do you want to perform another transaction");
+ Console.WriteLine("............ Press 1 to continue");
+ Console.WriteLine("............ Press 2 to quit");
+ Console.Write(">>>>> ");
+ var anotherAction = int.Parse(Console.ReadLine());
+
+ while (anotherAction > 2)
+ {
+ Console.WriteLine("Please input the correct value.");
+ Console.Write(">>>>> ");
+ anotherAction = int.Parse(Console.ReadLine());
+ }
+
+ if (anotherAction == 1)
+ {
+ Account(in userLogin);
+ }
+ else if(anotherAction == 2)
+ {
+ Console.WriteLine("Thank you for banking with us...");
+ Console.WriteLine("Goodbye.");
+ }
+
+ AddSymbol.AddBreakLines(symbol: "*");
+ }
+
+ private static void Main()
+ {
+ Auth(out var userLogin);
+
+ Account(in userLogin);
+ }
+}
\ No newline at end of file
diff --git a/BankingSoftware/Storage/Accounts/086591F1B63CEC976440DC2952AC4138.txt b/BankingSoftware/Storage/Accounts/086591F1B63CEC976440DC2952AC4138.txt
new file mode 100644
index 0000000..2f3dfb6
--- /dev/null
+++ b/BankingSoftware/Storage/Accounts/086591F1B63CEC976440DC2952AC4138.txt
@@ -0,0 +1,5 @@
+Last withdrawal amount: $0
+Last withdrawal time:
+Last deposit amount: $0
+Last deposit time:
+Total: $0
diff --git a/BankingSoftware/Storage/Accounts/0E84027714179DA860EC36BE3ABC2CCE.txt b/BankingSoftware/Storage/Accounts/0E84027714179DA860EC36BE3ABC2CCE.txt
new file mode 100644
index 0000000..3df7b09
--- /dev/null
+++ b/BankingSoftware/Storage/Accounts/0E84027714179DA860EC36BE3ABC2CCE.txt
@@ -0,0 +1,5 @@
+Last withdrawal amount: 20000
+Last withdrawal time: 3/3/2023 9:01:46 AM
+Last deposit amount: 60000
+Last deposit time: 3/3/2023 9:01:34 AM
+Total: $40000
diff --git a/BankingSoftware/Storage/Accounts/1BBCFB82D95A66785DABF16FE16DD079.txt b/BankingSoftware/Storage/Accounts/1BBCFB82D95A66785DABF16FE16DD079.txt
new file mode 100644
index 0000000..f7a2f63
--- /dev/null
+++ b/BankingSoftware/Storage/Accounts/1BBCFB82D95A66785DABF16FE16DD079.txt
@@ -0,0 +1,5 @@
+Last withdrawal amount: 50000
+Last withdrawal time: 3/3/2023 8:55:01 AM
+Last deposit amount: 80000
+Last deposit time: 3/3/2023 8:54:50 AM
+Total: $30000
diff --git a/BankingSoftware/Storage/Accounts/5FE5F846BD5D98FFF20F1384D0DFC710.txt b/BankingSoftware/Storage/Accounts/5FE5F846BD5D98FFF20F1384D0DFC710.txt
new file mode 100644
index 0000000..9f6b71d
--- /dev/null
+++ b/BankingSoftware/Storage/Accounts/5FE5F846BD5D98FFF20F1384D0DFC710.txt
@@ -0,0 +1,5 @@
+Last withdrawal amount: 20000
+Last withdrawal time: 3/1/2023 10:02:54 AM
+Last deposit amount: 90000
+Last deposit time: 3/1/2023 10:02:43 AM
+Total: $70000
diff --git a/BankingSoftware/Storage/Accounts/7445BDA0F89FE36B4BC831F76CB0533A.txt b/BankingSoftware/Storage/Accounts/7445BDA0F89FE36B4BC831F76CB0533A.txt
new file mode 100644
index 0000000..2f3dfb6
--- /dev/null
+++ b/BankingSoftware/Storage/Accounts/7445BDA0F89FE36B4BC831F76CB0533A.txt
@@ -0,0 +1,5 @@
+Last withdrawal amount: $0
+Last withdrawal time:
+Last deposit amount: $0
+Last deposit time:
+Total: $0
diff --git a/BankingSoftware/Storage/Accounts/A91CCAE139380AA1368393C8FC2953C1.txt b/BankingSoftware/Storage/Accounts/A91CCAE139380AA1368393C8FC2953C1.txt
new file mode 100644
index 0000000..15c2903
--- /dev/null
+++ b/BankingSoftware/Storage/Accounts/A91CCAE139380AA1368393C8FC2953C1.txt
@@ -0,0 +1,5 @@
+Last withdrawal amount: 20000
+Last withdrawal time: 3/3/2023 8:47:41 AM
+Last deposit amount: 80000
+Last deposit time: 3/3/2023 8:47:30 AM
+Total: $60000
diff --git a/BankingSoftware/Storage/Accounts/B56FD7BF4AB39D813239C006C4FF5813.txt b/BankingSoftware/Storage/Accounts/B56FD7BF4AB39D813239C006C4FF5813.txt
new file mode 100644
index 0000000..4c63b6b
--- /dev/null
+++ b/BankingSoftware/Storage/Accounts/B56FD7BF4AB39D813239C006C4FF5813.txt
@@ -0,0 +1,5 @@
+Last withdrawal amount: 30000
+Last withdrawal time: 3/3/2023 9:09:01 AM
+Last deposit amount: $60000
+Last deposit time: 3/3/2023 9:08:49 AM
+Total: $30000
diff --git a/BankingSoftware/Storage/Users/2947CB1B92BB13C8F373E78C76FCA765.txt b/BankingSoftware/Storage/Users/2947CB1B92BB13C8F373E78C76FCA765.txt
new file mode 100644
index 0000000..8cb0584
--- /dev/null
+++ b/BankingSoftware/Storage/Users/2947CB1B92BB13C8F373E78C76FCA765.txt
@@ -0,0 +1,8 @@
+First Name: Ade
+Last Name: Bendel
+Email: ade@bendel.com
+Username: ade
+Age: 78
+Phone Number: 9834563247
+HashedPassword: D84BA9FED904447C1BE566032AF633CD
+
diff --git a/BankingSoftware/Storage/Users/6A3B12DA60987AF16E55F3FCEC2A6EED.txt b/BankingSoftware/Storage/Users/6A3B12DA60987AF16E55F3FCEC2A6EED.txt
new file mode 100644
index 0000000..a8c4c60
--- /dev/null
+++ b/BankingSoftware/Storage/Users/6A3B12DA60987AF16E55F3FCEC2A6EED.txt
@@ -0,0 +1,8 @@
+First Name: Kunle
+Last Name: Afod
+Email: kunle@afod.com
+Username: kunle
+Age: 67
+Phone Number: 8734652345
+HashedPassword: F1A3AB9E8E8AB970167FEEE7A731F3E9
+
diff --git a/BankingSoftware/Storage/Users/7A362B30780DD15E6D710EC44AA0E023.txt b/BankingSoftware/Storage/Users/7A362B30780DD15E6D710EC44AA0E023.txt
new file mode 100644
index 0000000..bc3adbb
--- /dev/null
+++ b/BankingSoftware/Storage/Users/7A362B30780DD15E6D710EC44AA0E023.txt
@@ -0,0 +1,8 @@
+First Name: Usher
+Last Name: Ray
+Email: usher@ray.com
+Username: usher
+Age: 87
+Phone Number: 8975346523
+HashedPassword: BA4AFECD23BE0B3121EA708831022CEF
+
diff --git a/BankingSoftware/Storage/Users/C19291F87C625505E0CB012E128C3E33.txt b/BankingSoftware/Storage/Users/C19291F87C625505E0CB012E128C3E33.txt
new file mode 100644
index 0000000..738de81
--- /dev/null
+++ b/BankingSoftware/Storage/Users/C19291F87C625505E0CB012E128C3E33.txt
@@ -0,0 +1,8 @@
+First Name: Hal
+Last Name: Finley
+Email: hal@finley.com
+Username: hal
+Age: 87
+Phone Number: 9874523876
+HashedPassword: 8E3F0695D2604D26F5B64C23C485E3BF
+
diff --git a/BankingSoftware/Storage/Users/FA2111BDFC79125C0CF1D96B53B88FEB.txt b/BankingSoftware/Storage/Users/FA2111BDFC79125C0CF1D96B53B88FEB.txt
new file mode 100644
index 0000000..7ebdd13
--- /dev/null
+++ b/BankingSoftware/Storage/Users/FA2111BDFC79125C0CF1D96B53B88FEB.txt
@@ -0,0 +1,8 @@
+First Name: John
+Last Name: Camark
+Email: john@camark.com
+Username: john
+Age: 67
+Phone Number: 9834562346
+HashedPassword: E813ECD4F0196D88949044C1FA1B568D
+
diff --git a/BankingSoftware/Storage/Users/FE6FC17D485932CC797BB376BB501D44.txt b/BankingSoftware/Storage/Users/FE6FC17D485932CC797BB376BB501D44.txt
new file mode 100644
index 0000000..d548215
--- /dev/null
+++ b/BankingSoftware/Storage/Users/FE6FC17D485932CC797BB376BB501D44.txt
@@ -0,0 +1,8 @@
+First Name: Hailey
+Last Name: Bieber
+Email: hailey@bieber.com
+Username: hailey
+Age: 78
+Phone Number: 8777345623
+HashedPassword: 588BE3DDB98C4DB0FB1E484105399596
+
diff --git a/CodeBenders.sln b/CodeBenders.sln
new file mode 100644
index 0000000..c0017fd
--- /dev/null
+++ b/CodeBenders.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BankingSoftware", "BankingSoftware\BankingSoftware.csproj", "{335BF223-9311-4DF9-A534-4B3831D2279E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibraryManagementApp", "LibraryManagementApp\LibraryManagementApp.csproj", "{787924DD-178E-4142-BAA5-3E68A646B481}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {335BF223-9311-4DF9-A534-4B3831D2279E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {335BF223-9311-4DF9-A534-4B3831D2279E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {335BF223-9311-4DF9-A534-4B3831D2279E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {335BF223-9311-4DF9-A534-4B3831D2279E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {787924DD-178E-4142-BAA5-3E68A646B481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {787924DD-178E-4142-BAA5-3E68A646B481}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {787924DD-178E-4142-BAA5-3E68A646B481}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {787924DD-178E-4142-BAA5-3E68A646B481}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/LibraryManagementApp/ApiResponse/ApiResult.cs b/LibraryManagementApp/ApiResponse/ApiResult.cs
new file mode 100644
index 0000000..e91825b
--- /dev/null
+++ b/LibraryManagementApp/ApiResponse/ApiResult.cs
@@ -0,0 +1,152 @@
+using System.Linq.Dynamic.Core;
+using System.Reflection;
+using EFCore.BulkExtensions;
+using Microsoft.EntityFrameworkCore;
+
+namespace LibraryManagementApp.ApiResponse;
+
+public class ApiResult
+{
+ ///
+ /// Private constructor called by the CreateAsync method.
+ ///
+ private ApiResult(
+ List data,
+ int count,
+ int pageIndex,
+ int pageSize,
+ string? filterQuery)
+ {
+ Data = data;
+ PageIndex = pageIndex;
+ PageSize = pageSize;
+ TotalCount = count;
+ TotalPages = (int)Math.Ceiling(count / (double)pageSize);
+ FilterQuery = filterQuery;
+ }
+
+ #region Methods
+
+ ///
+ /// Pages, sorts and/or filters a IQueryable source.
+ ///
+ /// An IQueryable source of generic
+ /// type
+ /// Zero-based current page index
+ /// (0 = first page)
+ /// The actual size of
+ /// each page
+ /// The filtering query (value to
+ /// lookup)
+ ///
+ /// A object containing the IQueryable paged/sorted/filtered
+ /// result
+ /// and all the relevant paging/sorting/filtering navigation
+ /// info.
+ ///
+ public static async Task> CreateAsync(
+ IQueryable source,
+ int pageIndex,
+ int pageSize,
+ string? filterQuery = null)
+ {
+ if (!string.IsNullOrEmpty(filterQuery))
+ {
+ source = source.Where(filterQuery);
+ }
+
+ var count = await source.CountAsync();
+
+ source = source
+ .Skip(pageIndex * pageSize)
+ .Take(pageSize);
+
+#if DEBUG
+ // retrieve the SQL query (for debug purposes)
+ var sql = source.ToParametrizedSql();
+#endif
+
+ var data = await source.ToListAsync();
+
+ return new ApiResult(
+ data,
+ count,
+ pageIndex,
+ pageSize,
+ filterQuery);
+ }
+
+ ///
+ /// Checks if the given property name exists
+ /// to protect against SQL injection attacks
+ ///
+ public static bool IsValidProperty(
+ string propertyName,
+ bool throwExceptionIfNotFound = true)
+ {
+ var prop = typeof(T).GetProperty(
+ propertyName,
+ BindingFlags.IgnoreCase |
+ BindingFlags.Public |
+ BindingFlags.Static |
+ BindingFlags.Instance);
+ if (prop == null && throwExceptionIfNotFound)
+ throw new NotSupportedException($"ERROR: Property '{propertyName}' does not exist.");
+ return prop != null;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// IQueryable data result to return.
+ ///
+ public List Data { get; private set; }
+
+ ///
+ /// Zero-based index of current page.
+ ///
+ public int PageIndex { get; private set; }
+
+ ///
+ /// Number of items contained in each page.
+ ///
+ public int PageSize { get; private set; }
+
+ ///
+ /// Total items count
+ ///
+ public int TotalCount { get; private set; }
+
+ ///
+ /// Total pages count
+ ///
+ public int TotalPages { get; private set; }
+
+ ///
+ /// TRUE if the current page has a previous page,
+ /// FALSE otherwise.
+ ///
+ public bool HasPreviousPage
+ {
+ get { return (PageIndex > 0); }
+ }
+
+ ///
+ /// TRUE if the current page has a next page, FALSE otherwise.
+ ///
+ public bool HasNextPage
+ {
+ get { return ((PageIndex + 1) < TotalPages); }
+ }
+
+
+ ///
+ /// Filter Query string
+ /// (to be used within the given FilterColumn)
+ ///
+ public string? FilterQuery { get; set; }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Contracts/RepositoryContracts/IAuthorRepository.cs b/LibraryManagementApp/Contracts/RepositoryContracts/IAuthorRepository.cs
new file mode 100644
index 0000000..2253c59
--- /dev/null
+++ b/LibraryManagementApp/Contracts/RepositoryContracts/IAuthorRepository.cs
@@ -0,0 +1,8 @@
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Contracts.RepositoryContracts;
+
+public interface IAuthorRepository: IRepositoryBase
+{
+
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Contracts/RepositoryContracts/IBookRepository.cs b/LibraryManagementApp/Contracts/RepositoryContracts/IBookRepository.cs
new file mode 100644
index 0000000..e6bac7d
--- /dev/null
+++ b/LibraryManagementApp/Contracts/RepositoryContracts/IBookRepository.cs
@@ -0,0 +1,8 @@
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Contracts.RepositoryContracts;
+
+public interface IBookRepository: IRepositoryBase
+{
+
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Contracts/RepositoryContracts/ICategoryRepository.cs b/LibraryManagementApp/Contracts/RepositoryContracts/ICategoryRepository.cs
new file mode 100644
index 0000000..9575d32
--- /dev/null
+++ b/LibraryManagementApp/Contracts/RepositoryContracts/ICategoryRepository.cs
@@ -0,0 +1,8 @@
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Contracts.RepositoryContracts;
+
+public interface ICategoryRepository: IRepositoryBase
+{
+
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Contracts/RepositoryContracts/IPublisherRepository.cs b/LibraryManagementApp/Contracts/RepositoryContracts/IPublisherRepository.cs
new file mode 100644
index 0000000..b7aa2f2
--- /dev/null
+++ b/LibraryManagementApp/Contracts/RepositoryContracts/IPublisherRepository.cs
@@ -0,0 +1,8 @@
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Contracts.RepositoryContracts;
+
+public interface IPublisherRepository: IRepositoryBase
+{
+
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Contracts/RepositoryContracts/IRepositoryBase.cs b/LibraryManagementApp/Contracts/RepositoryContracts/IRepositoryBase.cs
new file mode 100644
index 0000000..032d48e
--- /dev/null
+++ b/LibraryManagementApp/Contracts/RepositoryContracts/IRepositoryBase.cs
@@ -0,0 +1,15 @@
+using System.Linq.Expressions;
+
+namespace LibraryManagementApp.Contracts.RepositoryContracts;
+
+public interface IRepositoryBase where T: class
+{
+ T FindById(Guid id);
+ IQueryable FindByCondition(Expression> expression, bool trackChanges);
+ IQueryable FindAll(bool trackChanges);
+ void Add(T entity);
+ void AddRange(IEnumerable entities);
+ void Update(T entity);
+ void Remove(T entity);
+ void RemoveRange(IEnumerable entities);
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Contracts/RepositoryContracts/IUnitOfWork.cs b/LibraryManagementApp/Contracts/RepositoryContracts/IUnitOfWork.cs
new file mode 100644
index 0000000..49bc705
--- /dev/null
+++ b/LibraryManagementApp/Contracts/RepositoryContracts/IUnitOfWork.cs
@@ -0,0 +1,11 @@
+namespace LibraryManagementApp.Contracts.RepositoryContracts;
+
+public interface IUnitOfWork: IDisposable
+{
+ IAuthorRepository Authors { get; }
+ IBookRepository Books { get; }
+ IPublisherRepository Publishers { get; }
+ ICategoryRepository Categories { get; }
+
+ int Complete();
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Controllers/AuthorController.cs b/LibraryManagementApp/Controllers/AuthorController.cs
new file mode 100644
index 0000000..b39220c
--- /dev/null
+++ b/LibraryManagementApp/Controllers/AuthorController.cs
@@ -0,0 +1,122 @@
+using LibraryManagementApp.ApiResponse;
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Domain;
+using LibraryManagementApp.Dtos;
+using LibraryManagementApp.Exceptions;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LibraryManagementApp.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class AuthorController: ControllerBase
+{
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ILogger _logger;
+
+ public AuthorController(ILogger logger, IUnitOfWork unitOfWork)
+ {
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ }
+
+ [HttpGet]
+ public async Task>> GetAuthors(
+ bool trackChanges,
+ int pageIndex = 0,
+ int pageSize = 10,
+ string? filterQuery = null)
+ {
+ var authors = await ApiResult.CreateAsync(
+ _unitOfWork.Authors.FindAll(trackChanges).Select(
+ a => new AuthorDto()
+ {
+ Id = a.Id,
+ FirstName = a.FirstName,
+ LastName = a.LastName,
+ PublisherName = a.Publisher.Name,
+ AvatarUrl = a.AvatarUrl
+ }),
+ pageIndex,
+ pageSize,
+ filterQuery);
+
+ return authors;
+ }
+
+ [HttpGet("{id}")]
+ public async Task> GetAuthorById(Guid id, bool trackChanges)
+ {
+ var entity = await _unitOfWork.Authors
+ .FindByCondition(a => a.Id.Equals(id), trackChanges)
+ .Select(a => new AuthorDto()
+ {
+ Id = a.Id,
+ FirstName = a.FirstName,
+ LastName = a.LastName,
+ PublisherName = a.Publisher.Name,
+ AvatarUrl = a.AvatarUrl
+ })
+ .FirstOrDefaultAsync();
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ return entity;
+ }
+
+ [HttpPost]
+ public ActionResult CreateAuthor(AuthorDto author)
+ {
+ var entity = new Author
+ {
+ FirstName = author.FirstName,
+ LastName = author.LastName,
+ AvatarUrl = author.AvatarUrl,
+ PublisherId = author.PublisherId
+ };
+
+ _unitOfWork.Authors.Add(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+
+ [HttpPut("{id:guid}")]
+ public ActionResult UpdateAuthor(AuthorDto author, Guid id)
+ {
+ var entity = _unitOfWork.Authors.FindById(id);
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ entity.FirstName = author.FirstName;
+ entity.LastName = author.LastName;
+ entity.AvatarUrl = author.AvatarUrl;
+ entity.PublisherId = author.PublisherId;
+
+
+ _unitOfWork.Authors.Update(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+
+ [HttpDelete("{id:guid}")]
+ public ActionResult DeleteAuthor(Guid id)
+ {
+ var entity = _unitOfWork.Authors.FindById(id);
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ _unitOfWork.Authors.Remove(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Controllers/BookController.cs b/LibraryManagementApp/Controllers/BookController.cs
new file mode 100644
index 0000000..fb3ba4b
--- /dev/null
+++ b/LibraryManagementApp/Controllers/BookController.cs
@@ -0,0 +1,134 @@
+using LibraryManagementApp.ApiResponse;
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Domain;
+using LibraryManagementApp.Dtos;
+using LibraryManagementApp.Exceptions;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LibraryManagementApp.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class BookController : ControllerBase
+{
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ILogger _logger;
+
+ public BookController(ILogger logger, IUnitOfWork unitOfWork)
+ {
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ }
+
+ [HttpGet]
+ public async Task>> GetBooks(
+ bool trackChanges,
+ int pageIndex = 0,
+ int pageSize = 10,
+ string? filterQuery = null)
+ {
+ var books = await ApiResult.CreateAsync(
+ _unitOfWork.Books.FindAll(trackChanges).Select(
+ b => new BookDto()
+ {
+ Id = b.Id,
+ Title = b.Title,
+ CopyrightNotice = b.CopyrightNotice,
+ ISBN = b.ISBN,
+ AuthorName = b.Author.FirstName,
+ PageLength = b.PageLength,
+ Language = b.Language,
+ ReleaseYear = b.ReleaseYear
+ }),
+ pageIndex,
+ pageSize,
+ filterQuery);
+
+ return books;
+ }
+
+ [HttpGet("{id}")]
+ public async Task> GetBookById(Guid id, bool trackChanges)
+ {
+ var entity = await _unitOfWork.Books
+ .FindByCondition(a => a.Id.Equals(id), trackChanges)
+ .Select(b => new BookDto()
+ {
+ Id = b.Id,
+ Title = b.Title,
+ CopyrightNotice = b.CopyrightNotice,
+ ISBN = b.ISBN,
+ AuthorName = b.Author.FirstName,
+ PageLength = b.PageLength,
+ Language = b.Language,
+ ReleaseYear = b.ReleaseYear
+ })
+ .FirstOrDefaultAsync();
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ return entity;
+ }
+
+ [HttpPost]
+ public ActionResult CreateBook(BookDto book)
+ {
+ var author = _unitOfWork.Authors.FindById(book.AuthourId);
+ var entity = new Book
+ {
+ Title = book.Title,
+ CopyrightNotice = book.CopyrightNotice,
+ ISBN = book.ISBN,
+ AuthorId = author.Id,
+ PageLength = book.PageLength,
+ Language = book.Language,
+ ReleaseYear = book.ReleaseYear
+ };
+
+ _unitOfWork.Books.Add(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+
+ [HttpPut("{id:guid}")]
+ public ActionResult UpdateBook(BookDto book, Guid id)
+ {
+ var entity = _unitOfWork.Books.FindById(id);
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ entity.Title = book.Title;
+ entity.CopyrightNotice = book.CopyrightNotice;
+ entity.ISBN = book.ISBN;
+ entity.PageLength = book.PageLength;
+ entity.Language = book.Language;
+ entity.ReleaseYear = book.ReleaseYear;
+
+
+ _unitOfWork.Books.Update(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+
+ [HttpDelete("{id:guid}")]
+ public ActionResult DeleteBook(Guid id)
+ {
+ var entity = _unitOfWork.Books.FindById(id);
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ _unitOfWork.Books.Remove(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Controllers/CategoryController.cs b/LibraryManagementApp/Controllers/CategoryController.cs
new file mode 100644
index 0000000..0392adf
--- /dev/null
+++ b/LibraryManagementApp/Controllers/CategoryController.cs
@@ -0,0 +1,113 @@
+using LibraryManagementApp.ApiResponse;
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Domain;
+using LibraryManagementApp.Dtos;
+using LibraryManagementApp.Exceptions;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LibraryManagementApp.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class CategoryController: ControllerBase
+{
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ILogger _logger;
+
+ public CategoryController(ILogger logger, IUnitOfWork unitOfWork)
+ {
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ }
+
+ [HttpGet]
+ public async Task>> GetPublishers(
+ bool trackChanges,
+ int pageIndex = 0,
+ int pageSize = 10,
+ string? filterQuery = null)
+ {
+ var categories = await ApiResult.CreateAsync(
+ _unitOfWork.Categories.FindAll(trackChanges).Select(
+ c => new CategoryDto()
+ {
+ Id = c.Id,
+ Name = c.Name,
+ Description = c.Description
+ }),
+ pageIndex,
+ pageSize,
+ filterQuery);
+
+ return categories;
+ }
+
+ [HttpGet("{id}")]
+ public async Task> GetCategoryById(Guid id, bool trackChanges)
+ {
+ var entity = await _unitOfWork.Categories
+ .FindByCondition(a => a.Id.Equals(id), trackChanges)
+ .Select(c => new CategoryDto()
+ {
+ Id = c.Id,
+ Name = c.Name,
+ Description = c.Description
+ })
+ .FirstOrDefaultAsync();
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ return entity;
+ }
+
+ [HttpPost]
+ public ActionResult CreateCategory(CategoryDto category)
+ {
+ var entity = new Category
+ {
+ Name = category.Name,
+ Description = category.Description,
+ };
+
+ _unitOfWork.Categories.Add(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+
+ [HttpPut("{id:guid}")]
+ public ActionResult UpdateCategory(CategoryDto category, Guid id)
+ {
+ var entity = _unitOfWork.Categories.FindById(id);
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ entity.Name = category.Name;
+ entity.Description = category.Description;
+
+ _unitOfWork.Categories.Update(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+
+ [HttpDelete("{id:guid}")]
+ public ActionResult DeleteCategory(Guid id)
+ {
+ var entity = _unitOfWork.Categories.FindById(id);
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ _unitOfWork.Categories.Remove(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Controllers/PublisherController.cs b/LibraryManagementApp/Controllers/PublisherController.cs
new file mode 100644
index 0000000..16c6f78
--- /dev/null
+++ b/LibraryManagementApp/Controllers/PublisherController.cs
@@ -0,0 +1,114 @@
+using LibraryManagementApp.ApiResponse;
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Domain;
+using LibraryManagementApp.Dtos;
+using LibraryManagementApp.Exceptions;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace LibraryManagementApp.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class PublisherController : ControllerBase
+{
+ private readonly IUnitOfWork _unitOfWork;
+ private readonly ILogger _logger;
+
+ public PublisherController(ILogger logger, IUnitOfWork unitOfWork)
+ {
+ _logger = logger;
+ _unitOfWork = unitOfWork;
+ }
+
+ [HttpGet]
+ public async Task>> GetPublishers(
+ bool trackChanges,
+ int pageIndex = 0,
+ int pageSize = 10,
+ string? filterQuery = null)
+ {
+ var publishers = await ApiResult.CreateAsync(
+ _unitOfWork.Publishers.FindAll(trackChanges).Select(
+ p => new PublisherDto()
+ {
+ Id = p.Id,
+ Name = p.Name,
+ LogoUrl = p.LogoUrl,
+ }),
+ pageIndex,
+ pageSize,
+ filterQuery);
+
+ return publishers;
+ }
+
+ [HttpGet("{id}")]
+ public async Task> GetPublisherById(Guid id, bool trackChanges)
+ {
+ var entity = await _unitOfWork.Publishers
+ .FindByCondition(p => p.Id.Equals(id), trackChanges)
+ .Select(p => new PublisherDto
+ {
+ Name = p.Name,
+ LogoUrl = p.LogoUrl,
+ TotalBooks = p.TotBooks,
+ TotalAuthors = p.TotAuthors
+ })
+ .FirstOrDefaultAsync();
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ return entity;
+ }
+
+ [HttpPost]
+ public ActionResult CreatePublisher(PublisherDto publisher)
+ {
+ var entity = new Publisher
+ {
+ Name = publisher.Name,
+ LogoUrl = publisher.LogoUrl,
+ };
+
+ _unitOfWork.Publishers.Add(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+
+ [HttpPut("{id:guid}")]
+ public ActionResult UpdatePublisher(PublisherDto publisher, Guid id)
+ {
+ var entity = _unitOfWork.Publishers.FindById(id);
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ entity.Name = publisher.Name;
+ entity.LogoUrl = publisher.LogoUrl;
+
+ _unitOfWork.Publishers.Update(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+
+ [HttpDelete("{id:guid}")]
+ public ActionResult DeletePublisher(Guid id)
+ {
+ var entity = _unitOfWork.Publishers.FindById(id);
+
+ if (entity == null)
+ throw new NotFoundException();
+
+ _unitOfWork.Publishers.Remove(entity);
+ _unitOfWork.Complete();
+ _unitOfWork.Dispose();
+
+ return Ok();
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Domain/Author.cs b/LibraryManagementApp/Domain/Author.cs
new file mode 100644
index 0000000..39a2f54
--- /dev/null
+++ b/LibraryManagementApp/Domain/Author.cs
@@ -0,0 +1,40 @@
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace LibraryManagementApp.Domain
+{
+ public class Author: BaseEntity
+ {
+ public Author()
+ {
+
+ }
+
+ public Author(string firstName, string lastName, string avatarUrl, Guid publisherId)
+ {
+ FirstName = firstName;
+ LastName = lastName;
+ AvatarUrl = avatarUrl;
+ PublisherId = publisherId;
+ }
+
+ public string FirstName{get; set;}
+ public string LastName{get; set;}
+ public string AvatarUrl{get; set;}
+ public Guid PublisherId{get; set;}
+ public Publisher Publisher { get; set; }
+ public ICollection Books{get; set;}
+
+ [NotMapped]
+ public int TotBooks
+ {
+ get
+ {
+ return (Books != null)
+ ? Books.Count
+ : _TotBooks;
+ }
+ set => _TotBooks = value;
+ }
+ private int _TotBooks;
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Domain/BaseEntity.cs b/LibraryManagementApp/Domain/BaseEntity.cs
new file mode 100644
index 0000000..5f3c4ea
--- /dev/null
+++ b/LibraryManagementApp/Domain/BaseEntity.cs
@@ -0,0 +1,23 @@
+namespace LibraryManagementApp.Domain;
+
+public class BaseEntity
+{
+ public BaseEntity()
+ {
+
+ }
+
+ public BaseEntity(Guid id, DateTime createdOn)
+ {
+ Id = new Guid();
+ CreatedOn = createdOn;
+ }
+
+ public Guid Id { get; init; }
+ public DateTime CreatedOn { get; init; }
+ public Guid CreatedBy { get; init; }
+ public DateTime ModifiedOn { get; set; }
+ public Guid ModifiedBy { get; set; }
+ public DateTime DeletedOn { get; init; }
+ public Guid DeletedBy { get; init; }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Domain/Book.cs b/LibraryManagementApp/Domain/Book.cs
new file mode 100644
index 0000000..a480777
--- /dev/null
+++ b/LibraryManagementApp/Domain/Book.cs
@@ -0,0 +1,26 @@
+namespace LibraryManagementApp.Domain
+{
+ public class Book: BaseEntity
+ {
+ public Book()
+ {
+
+ }
+
+ public Book(string title, string copyrightNotice, Guid authorId)
+ {
+ Title = title;
+ CopyrightNotice = copyrightNotice;
+ AuthorId = authorId;
+ }
+
+ public string Title{get; set;}
+ public string CopyrightNotice{get; set;}
+ public int ISBN{get; set;}
+ public Guid AuthorId{get; set;}
+ public Author Author { get; set; }
+ public int PageLength{get; set;}
+ public Language Language{get; set;}
+ public DateOnly ReleaseYear{get; set;}
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Domain/Category.cs b/LibraryManagementApp/Domain/Category.cs
new file mode 100644
index 0000000..74672ae
--- /dev/null
+++ b/LibraryManagementApp/Domain/Category.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace LibraryManagementApp.Domain
+{
+ public class Category: BaseEntity
+ {
+ public Category()
+ {
+
+ }
+ public Category(string name, string description)
+ {
+ Name = name;
+ Description = description;
+ }
+
+ public string Name{get; set;}
+ public string Description{get; set;}
+ public ICollection Books{get; set;}
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Domain/Language.cs b/LibraryManagementApp/Domain/Language.cs
new file mode 100644
index 0000000..8ad94e9
--- /dev/null
+++ b/LibraryManagementApp/Domain/Language.cs
@@ -0,0 +1,11 @@
+namespace LibraryManagementApp.Domain
+{
+ public enum Language
+ {
+ English = 10,
+ Yoruba = 20,
+ Igbo = 30,
+ Hausa = 40,
+ French = 50,
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Domain/Publisher.cs b/LibraryManagementApp/Domain/Publisher.cs
new file mode 100644
index 0000000..48105de
--- /dev/null
+++ b/LibraryManagementApp/Domain/Publisher.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace LibraryManagementApp.Domain
+{
+ public class Publisher: BaseEntity
+ {
+ public Publisher()
+ {
+
+ }
+ public Publisher(string name, string logoUrl)
+ {
+ Name = name;
+ LogoUrl = logoUrl;
+ }
+
+ public string Name{get; set;}
+ public string LogoUrl{get; set;}
+ public ICollection Books { get; set; }
+ public ICollection Authors { get; set; }
+
+ [NotMapped]
+ public int TotBooks
+ {
+ get
+ {
+ return (Books != null)
+ ? Books.Count
+ : _TotBooks;
+ }
+ set => _TotBooks = value;
+ }
+ private int _TotBooks;
+
+ [NotMapped]
+ public int TotAuthors
+ {
+ get
+ {
+ return (Books != null)
+ ? Books.Count
+ : _TotBooks;
+ }
+ set => _TotBooks = value;
+ }
+ private int _TotAuthors;
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Dtos/AuthorDto.cs b/LibraryManagementApp/Dtos/AuthorDto.cs
new file mode 100644
index 0000000..981bbeb
--- /dev/null
+++ b/LibraryManagementApp/Dtos/AuthorDto.cs
@@ -0,0 +1,11 @@
+namespace LibraryManagementApp.Dtos;
+
+public record AuthorDto
+{
+ public Guid Id { get; init; }
+ public string FirstName{get; init;}
+ public string LastName{get; init;}
+ public string AvatarUrl{get; init;}
+ public string PublisherName{get; init;}
+ public Guid PublisherId {get; init;}
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Dtos/BookDto.cs b/LibraryManagementApp/Dtos/BookDto.cs
new file mode 100644
index 0000000..f4799a6
--- /dev/null
+++ b/LibraryManagementApp/Dtos/BookDto.cs
@@ -0,0 +1,16 @@
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Dtos;
+
+public record BookDto
+{
+ public Guid Id { get; init; }
+ public string Title{get; init;}
+ public string CopyrightNotice{get; init;}
+ public int ISBN{get; init;}
+ public string AuthorName { get; init; }
+ public Guid AuthourId { get; init; }
+ public int PageLength{get; init;}
+ public Language Language{get; init;}
+ public DateOnly ReleaseYear{get; init;}
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Dtos/CategoryDto.cs b/LibraryManagementApp/Dtos/CategoryDto.cs
new file mode 100644
index 0000000..fb156db
--- /dev/null
+++ b/LibraryManagementApp/Dtos/CategoryDto.cs
@@ -0,0 +1,8 @@
+namespace LibraryManagementApp.Dtos;
+
+public record CategoryDto
+{
+ public Guid Id { get; init; }
+ public string Name{get; set;}
+ public string Description{get; set;}
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Dtos/PublisherDto.cs b/LibraryManagementApp/Dtos/PublisherDto.cs
new file mode 100644
index 0000000..e701323
--- /dev/null
+++ b/LibraryManagementApp/Dtos/PublisherDto.cs
@@ -0,0 +1,10 @@
+namespace LibraryManagementApp.Dtos;
+
+public record PublisherDto
+{
+ public Guid Id { get; init; }
+ public string Name{get; init;}
+ public string LogoUrl{get; init;}
+ public int? TotalAuthors { get; init; }
+ public int? TotalBooks { get; init; }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Exceptions/NotFoundException.cs b/LibraryManagementApp/Exceptions/NotFoundException.cs
new file mode 100644
index 0000000..fcdabbe
--- /dev/null
+++ b/LibraryManagementApp/Exceptions/NotFoundException.cs
@@ -0,0 +1,24 @@
+namespace LibraryManagementApp.Exceptions;
+
+public class NotFoundException : Exception
+{
+ public NotFoundException()
+ : base()
+ {
+ }
+
+ public NotFoundException(string message)
+ : base(message)
+ {
+ }
+
+ public NotFoundException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ public NotFoundException(string name, object key)
+ : base($"Entity \"{name}\" ({key}) was not found.")
+ {
+ }
+}
diff --git a/LibraryManagementApp/LibraryManagementApp.csproj b/LibraryManagementApp/LibraryManagementApp.csproj
new file mode 100644
index 0000000..5e13825
--- /dev/null
+++ b/LibraryManagementApp/LibraryManagementApp.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
diff --git a/LibraryManagementApp/Migrations/20230309042120_Initial.Designer.cs b/LibraryManagementApp/Migrations/20230309042120_Initial.Designer.cs
new file mode 100644
index 0000000..bacd982
--- /dev/null
+++ b/LibraryManagementApp/Migrations/20230309042120_Initial.Designer.cs
@@ -0,0 +1,262 @@
+//
+using System;
+using LibraryManagementApp.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace LibraryManagementApp.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20230309042120_Initial")]
+ partial class Initial
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.3")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("BookCategory", b =>
+ {
+ b.Property("BooksId")
+ .HasColumnType("uuid");
+
+ b.Property("CategoryId")
+ .HasColumnType("uuid");
+
+ b.HasKey("BooksId", "CategoryId");
+
+ b.HasIndex("CategoryId");
+
+ b.ToTable("BookCategory");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Author", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AvatarUrl")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FirstName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ModifiedBy")
+ .HasColumnType("uuid");
+
+ b.Property("ModifiedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PublisherId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.ToTable("Authors");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Book", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AuthorId")
+ .HasColumnType("uuid");
+
+ b.Property("CopyrightNotice")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ISBN")
+ .HasColumnType("integer");
+
+ b.Property("Language")
+ .HasColumnType("integer");
+
+ b.Property("ModifiedBy")
+ .HasColumnType("uuid");
+
+ b.Property("ModifiedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PageLength")
+ .HasColumnType("integer");
+
+ b.Property("PublisherId")
+ .HasColumnType("uuid");
+
+ b.Property("ReleaseYear")
+ .HasColumnType("date");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AuthorId");
+
+ b.HasIndex("PublisherId");
+
+ b.ToTable("Books");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Category", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ModifiedBy")
+ .HasColumnType("uuid");
+
+ b.Property("ModifiedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Categories");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Publisher", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LogoUrl")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ModifiedBy")
+ .HasColumnType("uuid");
+
+ b.Property("ModifiedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Publishers");
+ });
+
+ modelBuilder.Entity("BookCategory", b =>
+ {
+ b.HasOne("LibraryManagementApp.Domain.Book", null)
+ .WithMany()
+ .HasForeignKey("BooksId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("LibraryManagementApp.Domain.Category", null)
+ .WithMany()
+ .HasForeignKey("CategoryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Book", b =>
+ {
+ b.HasOne("LibraryManagementApp.Domain.Author", "Author")
+ .WithMany("Books")
+ .HasForeignKey("AuthorId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("LibraryManagementApp.Domain.Publisher", null)
+ .WithMany("Books")
+ .HasForeignKey("PublisherId");
+
+ b.Navigation("Author");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Author", b =>
+ {
+ b.Navigation("Books");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Publisher", b =>
+ {
+ b.Navigation("Books");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/LibraryManagementApp/Migrations/20230309042120_Initial.cs b/LibraryManagementApp/Migrations/20230309042120_Initial.cs
new file mode 100644
index 0000000..af9489d
--- /dev/null
+++ b/LibraryManagementApp/Migrations/20230309042120_Initial.cs
@@ -0,0 +1,168 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace LibraryManagementApp.Migrations
+{
+ ///
+ public partial class Initial : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "Authors",
+ columns: table => new
+ {
+ Id = table.Column(type: "uuid", nullable: false),
+ FirstName = table.Column(type: "text", nullable: false),
+ LastName = table.Column(type: "text", nullable: false),
+ AvatarUrl = table.Column(type: "text", nullable: false),
+ PublisherId = table.Column(type: "uuid", nullable: false),
+ CreatedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatedBy = table.Column(type: "uuid", nullable: false),
+ ModifiedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ ModifiedBy = table.Column(type: "uuid", nullable: false),
+ DeletedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ DeletedBy = table.Column(type: "uuid", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Authors", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Categories",
+ columns: table => new
+ {
+ Id = table.Column(type: "uuid", nullable: false),
+ Name = table.Column(type: "text", nullable: false),
+ Description = table.Column(type: "text", nullable: false),
+ CreatedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatedBy = table.Column(type: "uuid", nullable: false),
+ ModifiedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ ModifiedBy = table.Column(type: "uuid", nullable: false),
+ DeletedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ DeletedBy = table.Column(type: "uuid", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Categories", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Publishers",
+ columns: table => new
+ {
+ Id = table.Column(type: "uuid", nullable: false),
+ Name = table.Column(type: "text", nullable: false),
+ LogoUrl = table.Column(type: "text", nullable: false),
+ CreatedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatedBy = table.Column(type: "uuid", nullable: false),
+ ModifiedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ ModifiedBy = table.Column(type: "uuid", nullable: false),
+ DeletedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ DeletedBy = table.Column(type: "uuid", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Publishers", x => x.Id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "Books",
+ columns: table => new
+ {
+ Id = table.Column(type: "uuid", nullable: false),
+ Title = table.Column(type: "text", nullable: false),
+ CopyrightNotice = table.Column(type: "text", nullable: false),
+ ISBN = table.Column(type: "integer", nullable: false),
+ AuthorId = table.Column(type: "uuid", nullable: false),
+ PageLength = table.Column(type: "integer", nullable: false),
+ Language = table.Column(type: "integer", nullable: false),
+ ReleaseYear = table.Column(type: "date", nullable: false),
+ PublisherId = table.Column(type: "uuid", nullable: true),
+ CreatedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ CreatedBy = table.Column(type: "uuid", nullable: false),
+ ModifiedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ ModifiedBy = table.Column(type: "uuid", nullable: false),
+ DeletedOn = table.Column(type: "timestamp with time zone", nullable: false),
+ DeletedBy = table.Column(type: "uuid", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_Books", x => x.Id);
+ table.ForeignKey(
+ name: "FK_Books_Authors_AuthorId",
+ column: x => x.AuthorId,
+ principalTable: "Authors",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_Books_Publishers_PublisherId",
+ column: x => x.PublisherId,
+ principalTable: "Publishers",
+ principalColumn: "Id");
+ });
+
+ migrationBuilder.CreateTable(
+ name: "BookCategory",
+ columns: table => new
+ {
+ BooksId = table.Column(type: "uuid", nullable: false),
+ CategoryId = table.Column(type: "uuid", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_BookCategory", x => new { x.BooksId, x.CategoryId });
+ table.ForeignKey(
+ name: "FK_BookCategory_Books_BooksId",
+ column: x => x.BooksId,
+ principalTable: "Books",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_BookCategory_Categories_CategoryId",
+ column: x => x.CategoryId,
+ principalTable: "Categories",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_BookCategory_CategoryId",
+ table: "BookCategory",
+ column: "CategoryId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Books_AuthorId",
+ table: "Books",
+ column: "AuthorId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_Books_PublisherId",
+ table: "Books",
+ column: "PublisherId");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "BookCategory");
+
+ migrationBuilder.DropTable(
+ name: "Books");
+
+ migrationBuilder.DropTable(
+ name: "Categories");
+
+ migrationBuilder.DropTable(
+ name: "Authors");
+
+ migrationBuilder.DropTable(
+ name: "Publishers");
+ }
+ }
+}
diff --git a/LibraryManagementApp/Migrations/ApplicationDbContextModelSnapshot.cs b/LibraryManagementApp/Migrations/ApplicationDbContextModelSnapshot.cs
new file mode 100644
index 0000000..d011046
--- /dev/null
+++ b/LibraryManagementApp/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -0,0 +1,259 @@
+//
+using System;
+using LibraryManagementApp.Persistence;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace LibraryManagementApp.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ partial class ApplicationDbContextModelSnapshot : ModelSnapshot
+ {
+ protected override void BuildModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.3")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("BookCategory", b =>
+ {
+ b.Property("BooksId")
+ .HasColumnType("uuid");
+
+ b.Property("CategoryId")
+ .HasColumnType("uuid");
+
+ b.HasKey("BooksId", "CategoryId");
+
+ b.HasIndex("CategoryId");
+
+ b.ToTable("BookCategory");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Author", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AvatarUrl")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("FirstName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ModifiedBy")
+ .HasColumnType("uuid");
+
+ b.Property("ModifiedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PublisherId")
+ .HasColumnType("uuid");
+
+ b.HasKey("Id");
+
+ b.ToTable("Authors");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Book", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("AuthorId")
+ .HasColumnType("uuid");
+
+ b.Property("CopyrightNotice")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("ISBN")
+ .HasColumnType("integer");
+
+ b.Property("Language")
+ .HasColumnType("integer");
+
+ b.Property("ModifiedBy")
+ .HasColumnType("uuid");
+
+ b.Property("ModifiedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("PageLength")
+ .HasColumnType("integer");
+
+ b.Property("PublisherId")
+ .HasColumnType("uuid");
+
+ b.Property("ReleaseYear")
+ .HasColumnType("date");
+
+ b.Property("Title")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AuthorId");
+
+ b.HasIndex("PublisherId");
+
+ b.ToTable("Books");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Category", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ModifiedBy")
+ .HasColumnType("uuid");
+
+ b.Property("ModifiedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Categories");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Publisher", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid");
+
+ b.Property("CreatedBy")
+ .HasColumnType("uuid");
+
+ b.Property("CreatedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DeletedBy")
+ .HasColumnType("uuid");
+
+ b.Property("DeletedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("LogoUrl")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ModifiedBy")
+ .HasColumnType("uuid");
+
+ b.Property("ModifiedOn")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("Publishers");
+ });
+
+ modelBuilder.Entity("BookCategory", b =>
+ {
+ b.HasOne("LibraryManagementApp.Domain.Book", null)
+ .WithMany()
+ .HasForeignKey("BooksId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("LibraryManagementApp.Domain.Category", null)
+ .WithMany()
+ .HasForeignKey("CategoryId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Book", b =>
+ {
+ b.HasOne("LibraryManagementApp.Domain.Author", "Author")
+ .WithMany("Books")
+ .HasForeignKey("AuthorId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("LibraryManagementApp.Domain.Publisher", null)
+ .WithMany("Books")
+ .HasForeignKey("PublisherId");
+
+ b.Navigation("Author");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Author", b =>
+ {
+ b.Navigation("Books");
+ });
+
+ modelBuilder.Entity("LibraryManagementApp.Domain.Publisher", b =>
+ {
+ b.Navigation("Books");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/LibraryManagementApp/Persistence/ApplicationDbContext.cs b/LibraryManagementApp/Persistence/ApplicationDbContext.cs
new file mode 100644
index 0000000..365bfea
--- /dev/null
+++ b/LibraryManagementApp/Persistence/ApplicationDbContext.cs
@@ -0,0 +1,26 @@
+using LibraryManagementApp.Domain;
+using LibraryManagementApp.Persistence.Configurations;
+using Microsoft.EntityFrameworkCore;
+
+namespace LibraryManagementApp.Persistence;
+
+public class ApplicationDbContext: DbContext
+{
+ public ApplicationDbContext(DbContextOptions options) : base(options)
+ {
+
+ }
+
+ public DbSet Authors => Set();
+ public DbSet Books => Set();
+ public DbSet Categories => Set();
+ public DbSet Publishers => Set();
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.ApplyConfiguration(new AuthorConfiguration());
+ modelBuilder.ApplyConfiguration(new BookConfiguration());
+ modelBuilder.ApplyConfiguration(new CategoryConfiguration());
+ modelBuilder.ApplyConfiguration(new PublisherConfiguration());
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Configurations/AuthorConfiguration.cs b/LibraryManagementApp/Persistence/Configurations/AuthorConfiguration.cs
new file mode 100644
index 0000000..daa1236
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Configurations/AuthorConfiguration.cs
@@ -0,0 +1,15 @@
+using LibraryManagementApp.Domain;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace LibraryManagementApp.Persistence.Configurations;
+
+public class AuthorConfiguration: IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.Property(a => a.Id)
+ .IsRequired();
+ builder.HasMany(a => a.Books);
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Configurations/BookConfiguration.cs b/LibraryManagementApp/Persistence/Configurations/BookConfiguration.cs
new file mode 100644
index 0000000..64d2bd8
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Configurations/BookConfiguration.cs
@@ -0,0 +1,17 @@
+using LibraryManagementApp.Domain;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace LibraryManagementApp.Persistence.Configurations;
+
+public class BookConfiguration: IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ /*builder.Property(b => b.Id)
+ .IsRequired();
+ builder.HasOne(b => b.Author)
+ .WithMany(a => a.Books)
+ .HasForeignKey(b => b.AuthorId);*/
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Configurations/CategoryConfiguration.cs b/LibraryManagementApp/Persistence/Configurations/CategoryConfiguration.cs
new file mode 100644
index 0000000..30cd565
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Configurations/CategoryConfiguration.cs
@@ -0,0 +1,16 @@
+using LibraryManagementApp.Domain;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace LibraryManagementApp.Persistence.Configurations;
+
+public class CategoryConfiguration : IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.Property(b => b.Id)
+ .IsRequired();
+ builder.HasMany(b => b.Books)
+ .WithMany();
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Configurations/PublisherConfiguration.cs b/LibraryManagementApp/Persistence/Configurations/PublisherConfiguration.cs
new file mode 100644
index 0000000..7a1223a
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Configurations/PublisherConfiguration.cs
@@ -0,0 +1,16 @@
+using LibraryManagementApp.Domain;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace LibraryManagementApp.Persistence.Configurations;
+
+public class PublisherConfiguration: IEntityTypeConfiguration
+{
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.Property(p => p.Id)
+ .IsRequired();
+ builder.HasMany(p => p.Books)
+ .WithOne();
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Repository/AuthorRepository.cs b/LibraryManagementApp/Persistence/Repository/AuthorRepository.cs
new file mode 100644
index 0000000..0884e18
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Repository/AuthorRepository.cs
@@ -0,0 +1,11 @@
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Persistence.Repository;
+
+public class AuthorRepository: RepositoryBase, IAuthorRepository
+{
+ public AuthorRepository(ApplicationDbContext context) : base(context)
+ {
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Repository/BookRepository.cs b/LibraryManagementApp/Persistence/Repository/BookRepository.cs
new file mode 100644
index 0000000..4e151c0
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Repository/BookRepository.cs
@@ -0,0 +1,11 @@
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Persistence.Repository;
+
+public class BookRepository: RepositoryBase, IBookRepository
+{
+ public BookRepository(ApplicationDbContext context) : base(context)
+ {
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Repository/CategoryRepository.cs b/LibraryManagementApp/Persistence/Repository/CategoryRepository.cs
new file mode 100644
index 0000000..7d5ed2b
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Repository/CategoryRepository.cs
@@ -0,0 +1,11 @@
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Persistence.Repository;
+
+public class CategoryRepository: RepositoryBase, ICategoryRepository
+{
+ public CategoryRepository(ApplicationDbContext context) : base(context)
+ {
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Repository/PublisherRepository.cs b/LibraryManagementApp/Persistence/Repository/PublisherRepository.cs
new file mode 100644
index 0000000..822a685
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Repository/PublisherRepository.cs
@@ -0,0 +1,11 @@
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Domain;
+
+namespace LibraryManagementApp.Persistence.Repository;
+
+public class PublisherRepository: RepositoryBase, IPublisherRepository
+{
+ public PublisherRepository(ApplicationDbContext context) : base(context)
+ {
+ }
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Repository/RepositoryBase.cs b/LibraryManagementApp/Persistence/Repository/RepositoryBase.cs
new file mode 100644
index 0000000..706136e
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Repository/RepositoryBase.cs
@@ -0,0 +1,45 @@
+using System.Linq.Expressions;
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using Microsoft.EntityFrameworkCore;
+
+namespace LibraryManagementApp.Persistence.Repository;
+
+public class RepositoryBase: IRepositoryBase where T: class
+{
+ protected readonly ApplicationDbContext _context;
+
+ public RepositoryBase(ApplicationDbContext context) =>
+ _context = context;
+
+ public T FindById(Guid id) =>
+ _context.Set().Find(id);
+
+ public IQueryable FindByCondition(Expression> expression, bool trackChanges) =>
+ !trackChanges ?
+ _context.Set()
+ .Where(expression)
+ .AsNoTracking() :
+ _context.Set()
+ .Where(expression);
+
+ public IQueryable FindAll(bool trackChanges) =>
+ !trackChanges ?
+ _context.Set()
+ .AsNoTracking() :
+ _context.Set();
+
+ public void Add(T entity) =>
+ _context.Set().Add(entity);
+
+ public void AddRange(IEnumerable entities) =>
+ _context.Set().AddRange(entities);
+
+ public void Update(T entity) =>
+ _context.Set().Update(entity);
+
+ public void Remove(T entity) =>
+ _context.Set().Remove(entity);
+
+ public void RemoveRange(IEnumerable entities) =>
+ _context.Set().RemoveRange(entities);
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Persistence/Repository/UnitOfWork.cs b/LibraryManagementApp/Persistence/Repository/UnitOfWork.cs
new file mode 100644
index 0000000..8b64d33
--- /dev/null
+++ b/LibraryManagementApp/Persistence/Repository/UnitOfWork.cs
@@ -0,0 +1,28 @@
+using LibraryManagementApp.Contracts.RepositoryContracts;
+
+namespace LibraryManagementApp.Persistence.Repository;
+
+public class UnitOfWork: IUnitOfWork
+{
+ private readonly ApplicationDbContext _context;
+
+ public UnitOfWork(ApplicationDbContext context)
+ {
+ _context = context;
+ Authors = new AuthorRepository(_context);
+ Books = new BookRepository(_context);
+ Publishers = new PublisherRepository(_context);
+ Categories = new CategoryRepository(_context);
+ }
+
+ public IAuthorRepository Authors { get; private set; }
+ public IBookRepository Books { get; private set; }
+ public IPublisherRepository Publishers { get; private set; }
+ public ICategoryRepository Categories { get; private set; }
+
+ public int Complete() =>
+ _context.SaveChanges();
+
+ public void Dispose() =>
+ _context.Dispose();
+}
\ No newline at end of file
diff --git a/LibraryManagementApp/Program.cs b/LibraryManagementApp/Program.cs
new file mode 100644
index 0000000..1f7d868
--- /dev/null
+++ b/LibraryManagementApp/Program.cs
@@ -0,0 +1,41 @@
+using LibraryManagementApp.Contracts.RepositoryContracts;
+using LibraryManagementApp.Persistence;
+using LibraryManagementApp.Persistence.Repository;
+using Microsoft.EntityFrameworkCore;
+
+var builder = WebApplication.CreateBuilder(args);
+var connectionString = builder.Configuration.GetConnectionString("PostgresqlConnectionString");
+
+// AddDbContext
+builder.Services.AddDbContext(options =>
+ options.UseNpgsql(connectionString));
+
+builder.Services.AddControllers();
+// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+// Add Services
+builder.Services.AddTransient(typeof(IRepositoryBase<>), typeof(RepositoryBase<>));
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+builder.Services.AddTransient();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+
+app.UseAuthorization();
+
+app.MapControllers();
+
+app.Run();
\ No newline at end of file
diff --git a/LibraryManagementApp/Properties/launchSettings.json b/LibraryManagementApp/Properties/launchSettings.json
new file mode 100644
index 0000000..a7f8d21
--- /dev/null
+++ b/LibraryManagementApp/Properties/launchSettings.json
@@ -0,0 +1,41 @@
+{
+ "$schema": "https://json.schemastore.org/launchsettings.json",
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:20043",
+ "sslPort": 44351
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "http://localhost:5293",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "applicationUrl": "https://localhost:7119;http://localhost:5293",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "swagger",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/LibraryManagementApp/appsettings.Development.json b/LibraryManagementApp/appsettings.Development.json
new file mode 100644
index 0000000..0c208ae
--- /dev/null
+++ b/LibraryManagementApp/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/LibraryManagementApp/appsettings.json b/LibraryManagementApp/appsettings.json
new file mode 100644
index 0000000..9223b41
--- /dev/null
+++ b/LibraryManagementApp/appsettings.json
@@ -0,0 +1,12 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "ConnectionStrings": {
+ "PostgresqlConnectionString": "User ID =postgres;Password=ologuneru;Host=localhost;Port=5433;Database=librarydb; Integrated Security=true;Pooling=true;"
+ },
+ "AllowedHosts": "*"
+}