Source : C# Application from Start to Finish, freeCodeCamp tutorial
FYI : This is an ongoing README and Project Report. Documents will be seperated at the end of the project.
-
Stakeholder requests to build a Tournament tracker using bracket tournament system where application decides the opponents in a single-elimination style. At the end, tournament winner is identified.
-
The tournament model is based on March Madness Seeding (Best v Worst in a division)
-
March Madness Seeding
March Madness Seeding (Best v Worst in a division)- Divisions are broken into pools (e.g., North, South, East, West) and within each pool teams are ranked.
- The top seed plays the worst seed, the second best seed plays the 2nd worst seed within the pool, etc., until all teams play their first round.
- If the pool has an odd (not divisible by 2) number of teams, there will be a "play in" game of the worst vs the 2nd worst team. Such a seeding system produces a wide variety of matches, but requires many games to determine an outcome.
- Identifying the opponents and the winner
- Multiple competitors
- Define which competitors are competing and when
- Schedule games (how to, needs more specifics)
- Single loss elimination
- Identify last competitor standing as winner
- What if there are uneven number of competitors ?
- Randomly advance the odd competitor to next round, a.k.a "Byes"
- Is the total number of competitors fixed or variable ?
- Variable
- Are the opponents chosen randomly or based on any key factors?
- Random
- Are the games scheduled or played whenever possible ?
- Whenever
- If played whenever, could/should we include a pre-round/qualifiers to directly move to round 2 ?
- No. Consequential rounds
- Is there scoring or just the winner ?
- Each player with a score as 1 or 0
- User interface - form or webpage or app ? (never assume!)
- form for now, later website or app
- Data storage location ?
- Microsoft SQL Server, also a text file backup
- Are the Payins to the game(entry fee) or Payouts to the competitors(prizes) handled or nothing at all ?
- Yes, option to charge payins and payouts. Payout for top 3 teams as a percentage of the tournament income, but definetely the sum of payouts < tournament income
- Reporting type - results announced in detail or just brief?
- Send Overall rounds outcomes, each competitor stats to admin and users
- Game results entered by whom ?
- Anyone
- Levels of access - Admin, User ?
- Single-User access. No varying levels, but competitors get only email reports and notifications, admin only accesses form
- Notificaton to users about upcoming games ?
- Yes, competitors are notified by email
- Tracker tracks Players and Teams or only Teams ?
- Every member of the team must be tracked individually for game notifications and reports
- Structure : Windows Forms application and Class Library
- Data Storage : SQL and/or Text File
- Users : One user at a time or single-user access
NOTE : Always research the requirements to know what is lacking and to understand them
- Email : How to send emails through application using C#
- Custom Events : How to identify end of rounds, report round outcomes and notify users about upcoming games
- Error Handling : How to handle possible erroneous user input
- Interfaces : What are they and How to use them
- Random Ordering : How to randomize opponents and choosing Byes
- Texting : BONUS How to capture key user information for notification purposes
-
ALWAYS build Pseudocode.
-
ALWAYS okay to miss some details.
-
Define Objects/Classes by identifying unique categories to record data
- Person
- Team
- Tournament
- Prize
- Match (in tutorial, Matchup)
- MatchRegistry (in tutorial, MatchupEntry)
- Map Relations between Objects/Classes
- Person -> Team <-> Tournament
- Tournament <- Prize
- Tournament <- Match
- Match <-> MatchRegistry <-> Team
- Define Object/Class structure
Object/Class | Property (datatype) |
---|---|
Person |
|
Team |
|
Tournament |
|
Prize |
|
Match |
|
MatchRegistry |
|
- RECOMMENDED : Lay down the most possible UI layout on paper
- Focus on UI layout only, Backend layout will be figured out later
- Possible Forms to be designed and built as follows :
Form | Type | Calling Form | Function |
---|---|---|---|
Tournament Dashboard | Home Page/Main Form | None | Application - Start, End |
Tournament Viewer | Pop Up Form | Tournament Dashboard | Load Tournament |
Create Tournament | Pop Up Form | Tournament Dashboard | Create Tournament |
Create Team | Pop Up Form | Create Tournament | Create/Add Team, Members |
Create Prize | Pop Up Form | Create Tournament | Create Prize |
- NOTE :
- Main Form or Tournament Dashboard must be open at the times for the application to be active
- Main Form or Tournament Dashboard must be functionally detached from other Forms for the application to perform multiple tasks simultaneously, such as :
- Create Tournament for an upcoming Tournament
- Create/Add Teams to new Tournament
- Create Prize for new Tournament
- Load Tournament for viewing and updating status of an active Tournament on Tournament Viewer
- Evaluate HOW :
- Forms are interconnected
- Forms are navigated, back and forth
- For example : In Create Tournament, to create a new team and for that team to show up in the Teams/Players section, an Interface will be required
- Items from Dropdown lists, such as Teams/Players and Prizes, are displayed :
- Removed after selection
- Added after deletion from display sections
- Shown in the related display sections
- Fields with required text values cannot be empty
- Fields with decimal values cannot be negative
- Create XYZ Form button - determines the Logic to randomize Teams into Rounds and Byes
- Post completing Form function, Form closes returns to Calling Form
- Information related to Tournament Viewer :
- Tournament Name
- Current Round and Rounds completed
- Matches played and unplayed
- Scoring only for current Round Matches
- Email notifications on submitting scores
- Data storage and access
- Trigger for updating current Match outcomes and next Match details
- SQL Server Developer Edition 2022 - includes all enterprise features except for a production environment
- SSMS (SQL Server Management Studio) - GUI for managing databases, tables, relationships and connections
- Database Design built using SSMS :
- Stored Procedures :
- Secure protocols to minimze SQL-Injection hacking attempts
- Used to display data from Database tables in the application through simple query results
- How to validate incoming data ?
- Verify if they are in the required format
- How to save the received data ?
- Create methods to save data to SQL Database and Text file
- How to connect to SQL Database ?
- Use the connection string in a Global Static Class
- Where to store the incoming data ?
- SQL Database or Text file or both
- How to save the received data to both data storage points ?
-
Through an Interface
What is an Interface ?
- In simple terms, it is a calling card for structurally and functionally different classes under one umbrella
- For Example: Let us consider health care costs in a family with adults, children and pets. Each category has their own issues but expenses would be paid with the same money.
- Above example can be expressed in an Interface as :
- In the above Interface, an abstract method (method without implementation) is defined.
- If a Class or Struct implements the Interface
IHealthCare
, it must define the method implementation of the interface member - In our example, above it is defined as follows :
- In the above example, since the categories differ their health expense calculation also differs.
- Hence, a full-abstract class/Interface supports building multiple Inheritance to better connect these categories.
// Health Record class HealthInfo { // properties } // Interfaces for health information and expenses public interface IHealthCare { double GetHealthExpenses(); } public interface IHealthInfo { List<HealthInfo> GetHealthInfo(); }
// Adult Healthcare class AdultHealthCare : IHealthInfo, IHealthCare { public List<HealthInfo> GetHealthInfo() { // code } public double GetHealthExpenses() { // code } } // Pet Healthcare class PetHealthCare : IHealthInfo, IHealthCare { public List<HealthInfo> GetHealthInfo() { // code } public double GetHealthExpenses() { // code } }
-
-
To setup the backend connection to a database, in our case SQL Server, we need an ORM (Object-Relational Mapping) Tool.
What is an ORM tool
- ORM or Object-Relational Mapping is the process of handling database access and operations
through Object-oriented paradigm
- An ORM Tool is a library or a framework used to implement ORM functionality in an application, such as define database schema, perform CRUD operations, etc.
- ORM Tools help by abstracting the low-level database handling events. For ex. Dapper and Entity Framework
ORM Alternative
- The namespace
System.Data.SqlClient
contains classes for accessing data from a SQL Servere database. - Nevertheless, this makes database access handling laborious, as every low-level database interaction must be manually managed.
- ORM or Object-Relational Mapping is the process of handling database access and operations
through
-
Suitable ORM library/frameworks - ADO.NET, Dapper, Entity Framework (EF Core)
Feature | ADO.NET | Dapper | EF Core |
---|---|---|---|
Performance | Fast | Faster than ADO.NET and EF Core | Based on usage and configuration |
Database Access Handling | Manual | Semi-Automated | Automated |
Generates Class model | No | No | Yes |
Generates Queries | No | No | Yes |
Object Tracking | No | No | Yes |
-
For this project, we chose Dapper as the ORM tool for the following reasons :
-
Basic Mapping between database tables and C# objects
-
Managing database connections
-
Control over SQL Queries
-
Enhanced performance
-
-
In the Frontend, App.config file needs to be configured with SQL Server Connection string
-
In the Backend, Models use ORM tool to perform CRUD operations to their respective tables in SQL Server Database
-
Similar to the database having a table for every Model, a text file for every Model is ideal.
-
Pseudo code :
- Load text file,
- Convert text file data to List
- Read and Find max(ID)
- Assign ID = max+1 for new prize data
- Save List data
- Convert List to text file
-
To generalize and automate this process for all the Models would be possible by an external library called, AutoMapper
-
AutoMapper is an Object-Object Mapper which could work ideally for our current Models,
-
In the current scenario, we explicitly :
- convert text data to specific data types,
- process the data
- revert them back
- and finally save them as text files
-
Hence, AutoMapper would be an efficient tool to refine the process.
- In
CreateTournamentForm
there are two different options :- Call
CreatePrizeForm
to create a new prize - Call
CreateTeamForm
to create a new team/player
- Call
- An Interface class
ICreateRequestor
is defined to act as an intermediate betweenCreatePrizeForm
<->CreateTournamentForm
throughPrizeComplete
member,CreateTeamForm
<->CreateTournamentForm
throughTeamComplete
member
- In
CreatePrizeForm
andCreateTeamForm
classes, this interface is declared in the constructor as a class variable - In their respective class methods which signify the process completion (
createPrizeButton_Click
andcreateTeamButton_Click
) the Interface Members are called and model data is forwarded to the calling class,CreateTournamentForm
. - In the calling class (
CreatTournamentForm
), the Interface Members(PrizeComplete
andTeamComplete
) are implemented- ListBox display list (
availablePrizes
) and Dropdown teams list (availableTeams
) are updated.
- ListBox display list (
- To figure out when to choose what kind of project model in Visual Studio. For example in this project :
ClassLibrary
project model was chosen to build Backend (Data Models & Data Access configurations)WindowsForms
project was added as StartUp project to build Frontend (Forms)
- Solution is built from the project. Hence, the final product should be signified in the Solution name.
- Break down logically complex tasks into smaller chunks
- PLAN -> DESIGN -> Test IMPLEMENTATION -> DEBUG -> Repeat
- Start simple and Build towards complex.
For example, Interconnected UIs require data to test the connection. Hence, start as follows : populate data -> store data -> data interaction
-
Application Development Layout, as understood from Lessons so far (till Lesson10-SQL Connection)
-
Post organizing files into directories, ALWAYS verify and modify namespaces
-
-
A parameter in a method is preceded by the above modifier
-
Used in Extension methods
-
Extension methods are in scope when explicitly imported with the respective namespaces into source code with a
using
directiveExample Implementation
// EXTENSION METHOD DEFINITION - namespace ExtensionMethods { public static class MyExtension { public static int WordCount(this string str) { int count = str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; return count; } } } // SOURCE CODE - // call with Instance method syntax as follows using ExtensionMethods; string s = "Hello Extension Method"; int i = s.WordCount(); // OR call with Static method syntax, by passing arguments string s = "Hello Extension Method"; int i = MyExtension.WordCount(s);
-
-
Cases for data type casting :
- VALID Case :
string
toint
/double
/decimal
, i.e., from string to a numerical format - INVALID Case :
int
todouble
, viceversa, or to any other numerical format- Possible data loss and inefficient outputs
- VALID Case :
-
With current level of logical understanding, the following seems complex and hard to follow:
- Logic behind Data storage to SQL and Textfiles for -
- Nevertheless, keep up - slowly but steadily.
- Most likely errors, NullReferenceException
- Start with Data storage format
- Retrace to 'Save' and 'Load' methods
- Dive into 'Helper-Save' or 'Helper-Load' methods, if any
- Check that the proper methods are called in the right places
- For ex. Text file storage
- Calling the right files inside the corresponding 'Save/Load' methods alone is not enough
- Make sure to supply the filepath as well
public static void SaveEntryToTournamentsFile(this MatchRegistryModel entry, string MatchRegistryDataFile) { //code . . File.WriteAllLines(MatchRegistryDataFile.GetFilePath(), modelsData); }