2

I am building a console app in C# within Visual Studio which is intended to do these three functions:

  1. Total the number of cars sold in a week by a fake car dealer
  2. Display employee name/cars sold in one week by a fake car dealer for an employee of the week
  3. Write all the sales data to a text file

The cars sold/employee of the week needs to be randomised on a week by week basis but sales will be between 50 and 200, with six staff on the books.

The employee of the week is the one who has sold the most cars in a week versus the other staff. E.g. Ben is employee of the week as he sells 150 cars versus Jordan at 50.

CODE:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] stringEmployeeArray = new string[6];
            stringEmployeeArray[0] = "Bob";
            stringEmployeeArray[1] = "Steven";
            stringEmployeeArray[2] = "Jordan";
            stringEmployeeArray[3] = "Lee";
            stringEmployeeArray[4] = "Max";
            stringEmployeeArray[5] = "Ben";

            int[] intCarsSoldArray = new int[4];
            intCarsSoldArray[0] = 50;
            intCarsSoldArray[1] = 100;
            intCarsSoldArray[2] = 150;
            intCarsSoldArray[3] = 200;

            Random rnd = new Random();

            Console.WriteLine("Welcome to the Car Dealership Sales Tracker");
            Console.WriteLine("Press 1 to output the name and number of cars sold for the employee with the highest sales (1) ");
            Console.WriteLine("Press 2 to calculate the total number of cars sold by the company (2) ");
            Console.WriteLine("Press 3 to output all dsales data to a text file (3) ");
            Console.WriteLine("-------------------------------------------");
            Console.WriteLine("Enter option number and hit ENTER");
            int val = 5;
            ConsoleKeyInfo input = Console.ReadKey();
            Console.WriteLine();
            switch (val)
            {
                if (input.Key == ConsoleKey.NumPad1)
                case 1:
                    string r = rnd.Next(stringEmployeeArray.Count()).ToString();
                    int x = rnd.Next(intCarsSoldArray.Count());
                    Console.WriteLine("This weeks employee of the week is(string)stringEmployeeArray[r] + (int)intCarsSoldArray[x]");
                    break;
            if (input.Key == ConsoleKey.NumPad2)
                case 2:
                    int sum = intCarsSoldArray.Sum();
                    Console.WriteLine("Total cars sold for this week is(intCarsSoldArray.Sum)");
                    break;
            if (input.Key == ConsoleKey.NumPad3)
                case 3:
                    break;
            }
        }
    }
}

It runs on a sort of menu system where the number key pressed decides on a option. At the moment, you can see 1 is to do employee of the week with his/her cars sold, total cars sold in a week is number 2 and text file writing is number 3.

However, when I enter a option number and hit enter the program shuts itself down.

What I would like to know is

  1. How to code number 1 when pressed to retrieve employee of the week data
  2. How to code number 2 pressed to calculate all sales for a week
  3. How to code number 3 when pressed to write all this data for a week to a text file to be stored on the computer and how to code the actual writing of the data to the file.

I would also like to know how to do an error message whereby if the user enters a number of 0 or 4 or more they get this WriteLine message: "Enter a valid number between 1 and 3".

Being new to C#, any help greatly appreciated.

Thanks

NEW CODE FOR RUFUS

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    public class Employee
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int WeeklySales { get; set; }

        public override string ToString()
        {
            return $"{FirstName} {LastName}";
        }
    }
    public class Program
    {
        private static readonly Random Rnd = new Random();

        private static List<Employee> Employees = new List<Employee>
    {
        new Employee {FirstName = "Bob"},
        new Employee {FirstName = "Steven"},
        new Employee {FirstName = "Jordan"},
        new Employee {FirstName = "Lee"},
        new Employee {FirstName = "Max"},
        new Employee {FirstName = "Ben"}
    };
        static void Main()
        {
            GenerateSalesData();
            MainMenu();
        }
        private static void ClearAndWriteHeading(string heading)
        {
            Console.Clear();
            Console.WriteLine(heading);
            Console.WriteLine(new string('-', heading.Length));
        }
        private static void GenerateSalesData()
        {
            ClearAndWriteHeading("Car Dealership Sales Tracker - Generate Sales Data");

            foreach (var employee in Employees)
            {
                employee.WeeklySales = Rnd.Next(50, 201);
            }

            Console.WriteLine("\nSales data has been generated!!");
        }
        private static void MainMenu()
        {
            ClearAndWriteHeading("Car Dealership Sales Tracker - Main Menu");

            Console.WriteLine("1: Display employee of the week information");
            Console.WriteLine("2: Display total number of cars sold by the company");
            Console.WriteLine("3: Write all sales data to a text file");
            Console.WriteLine("4: Generate new weekly sales info");
            Console.WriteLine("5: Exit program\n");
            Console.Write("Enter option number 1 - 5: ");

            // Get input from user (ensure they only enter 1 - 5)
            int input;
            while (!int.TryParse(Console.ReadKey().KeyChar.ToString(), out input) ||
                input < 1 || input > 5)
            {
                // Erase input and wait for valid response
                Console.SetCursorPosition(0, 8);
                Console.Write("Enter option number 1 - 5:  ");
                Console.SetCursorPosition(Console.CursorLeft - 1, 8);
            }

            ProcessMenuItem(input);
        }
        private static void ProcessMenuItem(int itemNumber)
        {
            switch (itemNumber)
            {
                case 1:
                    DisplayEmployeeOfTheWeekInfo();
                    break;
                case 2:
                    DisplayTotalSales();
                    break;
                case 3:
                    WriteSalesToFile();
                    break;
                case 4:
                    GenerateSalesData();
                    break;
                default:
                    Environment.Exit(0);
                    break;
            }

            Console.Write("\nPress any key to go back to main menu...");
            Console.ReadKey();
            MainMenu();
        }
        private static void DisplayEmployeeOfTheWeekInfo()
        {
            ClearAndWriteHeading("Car Dealership Sales Tracker - Employee of the Week");

            var employeeOfTheWeek = Employees.OrderByDescending(employee => employee.WeeklySales).First();

            Console.WriteLine($"{employeeOfTheWeek} is the employee of the week!");
            Console.WriteLine($"This person sold {employeeOfTheWeek.WeeklySales} cars this week.");
        }
        private static string GetSalesData()
        {
            var data = new StringBuilder();
            data.AppendLine("Employee".PadRight(25) + "Sales");
            data.AppendLine(new string('-', 30));

            foreach (var employee in Employees)
            {
                data.AppendLine(employee.FirstName.PadRight(25) + employee.WeeklySales);
            }

            data.AppendLine(new string('-', 30));
            data.AppendLine("Total".PadRight(25) +
                Employees.Sum(employee => employee.WeeklySales));

            return data.ToString();
        }
        private static void DisplayTotalSales()
        {
            ClearAndWriteHeading("Car Dealership Sales Tracker - Total Sales");

            Console.WriteLine("\nHere are the total sales for the week:\n");
            Console.WriteLine(GetSalesData());
        }
        private static void WriteSalesToFile(string errorMessage = null)
        {
            ClearAndWriteHeading("Car Dealership Sales Tracker - Write Sales Data To File");

            if (!string.IsNullOrEmpty(errorMessage)) Console.WriteLine($"\n{errorMessage}\n");
            Console.Write("\nEnter the path to the sales data file:");
            var filePath = Console.ReadLine();
            var salesData = GetSalesData();
            var error = false;

            if (ERROR HERE File.Exists(filePath))
            {
                Console.Write("File exists. (O)verwrite or (A)ppend: ");
                var input = Console.ReadKey().Key;

                while (input != ConsoleKey.A && input != ConsoleKey.O)
                {
                    Console.Write("Enter 'O' or 'A': ");
                    input = Console.ReadKey().Key;
                }

                if (input == ConsoleKey.A)
                {
                    ERROR HERE File.AppendAllText(filePath, salesData);
                }
                else
                {
                    ERROR HERE File.WriteAllText(filePath, salesData);
                }
            }
            else
            {
                try
                {
                    ERROR HERE File.WriteAllText(filePath, salesData);
                }
                catch
                {
                    error = true;
                }
            }

            if (error)
            {
                WriteSalesToFile($"Unable to write to file: {filePath}\nPlease try again.");
            }
            else
            {
                Console.WriteLine($"\nSuccessfully added sales data to file: {filePath}");
            }
        }
    }
}
  • Your `switch` / `case` statements are probably supposed to act on the value of the key pressed - instead they're acting on `val` which is a constant of `5`'. Which doesn't match any of the `case`s so control falls through. – peeebeee Apr 04 '18 at 13:46
  • I can't remember where I got the idea to write that line, I think it was to do with the sales array of 5 elements, although really this should not be fixed if the numbers are to be random, with that current one I couldn't have odd numbers not in increments of 50 between 50 and 200 – Hugi Husser Apr 04 '18 at 13:50
  • You can't have `if` statements in your `switch` like that... – Rufus L Apr 04 '18 at 13:51
  • You should decide whether you're going to use `switch/case` or `if` statements - either will work. You've got a mixture of the two. – peeebeee Apr 04 '18 at 14:12
  • You should only ask one question per stack overflow question, and you should state specifically how your current code isn't working. – Rufus L Apr 04 '18 at 14:12

2 Answers2

1

I think you might want to step back and examine the overall design. There are a lot of moving pieces to a menu system, and it can be helpful to break out the different tasks you need to do into separate functions.

For example, you could have a generic function that clears the console and displays a menu heading, you could have a method that displays the menu options, and you could have a method for each option itself. In this way, you would just call "MainMenu" from your Main method, then the main menu method would get the user input and call the appropriate method depending on what the user entered.

You could also have a method that randomized the employee data, so that you could simulate a new week if you want to. And, for that matter, a simple employee class that stores the employee name and his or her weekly sales could come in handy, too.

Here's how I would do it. First the Employee class, which associates a name with weekly sales:

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int WeeklySales { get; set; }

    public override string ToString()
    {
        return $"{FirstName} {LastName}";
    }
}

Then in our Program class we can have some private variables to hold the employees and an instance of the Random class:

public class Program
{
    private static readonly Random Rnd = new Random();

    private static List<Employee> Employees = new List<Employee>
    {
        new Employee {FirstName = "Bob"},
        new Employee {FirstName = "Steven"},
        new Employee {FirstName = "Jordan"},
        new Employee {FirstName = "Lee"},
        new Employee {FirstName = "Max"},
        new Employee {FirstName = "Ben"}
    };

Then in our Main method we can just call a method to get some random sales data, and another method to show the main menu:

    private static void Main()
    {
        GenerateSalesData();
        MainMenu();
    }

Before getting into these methods, let's create a simple one that clears the console and writes a heading line, followed by a dashed line (to act as an underline):

    private static void ClearAndWriteHeading(string heading)
    {
        Console.Clear();
        Console.WriteLine(heading);
        Console.WriteLine(new string('-', heading.Length));
    }

Now the method that generates random data for our employees. All we have to do is loop through our employees and, for each one, pick a random number between 50 and 200 and assign that to the Employee.WeeklySales property:

    private static void GenerateSalesData()
    {
        ClearAndWriteHeading("Car Dealership Sales Tracker - Generate Sales Data");

        foreach (var employee in Employees)
        {
            employee.WeeklySales = Rnd.Next(50, 201);
        }

        Console.WriteLine("\nSales data has been generated!!");
    }

And one that displays the Main Menu. Note here that we do some validation on the user input to be sure that it can be converted to an int (using the int.TryParse method) and that the int is within our specified bounds. When we get valid input, we call a different method to process that input:

    private static void MainMenu()
    {
        ClearAndWriteHeading("Car Dealership Sales Tracker - Main Menu");

        Console.WriteLine("1: Display employee of the week information");
        Console.WriteLine("2: Display total number of cars sold by the company");
        Console.WriteLine("3: Write all sales data to a text file");
        Console.WriteLine("4: Generate new weekly sales info");
        Console.WriteLine("5: Exit program\n");
        Console.Write("Enter option number 1 - 5: ");

        // Get input from user (ensure they only enter 1 - 5)
        int input;
        while (!int.TryParse(Console.ReadKey().KeyChar.ToString(), out input) ||
            input < 1 || input > 5)
        {
            // Erase input and wait for valid response
            Console.SetCursorPosition(0, 8);
            Console.Write("Enter option number 1 - 5:  ");
            Console.SetCursorPosition(Console.CursorLeft - 1, 8);
        }

        ProcessMenuItem(input);
    }

Here's the method that processes the input, which uses a switch statement on the input value and then calls the appropriate method for that command:

    private static void ProcessMenuItem(int itemNumber)
    {
        switch (itemNumber)
        {
            case 1:
                DisplayEmployeeOfTheWeekInfo();
                break;
            case 2:
                DisplayTotalSales();
                break;
            case 3:
                WriteSalesToFile();
                break;
            case 4:
                GenerateSalesData();
                break;
            default:
                Environment.Exit(0);
                break;
        }

        Console.Write("\nPress any key to go back to main menu...");
        Console.ReadKey();
        MainMenu();
    }

To display the employee of the week, all we have to do is order our employees list by the WeeklySales property, and choose the first one:

    private static void DisplayEmployeeOfTheWeekInfo()
    {
        ClearAndWriteHeading("Car Dealership Sales Tracker - Employee of the Week");

        var employeeOfTheWeek = Employees
            .OrderByDescending(employee => employee.WeeklySales)
            .First();

        Console.WriteLine($"{employeeOfTheWeek} is the employee of the week!");
        Console.WriteLine(
            $"This person sold {employeeOfTheWeek.WeeklySales} cars this week.");
    }

Now, before we get into displaying the total sales, I noticed that we really have two options for the sales info. One writes the info to the console window, and the other writes it to a file. Rather than have two methods with almost the exact same code, we should just create one method that returns the data, and then the other methods can call it and either write that data to the console or to a file.

The GetSalesData method loops through all our employees and displays their sales, along with the total sales at the end:

    private static string GetSalesData()
    {
        var data = new StringBuilder();
        data.AppendLine("Employee".PadRight(25) + "Sales");
        data.AppendLine(new string('-', 30));

        foreach (var employee in Employees)
        {
            data.AppendLine(employee.FirstName.PadRight(25) + employee.WeeklySales);
        }

        data.AppendLine(new string('-', 30));
        data.AppendLine("Total".PadRight(25) + 
            Employees.Sum(employee => employee.WeeklySales));

        return data.ToString();
    }

Now we can call this method to display it to the console:

    private static void DisplayTotalSales()
    {
        ClearAndWriteHeading("Car Dealership Sales Tracker - Total Sales");

        Console.WriteLine("\nHere are the total sales for the week:\n");
        Console.WriteLine(GetSalesData());
    }

And we can have another method that writes it to a file (we call GetSalesData from this method, too).

Notice that we have some additional stuff in this method to get the file path from the user, check if the file exists, and then asks them if they want to overwrite the file or append to it. I also put in a try/catch block to re-try the method (by calling it from itself, with an error message) in case there is some file I/O error:

    private static void WriteSalesToFile(string errorMessage = null)
    {
        ClearAndWriteHeading("Car Dealership Sales Tracker - Write Sales Data To File");

        if (!string.IsNullOrEmpty(errorMessage)) Console.WriteLine($"\n{errorMessage}\n");
        Console.Write("\nEnter the path to the sales data file: ");
        var filePath = Console.ReadLine();
        var salesData = GetSalesData();
        var error = false;

        if (File.Exists(filePath))
        {
            Console.Write("File exists. (O)verwrite or (A)ppend: ");
            var input = Console.ReadKey().Key;

            while (input != ConsoleKey.A && input != ConsoleKey.O)
            {
                Console.Write("Enter 'O' or 'A': ");
                input = Console.ReadKey().Key;
            }

            if (input == ConsoleKey.A)
            {
                File.AppendAllText(filePath, salesData);
            }
            else
            {
                File.WriteAllText(filePath, salesData);
            }
        }
        else
        {
            try
            {
                File.WriteAllText(filePath, salesData);
            }
            catch
            {
                error = true;
            }
        }

        if (error)
        {
            WriteSalesToFile($"Unable to write to file: {filePath}\nPlease try again.");
        }
        else
        {
            Console.WriteLine($"\nSuccessfully added sales data to file: {filePath}");
        }
    }

Anyway, I know it's a lot of code, but hopefully it gives you some ideas of how you can break parts of your program out into methods to make it a little more readable, maintainable, and easier to use.

Output

Here are some screenshots of the different menus:

enter image description here

Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • If you are trying to hard-code the path into the program, then, since the backslash character is an escape character in C#, you will need to either use double-backslashes inside the string (to escape the escape character) or the "@" character before the string (which means treat the string as a literal and ignore escape characters). Either: `var filePath = @"c:\users\userName\desktop\sales.txt"` or `var filePath = "c:\\users\\userName\\desktop\\sales.txt"` – Rufus L Apr 04 '18 at 16:45
  • CS0103 ISSUE HERE if (File.Exists(filePath)) { Console.Write("File exists. (O)verwrite or (A)ppend: "); var input = Console.ReadKey().Key; while (input != ConsoleKey.A && input != ConsoleKey.O) { Console.Write("Enter 'O' or 'A': "); input = Console.ReadKey().Key; } if (input == ConsoleKey.A) { File.AppendAllText(filePath, salesData); } else – Hugi Husser Apr 04 '18 at 16:52
  • Why are you writing the path to the console? What are you trying to do? You've only changed what's displayed to the user, but this line sets the path: `var filePath = Console.ReadLine();` If you want to hard-code it, then delete the line that prompts the user to enter the file path and use the code in my comment above. – Rufus L Apr 04 '18 at 16:53
  • Yeah sorry I got confused I thought you had to pre specify the path before the program is run. I still have the name file does not exist in current context problem – Hugi Husser Apr 04 '18 at 16:57
  • That means that you're trying to access the variable `fileName` from outside the scope in which it's defined. Are you trying to hard-code it so the user doesn't have to enter it? And are you accessing it from some other method? If so, just put it in at the `class` level, where we declare `employees` and `Rnd`, something like: `private string DataFile = @"c:\users\userName\desktop\sales.txt";` Otherwise share your code somewhere so I can see what you're doing. – Rufus L Apr 04 '18 at 17:02
  • Where I have "File" - I have put comments in the code for you. – Hugi Husser Apr 04 '18 at 17:38
  • Add `using System.IO;` to your usings to access the `File` class. – Rufus L Apr 04 '18 at 17:42
0

The main problem here is the invalid switch statement. Check out the documentation here for more examples.

You are switching based on val but it seems that you want to be switching based on input.Key, like this:

switch (input.Key)
{
    case ConsoleKey.NumPad1:
        string r = rnd.Next(stringEmployeeArray.Count()).ToString();
        int x = rnd.Next(intCarsSoldArray.Count());
        Console.WriteLine("This weeks employee of the week is(string)stringEmployeeArray[r] + (int)intCarsSoldArray[x]");
        break;
    case ConsoleKey.NumPad2:
        int sum = intCarsSoldArray.Sum();
        Console.WriteLine("Total cars sold for this week is(intCarsSoldArray.Sum)");
        break;
    case ConsoleKey.NumPad2:
        break;
}

Adding an error for when the use does anything but the numbers that you want is as simple as adding a default case:

switch (input.Key)
{
    ... //other cases
    case default:
        Console.WriteLine("You pressed a wrong key");
        break;
}

In order to write to disk I think the best way is to use FileStream along with StreamWriter. FileStream is used to open a stream to write to disk, and StreamWriter is used to write text to the underlying stream. Here is an example.

using(FileStream fs = new FileStream("OutputFile.txt"))
using(StreamWriter writer = new StreamWriter(fs))
{
    writer.WriteLine("This is the first line of the text file");
    writer.Write("No new line here");
}

The using clause ensures that the streams that you opened are closed properly after using them.

Not sure if this is part of the question but you aren't going to output any of the information because of the quote placement.

Console.WriteLine("This weeks employee of the week is(string)stringEmployeeArray[r] + (int)intCarsSoldArray[x]");

Should be

Console.WriteLine("This weeks employee of the week is " + stringEmployeeArray[r] + " " + intCarsSoldArray[x]);

and

Console.WriteLine("Total cars sold for this week is(intCarsSoldArray.Sum)");

Should be

Console.WriteLine("Total cars sold for this week is" + sum);

in order to output the information that you instead of just the plain string containing the code.

McAngus
  • 1,826
  • 18
  • 34
  • Thank you all. Having adopted McAgnus's edits and your comment about defining the count for the employee array - int r = rnd.Next(stringEmployeeArray.Count()); all is good except the program is still closing itself down if you put in a number ( don't even hit Enter!). I have the console.read below every case. I have commented out the data write function until the other parts are sorted. How can I fix it so the program does take in the num input – Hugi Husser Apr 04 '18 at 14:46
  • You need to put the whole program in a loop if you want to run it multiple times. Check out this link for some examples on that: https://stackoverflow.com/questions/1754282/how-to-loop-a-console-app – McAngus Apr 04 '18 at 14:49
  • Yes if I want to switch back to the menu and perform other functions, but at the moment I can't get any function to run, all I get is the number written to console and the app closing – Hugi Husser Apr 04 '18 at 14:53
  • Are you running this from visual studio? If so the window closes when the application terminates. To prevent this put another Console.ReadKey at the end of the program. – McAngus Apr 04 '18 at 14:56