Implemented an interactive football database console manager

Database - PostgreSQL
ORM framework - Entity Framework Core 6.0
Distributed cache - Redis (Not implemented yet)
Pretty console output - Sharprompt
This commit is contained in:
Egor 2022-08-10 19:56:33 +03:00
parent 5c6b306b16
commit 0ef7fd22bf
7 changed files with 446 additions and 0 deletions

25
EFTask/EFTask.sln Normal file
View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32630.192
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFTask", "EFTask\EFTask.csproj", "{38961979-A830-4012-9CD2-A7AFA0201540}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{38961979-A830-4012-9CD2-A7AFA0201540}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{38961979-A830-4012-9CD2-A7AFA0201540}.Debug|Any CPU.Build.0 = Debug|Any CPU
{38961979-A830-4012-9CD2-A7AFA0201540}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38961979-A830-4012-9CD2-A7AFA0201540}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E1D0D85F-491D-4ABB-BD22-A11F03D7A83F}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.7" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.6" />
<PackageReference Include="Sharprompt" Version="2.4.3" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View file

@ -0,0 +1,51 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EFTask
{
public class FootballContext : DbContext
{
public DbSet<FootballPlayer> Players { get; set; } = null!;
public DbSet<FootballTeam> Teams { get; set; } = null!;
public DbSet<FootbalContract> Contracts { get; set; } = null!;
public FootballContext()
{
Console.WriteLine("Connecting to database...");
Database.EnsureCreated();
Console.WriteLine("Connection established");
Console.WriteLine();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connectionString = GetConnectionString();
optionsBuilder.UseNpgsql(connectionString);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
private static string GetConnectionString()
{
var builder = new ConfigurationBuilder();
var config = builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var dbConnectionSettings = config.GetSection("databaseConnectionSettings");
string host = dbConnectionSettings["host"],
port = dbConnectionSettings["port"],
db = dbConnectionSettings["database"],
user = dbConnectionSettings["username"],
password = dbConnectionSettings["password"];
return $"Host={host};Port={port};Database={db};Username={user};Password={password};";
}
}
}

View file

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EFTask
{
[Table("players")]
public class FootballPlayer
{
[Key, Column("id")]
public long Id { get; set; }
[Required, Column("name")]
public string Name { get; set; } = "";
[Required, Column("age")]
public int Age { get; set; }
public ISet<FootbalContract> Contracts { get; set; } = new HashSet<FootbalContract>();
}
[Table("teams")]
public class FootballTeam
{
[Key, Column("id")]
public long Id { get; set; }
[Required, Column("name")]
public string Name { get; set; } = "";
public ISet<FootbalContract> Contracts { get; set; } = new HashSet<FootbalContract>();
}
[Table("contracts")]
public class FootbalContract
{
[Key, Column("id")]
public long Id { get; set; }
[Required, Column("player_id")]
public long PlayerId { get; set; }
public FootballPlayer Player { get; set; } = new();
[Required, Column("team_id")]
public long TeamId { get; set; }
public FootballTeam Team { get; set; } = new();
[Required, Column("salary")]
public decimal Salary { get; set; }
}
}

View file

@ -0,0 +1,286 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Sharprompt;
namespace EFTask
{
public class FootballManager
{
protected readonly Dictionary<string, Action<FootballContext>> ACTIONS;
protected bool isRunning = false;
public FootballManager()
{
ACTIONS = new()
{
{ "Show players", fc => ShowPlayers(fc) },
{ "Insert player", fc => InsertPlayer(fc) },
{ "Update player", fc => UpdatePlayer(fc) },
{ "Delete players", fc => DeletePlayers(fc) },
{ "Show teams", fc => ShowTeams(fc) },
{ "Insert team", fc => InsertTeam(fc) },
{ "Update team", fc => UpdateTeam(fc) },
{ "Delete teams", fc => DeleteTeams(fc) },
{ "Show contracts", fc => ShowContracts(fc) },
{ "Insert contract", fc => InsertContract(fc) },
{ "Update contract", fc => UpdateContract(fc) },
{ "Delete contracts", fc => DeleteContracts(fc) },
{ "Amount of players total", fc => PlayersTotalAmount(fc) },
{ "Amount of players in a team", fc => PlayersTeamAmount(fc) },
{ "Overall expenses", fc => OverallExpenses(fc) },
{ "Team expenses", fc => TeamExpenses(fc) },
{ "Quit", _ => Quit(_) }
};
}
public void Start(FootballContext fc)
{
Console.WriteLine("Welcome to the interactive football database console manager!");
isRunning = true;
while (isRunning)
{
Console.WriteLine();
var actionKey = Prompt.Select("Select the action to perform", ACTIONS.Keys, 4);
Console.WriteLine();
var action = ACTIONS[actionKey];
action.Invoke(fc);
}
}
public void AddAction(string name, Action<FootballContext> fction) => ACTIONS.Add(name, fction);
protected void ShowPlayers(FootballContext fc)
{
var players = fc.Players.Include(p => p.Contracts).ToHashSet<FootballPlayer?>();
players.Add(null);
while (true)
{
var playerToDescribe = Prompt.Select("Select a player for additional information", players, 10,
textSelector: p => p == null ? "Back" : $"{p.Id}. {p.Name}, {p.Age}");
if (playerToDescribe == null) return;
Console.WriteLine("\nFootball player\n\n"
+ $"Id: {playerToDescribe.Id}\n"
+ $"Name: {playerToDescribe.Name}\n"
+ $"Age: {playerToDescribe.Age}\n"
+ $"Contracts:\n{string.Join("\n", playerToDescribe.Contracts.Select(c => $"{c.Team.Id}. {c.Team.Name}, {c.Salary} $/year"))}\n\n"
+ "Press any key to go back...");
Console.ReadKey();
Console.WriteLine();
}
}
protected void InsertPlayer(FootballContext fc)
{
Console.WriteLine("Inserting a new football player...");
var name = Prompt.Input<string>("Enter the player's name");
var age = Prompt.Input<int>("Enter the player's age");
var player = new FootballPlayer() { Name = name, Age = age };
fc.Players.Add(player);
fc.SaveChanges();
Console.WriteLine($"Successfully inserted a new player with an id {player.Id}");
}
protected void UpdatePlayer(FootballContext fc)
{
Console.WriteLine("Updating a football player...");
var players = fc.Players.ToHashSet<FootballPlayer?>();
players.Add(null);
var player = Prompt.Select("Select a player to update", players, 10,
textSelector: p => p == null ? "Back" : $"{p.Id}. {p.Name}, {p.Age}");
if (player == null) return;
player.Name = Prompt.Input<string>("Enter the player's new name", player.Name);
player.Age = Prompt.Input<int>("Enter the player's new age", player.Age);
fc.SaveChanges();
Console.WriteLine($"Successfully updated a player with an id {player.Id}");
}
protected void DeletePlayers(FootballContext fc)
{
Console.WriteLine("Deleting football players...");
var playersToDelete = Prompt.MultiSelect("Select players to delete", fc.Players, 10, 0,
textSelector: p => $"{p.Id}. {p.Name}, {p.Age}");
foreach (var player in playersToDelete)
fc.Players.Remove(player);
fc.SaveChanges();
Console.WriteLine($"Successfully deleted {playersToDelete.Count()} players");
}
protected void ShowTeams(FootballContext fc)
{
var teams = fc.Teams.Include(t => t.Contracts).ToHashSet<FootballTeam?>();
teams.Add(null);
while (true)
{
var teamToDescribe = Prompt.Select("Select a team for additional information", teams, 10,
textSelector: t => t == null ? "Back" : $"{t.Id}. {t.Name}");
if (teamToDescribe == null) return;
Console.WriteLine("\nFootball team\n\n"
+ $"Id: {teamToDescribe.Id}\n"
+ $"Name: {teamToDescribe.Name}\n"
+ $"Contracts:\n{string.Join("\n", teamToDescribe.Contracts.Select(c => $"{c.Player.Id}. {c.Player.Name}, {c.Player.Age}"))}\n\n"
+ "Press any key to go back...");
Console.ReadKey();
Console.WriteLine();
}
}
protected void InsertTeam(FootballContext fc)
{
Console.WriteLine("Inserting a new football team...");
var name = Prompt.Input<string>("Enter the team's name");
var team = new FootballTeam() { Name = name };
fc.Teams.Add(team);
fc.SaveChanges();
Console.WriteLine($"Successfully inserted a new team with an id {team.Id}");
}
protected void UpdateTeam(FootballContext fc)
{
Console.WriteLine("Updating a football team...");
var teams = fc.Teams.ToHashSet<FootballTeam?>();
teams.Add(null);
var team = Prompt.Select("Select a team to update", teams, 10,
textSelector: t => t == null ? "Back" : $"{t.Id}. {t.Name}");
if (team == null) return;
team.Name = Prompt.Input<string>("Enter the team's new name", team.Name);
fc.SaveChanges();
Console.WriteLine($"Successfully updated a team with an id {team.Id}");
}
protected void DeleteTeams(FootballContext fc)
{
Console.WriteLine("Deleting football teams...");
var teamsToDelete = Prompt.MultiSelect("Select teams to delete", fc.Teams, 10, 0,
textSelector: t => $"{t.Id}. {t.Name}");
foreach (var team in teamsToDelete)
fc.Teams.Remove(team);
fc.SaveChanges();
Console.WriteLine($"Successfully deleted {teamsToDelete.Count()} teams");
}
protected void ShowContracts(FootballContext fc)
{
var contracts = fc.Contracts.ToHashSet<FootbalContract?>();
contracts.Add(null);
while (true)
{
var contractToDescribe = Prompt.Select("Select a team for additional information", contracts, 10,
textSelector: c => c == null ? "Back" : $"{c.Id}. {c.Player.Name} - {c.Team.Name}");
if (contractToDescribe == null) return;
Console.WriteLine("\nFootball contract\n\n"
+ $"Id: {contractToDescribe.Id}\n"
+ $"Player:\n"
+ $"\tId: {contractToDescribe.PlayerId}\n"
+ $"\tName: {contractToDescribe.Player.Name}\n"
+ $"\tAge: {contractToDescribe.Player.Age}\n"
+ $"Team:\n"
+ $"\tId: {contractToDescribe.TeamId}\n"
+ $"\tNAme: {contractToDescribe.Team.Name}\n"
+ $"Salary: {contractToDescribe.Salary} $/year\n\n"
+ "Press any key to go back...");
Console.ReadKey();
Console.WriteLine();
}
}
protected void InsertContract(FootballContext fc)
{
Console.WriteLine("Inserting a new football contract...");
if (fc.Players.Count() == 0)
{
Console.WriteLine("No players were found, insert a new player to make a contract");
Console.WriteLine("Cancelling operation...");
return;
}
var player = Prompt.Select("Select the contract's player", fc.Players, 10,
textSelector: p => $"{p.Id}. {p.Name}, {p.Age}");
var availableTeams = fc.Teams.Except(player.Contracts.Select(p => p.Team));
if (availableTeams.Count() == 0)
{
Console.WriteLine("No available teams were found, insert a new team to make a contract with this player");
Console.WriteLine("Cancelling operation...");
return;
}
var team = Prompt.Select("Select the contract's team", availableTeams, 10,
textSelector: t => $"{t.Id}. {t.Name}");
var salary = Prompt.Input<decimal>("Input the player's salary ($/year)");
var contract = new FootbalContract() { Player = player, Team = team, Salary = salary };
fc.Contracts.Add(contract);
fc.SaveChanges();
Console.WriteLine($"Successfully inserted a new contract with an id {contract.Id}");
}
protected void UpdateContract(FootballContext fc)
{
Console.WriteLine("Updating a football contract...");
var contracts = fc.Contracts.ToHashSet<FootbalContract?>();
contracts.Add(null);
var contract = Prompt.Select("Select a contract to update", contracts, 10,
textSelector: c => c == null ? "Back" : $"{c.Id}. {c.Player.Name} - {c.Team.Name}");
if (contract == null) return;
var players = fc.Players.Where(p => p.Id != contract.PlayerId).ToHashSet<FootballPlayer?>();
players.Add(null);
var player = Prompt.Select("Select a new player", players, 10,
textSelector: p => p == null ? "Do not change" : $"{p.Id}. {p.Name}, {p.Age}");
if (player != null)
contract.Player = player;
var teams = fc.Teams.Where(t => t.Id != contract.TeamId).ToHashSet<FootballTeam?>();
teams.Add(null);
var team = Prompt.Select("Select a new team", teams, 10,
textSelector: t => t == null ? "Do not change" : $"{t.Id}. {t.Name}");
if (team != null)
contract.Team = team;
var salary = Prompt.Input<decimal>("Input the player's salary", contract.Salary, contract.Salary.ToString());
contract.Salary = salary;
fc.SaveChanges();
Console.WriteLine($"Successfully updated a contract with an id {contract.Id}");
}
protected void DeleteContracts(FootballContext fc)
{
Console.WriteLine("Deleting football contracts...");
var contractsToDelete = Prompt.MultiSelect("Select contracts to delete", fc.Contracts, 10, 0,
textSelector: c => $"{c.Id}. {c.Player.Name} - {c.Team.Name}");
foreach (var contract in contractsToDelete)
fc.Contracts.Remove(contract);
fc.SaveChanges();
Console.WriteLine($"Successfully deleted {contractsToDelete.Count()} contracts");
}
protected void PlayersTeamAmount(FootballContext fc)
{
throw new NotImplementedException();
}
protected void PlayersTotalAmount(FootballContext fc)
{
throw new NotImplementedException();
}
protected void TeamExpenses(FootballContext fc)
{
throw new NotImplementedException();
}
protected void OverallExpenses(FootballContext fc)
{
throw new NotImplementedException();
}
protected void Quit(FootballContext _)
{
isRunning = false;
Console.WriteLine();
Console.WriteLine("Quitting...");
}
}
}

5
EFTask/EFTask/Program.cs Normal file
View file

@ -0,0 +1,5 @@
using EFTask;
var manager = new FootballManager();
using (var applicationContext = new FootballContext())
manager.Start(applicationContext);

View file

@ -0,0 +1,9 @@
{
"databaseConnectionSettings": {
"host": "localhost",
"port": "5432",
"database": "football",
"username": "cbgr",
"password": "cbgr"
}
}