Skip to main content
Notice removed Draw attention by Matt Rohde
Bounty Ended with Cohen's answer chosen by Matt Rohde
Tweeted twitter.com/#!/StackCodeReview/status/273105655232094208
Notice added Draw attention by Matt Rohde
Bounty Started worth 50 reputation by Matt Rohde
added 113 characters in body
Source Link
Matt Rohde
  • 455
  • 2
  • 6
  • 17

In my previous questions, I feel I have repeatedly been making the mistake of posting portions of my code that I felt was relevant to the immediate problem I was facing, and as a result, I suspect that people weren't seeing the big picture of the problem with my code overall.

My problem is I need: to know

  • clear separation of logic
  • is my code reusability
  • cleanlinessin my CRUD functions structured well and effectively? I plan to implement CRUD for Locations entity too. hopefully without redundant code.
  • single responsibility principleyou see how one CRUD function in the program class calls the other CRUD function in the DataAccess Class? I"m trying to set it up so that i can control which entity gets edited or read in the control class, and access it in the dataAccess class. Is this bad logic?

So I'm posting all my code with formatting to make it much easier to navigate.

class Program
{
    static private UserInterface _MyUI; 
    static void Main(string[] args)
    {
        DbResult resultSQLRunning;
        ServiceManager mySM = new ServiceManager();
        _MyUI = new UserInterface();
        
        resultSQLRunning = mySM.SQLRunning();

        _MyUI.DisplayMessage(resultSQLRunning.Message);

        if (resultSQLRunning.bSuccess)
        {
            using (TestDatabaseEntities myDB = new TestDatabaseEntities())
            {
                DataAccess myDA = new DataAccess(myDB);
                DbResult myResult = myDA.DatabaseExists();
                _MyUI.DisplayMessage(myResult.Message);
            }
            do
            {
                DecideFromMenuChoice(_MyUI.GetUserAction());
            } while (_MyUI._MyMenuStage != UserInterface.MenuStage.MENU_EXIT);
        }
        Console.ReadLine(); // press any key to exit, basically.
    }
    
    static private void DecideFromMenuChoice(int choice)
    {
        if (_MyUI._MyMenuStage == UserInterface.MenuStage.MENU_CRUD)
        {
            switch (choice)
            {
                case 1: DoCreate(); break;
                case 2: DoRead(); break;
                case 3: DoUpdate(); break;
                case 4: DoDelete(); break;
                default:
                    break;
            }
        }
        /* else if (_MyUI._MyMenuStage == UserInterface.MenuStage.MENU_TABLE)
        {
            switch (choice)
            {
                // not sure what to do here, somehow select a table to be used in CRUD functions? 
                // do i need to call more CRUD functions for each new table I add?
            }
        }*/
    }
    // ============================
    // CRUD FUNCTIONS
    // ============================
    static private void DoCreate()
    {
        int myID;
        bool isValidID;
        var dbEntities = new TestDatabaseEntities();
        string newName;

        DataAccess MyDA = new DataAccess(dbEntities);
        DbResult CreationResult, SaveResult;

        do
        {
            isValidID = _MyUI.GetValidInput<int>("Enter ID: ", int.TryParse, out myID);
        }
        while (!isValidID);

        newName = _MyUI.GetInput<string>("Enter Name:", x => x.Trim());

        CreationResult = MyDA.Create(new Man() {ManID = myID, Name = newName });
        _MyUI.DisplayMessage(CreationResult.Message);

        if (!CreationResult.bSuccess)
            return;

        SaveResult = MyDA.SaveChanges();
        _MyUI.DisplayMessage(SaveResult.Message);
    }
    static private void DoRead()
    {
        var dbEntities = new TestDatabaseEntities();
        DataAccess myDA = new DataAccess(dbEntities);
        string [,] records;
        DbResult readResult;

        var query = from person in dbEntities.Men
                    where true
                    select person;
        readResult = myDA.Read(query, out records);
        if (readResult.bSuccess)
        {
            _MyUI.DisplayRecords(records);
        }
        if (!readResult.bSuccess)
            _MyUI.DisplayMessage(readResult.Message);
    }
    static private void DoUpdate()
    {
        /* The code in this DoUpdate function is "simplified" and "cleaner" 
         * than the commented block below, I'm told. is it?*/
        int myID = _MyUI.GetInput<int>("Enter ID to update: ", int.Parse);
        string newName = _MyUI.GetInput<string>("Enter new name: ", x => x.Trim());

        try
        {
            using (var dbEntities = new TestDatabaseEntities())
            {
                //TODO: If ManID is unique then we can use FirstOrDefault here.
                var allMatchingMen =
                    from person in dbEntities.Men
                    where person.ManID == myID
                    select person;

                foreach (var man in allMatchingMen)
                    man.Name = newName;

                dbEntities.SaveChanges();
                _MyUI.DisplayMessage("Record(s) updated");
            }
        }
        /*catch (OptimisticConcurrencyException ex)
        {
            //TODO: add auto-retry logic
            _MyUI.DisplayMessage("Someone updated the record, let's retry");
        }*/
        catch (Exception ex)
        {
            _MyUI.DisplayMessage("Something went wrong, could not update");
        }
    } 
    /*{
        int myID;
        var dbEntities = new TestDatabaseEntities();
        string newName = "";
        DataAccess MyDA = new DataAccess(dbEntities);
        DbResult updateResult, saveResult;

        myID = _MyUI.GetInput<int>("Enter ID to update: ", int.Parse);
        newName = _MyUI.GetInput<string>("Enter new name: ", x => x.Trim());

        var query =
            from person in dbEntities.Men
            where person.ManID == myID
            select person;

        updateResult = myDA.Update(query, new Man() { ManID = myID, Name = sNewName });
        _MyUI.DisplayMessage(updateResult.Message);

        if (!UpdateResult.bSuccess)
            return;

        SaveResult = myDA.SaveChanges();
        _MyUI.DisplayMessage(saveResult.Message);
    }*/
    static private void DoDelete() 
    {
        int myID;
        bool isValidInput;
        var dbEntities = new TestDatabaseEntities();
        DataAccess myDA = new DataAccess(dbEntities);
        DbResult deleteResult, saveResult;

        do
        {
            isValidInput = _MyUI.GetValidInput<int>("Enter ID to delete: ", int.TryParse, out myID);
        } while (!isValidInput);

        var Query =
            from person in dbEntities.Men
            where person.ManID == myID
            select person;

        deleteResult = myDA.Delete(Query);
        _MyUI.DisplayMessage(deleteResult.Message);

        if (!deleteResult.bSuccess)
            return;

        saveResult = myDA.SaveChanges();
        _MyUI.DisplayMessage(saveResult.Message);
    }
}

In my previous questions, I feel I have repeatedly been making the mistake of posting portions of my code that I felt was relevant to the immediate problem I was facing, and as a result, I suspect that people weren't seeing the big picture of the problem with my code overall.

My problem is I need:

  • clear separation of logic
  • code reusability
  • cleanliness
  • single responsibility principle

So I'm posting all my code with formatting to make it much easier to navigate.

class Program
{
    static private UserInterface _MyUI; 
    static void Main(string[] args)
    {
        DbResult resultSQLRunning;
        ServiceManager mySM = new ServiceManager();
        _MyUI = new UserInterface();
        
        resultSQLRunning = mySM.SQLRunning();

        _MyUI.DisplayMessage(resultSQLRunning.Message);

        if (resultSQLRunning.bSuccess)
        {
            using (TestDatabaseEntities myDB = new TestDatabaseEntities())
            {
                DataAccess myDA = new DataAccess(myDB);
                DbResult myResult = myDA.DatabaseExists();
                _MyUI.DisplayMessage(myResult.Message);
            }
            do
            {
                DecideFromMenuChoice(_MyUI.GetUserAction());
            } while (_MyUI._MyMenuStage != UserInterface.MenuStage.MENU_EXIT);
        }
        Console.ReadLine(); // press any key to exit, basically.
    }
    
    static private void DecideFromMenuChoice(int choice)
    {
        if (_MyUI._MyMenuStage == UserInterface.MenuStage.MENU_CRUD)
        {
            switch (choice)
            {
                case 1: DoCreate(); break;
                case 2: DoRead(); break;
                case 3: DoUpdate(); break;
                case 4: DoDelete(); break;
                default:
                    break;
            }
        }
        /* else if (_MyUI._MyMenuStage == UserInterface.MenuStage.MENU_TABLE)
        {
            switch (choice)
            {
                // not sure what to do here, somehow select a table to be used in CRUD functions? 
                // do i need to call more CRUD functions for each new table I add?
            }
        }*/
    }
    // ============================
    // CRUD FUNCTIONS
    // ============================
    static private void DoCreate()
    {
        int myID;
        bool isValidID;
        var dbEntities = new TestDatabaseEntities();
        string newName;

        DataAccess MyDA = new DataAccess(dbEntities);
        DbResult CreationResult, SaveResult;

        do
        {
            isValidID = _MyUI.GetValidInput<int>("Enter ID: ", int.TryParse, out myID);
        }
        while (!isValidID);

        newName = _MyUI.GetInput<string>("Enter Name:", x => x.Trim());

        CreationResult = MyDA.Create(new Man() {ManID = myID, Name = newName });
        _MyUI.DisplayMessage(CreationResult.Message);

        if (!CreationResult.bSuccess)
            return;

        SaveResult = MyDA.SaveChanges();
        _MyUI.DisplayMessage(SaveResult.Message);
    }
    static private void DoRead()
    {
        var dbEntities = new TestDatabaseEntities();
        DataAccess myDA = new DataAccess(dbEntities);
        string [,] records;
        DbResult readResult;

        var query = from person in dbEntities.Men
                    where true
                    select person;
        readResult = myDA.Read(query, out records);
        if (readResult.bSuccess)
        {
            _MyUI.DisplayRecords(records);
        }
        if (!readResult.bSuccess)
            _MyUI.DisplayMessage(readResult.Message);
    }
    static private void DoUpdate()
    {
        int myID = _MyUI.GetInput<int>("Enter ID to update: ", int.Parse);
        string newName = _MyUI.GetInput<string>("Enter new name: ", x => x.Trim());

        try
        {
            using (var dbEntities = new TestDatabaseEntities())
            {
                //TODO: If ManID is unique then we can use FirstOrDefault here.
                var allMatchingMen =
                    from person in dbEntities.Men
                    where person.ManID == myID
                    select person;

                foreach (var man in allMatchingMen)
                    man.Name = newName;

                dbEntities.SaveChanges();
                _MyUI.DisplayMessage("Record(s) updated");
            }
        }
        /*catch (OptimisticConcurrencyException ex)
        {
            //TODO: add auto-retry logic
            _MyUI.DisplayMessage("Someone updated the record, let's retry");
        }*/
        catch (Exception ex)
        {
            _MyUI.DisplayMessage("Something went wrong, could not update");
        }
    } 
    /*{
        int myID;
        var dbEntities = new TestDatabaseEntities();
        string newName = "";
        DataAccess MyDA = new DataAccess(dbEntities);
        DbResult updateResult, saveResult;

        myID = _MyUI.GetInput<int>("Enter ID to update: ", int.Parse);
        newName = _MyUI.GetInput<string>("Enter new name: ", x => x.Trim());

        var query =
            from person in dbEntities.Men
            where person.ManID == myID
            select person;

        updateResult = myDA.Update(query, new Man() { ManID = myID, Name = sNewName });
        _MyUI.DisplayMessage(updateResult.Message);

        if (!UpdateResult.bSuccess)
            return;

        SaveResult = myDA.SaveChanges();
        _MyUI.DisplayMessage(saveResult.Message);
    }*/
    static private void DoDelete() 
    {
        int myID;
        bool isValidInput;
        var dbEntities = new TestDatabaseEntities();
        DataAccess myDA = new DataAccess(dbEntities);
        DbResult deleteResult, saveResult;

        do
        {
            isValidInput = _MyUI.GetValidInput<int>("Enter ID to delete: ", int.TryParse, out myID);
        } while (!isValidInput);

        var Query =
            from person in dbEntities.Men
            where person.ManID == myID
            select person;

        deleteResult = myDA.Delete(Query);
        _MyUI.DisplayMessage(deleteResult.Message);

        if (!deleteResult.bSuccess)
            return;

        saveResult = myDA.SaveChanges();
        _MyUI.DisplayMessage(saveResult.Message);
    }
}

My problem is I need to know

  • is my code in my CRUD functions structured well and effectively? I plan to implement CRUD for Locations entity too. hopefully without redundant code.
  • you see how one CRUD function in the program class calls the other CRUD function in the DataAccess Class? I"m trying to set it up so that i can control which entity gets edited or read in the control class, and access it in the dataAccess class. Is this bad logic?
class Program
{
    static private UserInterface _MyUI; 
    static void Main(string[] args)
    {
        DbResult resultSQLRunning;
        ServiceManager mySM = new ServiceManager();
        _MyUI = new UserInterface();
        
        resultSQLRunning = mySM.SQLRunning();

        _MyUI.DisplayMessage(resultSQLRunning.Message);

        if (resultSQLRunning.bSuccess)
        {
            using (TestDatabaseEntities myDB = new TestDatabaseEntities())
            {
                DataAccess myDA = new DataAccess(myDB);
                DbResult myResult = myDA.DatabaseExists();
                _MyUI.DisplayMessage(myResult.Message);
            }
            do
            {
                DecideFromMenuChoice(_MyUI.GetUserAction());
            } while (_MyUI._MyMenuStage != UserInterface.MenuStage.MENU_EXIT);
        }
        Console.ReadLine(); // press any key to exit, basically.
    }
    
    static private void DecideFromMenuChoice(int choice)
    {
        if (_MyUI._MyMenuStage == UserInterface.MenuStage.MENU_CRUD)
        {
            switch (choice)
            {
                case 1: DoCreate(); break;
                case 2: DoRead(); break;
                case 3: DoUpdate(); break;
                case 4: DoDelete(); break;
                default:
                    break;
            }
        }
        /* else if (_MyUI._MyMenuStage == UserInterface.MenuStage.MENU_TABLE)
        {
            switch (choice)
            {
                // not sure what to do here, somehow select a table to be used in CRUD functions? 
                // do i need to call more CRUD functions for each new table I add?
            }
        }*/
    }
    // ============================
    // CRUD FUNCTIONS
    // ============================
    static private void DoCreate()
    {
    int myID;
        bool isValidID;
        var dbEntities = new TestDatabaseEntities();
        string newName;

        DataAccess MyDA = new DataAccess(dbEntities);
        DbResult CreationResult, SaveResult;

        do
        {
            isValidID = _MyUI.GetValidInput<int>("Enter ID: ", int.TryParse, out myID);
        }
        while (!isValidID);

        newName = _MyUI.GetInput<string>("Enter Name:", x => x.Trim());

        CreationResult = MyDA.Create(new Man() {ManID = myID, Name = newName });
        _MyUI.DisplayMessage(CreationResult.Message);

        if (!CreationResult.bSuccess)
            return;

        SaveResult = MyDA.SaveChanges();
        _MyUI.DisplayMessage(SaveResult.Message);
    }
    static private void DoRead()
    {
        var dbEntities = new TestDatabaseEntities();
        DataAccess myDA = new DataAccess(dbEntities);
        string [,] records;
        DbResult readResult;

        var query = from person in dbEntities.Men
                    where true
                    select person;
        readResult = myDA.Read(query, out records);
        if (readResult.bSuccess)
        {
            _MyUI.DisplayRecords(records);
        }
        if (!readResult.bSuccess)
            _MyUI.DisplayMessage(readResult.Message);
    }
    static private void DoUpdate()
    {
        /* The code in this DoUpdate function is "simplified" and "cleaner" 
         * than the commented block below, I'm told. is it?*/
        int myID = _MyUI.GetInput<int>("Enter ID to update: ", int.Parse);
        string newName = _MyUI.GetInput<string>("Enter new name: ", x => x.Trim());

        try
        {
            using (var dbEntities = new TestDatabaseEntities())
            {
                //TODO: If ManID is unique then we can use FirstOrDefault here.
                var allMatchingMen =
                    from person in dbEntities.Men
                    where person.ManID == myID
                    select person;

                foreach (var man in allMatchingMen)
                    man.Name = newName;

                dbEntities.SaveChanges();
                _MyUI.DisplayMessage("Record(s) updated");
            }
        }
        /*catch (OptimisticConcurrencyException ex)
        {
            //TODO: add auto-retry logic
            _MyUI.DisplayMessage("Someone updated the record, let's retry");
        }*/
        catch (Exception ex)
        {
            _MyUI.DisplayMessage("Something went wrong, could not update");
        }
    } 
    /*{
        int myID;
        var dbEntities = new TestDatabaseEntities();
        string newName = "";
        DataAccess MyDA = new DataAccess(dbEntities);
        DbResult updateResult, saveResult;

        myID = _MyUI.GetInput<int>("Enter ID to update: ", int.Parse);
        newName = _MyUI.GetInput<string>("Enter new name: ", x => x.Trim());

        var query =
            from person in dbEntities.Men
            where person.ManID == myID
            select person;

        updateResult = myDA.Update(query, new Man() { ManID = myID, Name = sNewName });
        _MyUI.DisplayMessage(updateResult.Message);

        if (!UpdateResult.bSuccess)
            return;

        SaveResult = myDA.SaveChanges();
        _MyUI.DisplayMessage(saveResult.Message);
    }*/
    static private void DoDelete() 
    {
        int myID;
        bool isValidInput;
        var dbEntities = new TestDatabaseEntities();
        DataAccess myDA = new DataAccess(dbEntities);
        DbResult deleteResult, saveResult;

        do
        {
            isValidInput = _MyUI.GetValidInput<int>("Enter ID to delete: ", int.TryParse, out myID);
        } while (!isValidInput);

        var Query =
            from person in dbEntities.Men
            where person.ManID == myID
            select person;

        deleteResult = myDA.Delete(Query);
        _MyUI.DisplayMessage(deleteResult.Message);

        if (!deleteResult.bSuccess)
            return;

        saveResult = myDA.SaveChanges();
        _MyUI.DisplayMessage(saveResult.Message);
    }
}
added 968 characters in body
Source Link
Matt Rohde
  • 455
  • 2
  • 6
  • 17
public class UserInterface
{
    public enum MenuStage
    {
        MENU_EXIT,
        MENU_CRUD,
        MENU_TableSelect
    }
    public MenuStage _MyMenuStage{ get; private set; }

    public UserInterface()
    {
        _MyMenuStage = MenuStage.MENU_CRUD;
    }
    /// <summary>
    /// Prompts user for input with given message, and converts input to type T
    /// </summary>
    /// <typeparam name="T">Value type to convert to, and return</typeparam>
    /// <param name="message">Message to be printed to console</param>
    /// <param name="transform">The type conversion function to use on user's input</param>
    /// <returns>Type T</returns>
    public T GetInput<T>(string message, Converter<string, T> transform)
    {
        DisplayPrompt(message);
        return transform(Console.ReadLine());
    }
    /// <summary>
    /// Asks the user for valid input
    /// </summary>
    /// <typeparam name="T">The type of result to return as out parameter</typeparam>
    /// <param name="message">The message to prompt the user with</param>
    /// <param name="errorMessage">The message to Display to user with if input is invalid</param>
    /// <param name="typeValidator">The TryParse function to use to test the input.</param>
    /// <returns>True if input is valid as per function given to TypeValidator, Result as type T</returns>
    public bool GetValidInput<T>(string message, InputValidation.TryParse<T> typeValidator, out T result, int upper = -1, int lower = -1)
    {
        InputValidation myInputValidator = new InputValidation();
        bool isValid = false;
        bool shouldTestRange = (upper != -1 && lower != -1);
        string input;
        
        input = GetInput(message, x => (x.Trim()));
        
        isValid = myInputValidator.ValidateInputType(input, typeValidator, out result);

        if (!isValid)
        {
            DisplayMessage("Error, invalid input type entered, type required is " + typeof(T).ToString());
            return false;
        }

        if (shouldTestRange)
            isValid = myInputValidator.ValidateInputRange(ref result, lower, upper);

        if (!isValid)
        {
            DisplayMessage("Error, input is out of range.  Range: " + lower.ToString() + " - " + upper.ToString());
            return false;
        }

        return isValid;
    }

    // ============================
    // OUTPUT FUNCTIONS
    // ============================
    public void DisplayMessage(string message)
    {
        Console.WriteLine(message);
    }
    public void DisplayPrompt(string message)
    {
        Console.Write(message);
    }
    public void DisplayRecords(string[,] message)
    {
        if (message == null)
            return;

        DisplayDivider();
        for (int i = 0; i < message.GetLength(0); i++)
        {
            for (int j = 0; j < message.GetLength(1); j++)
            {
                Console.Write(message[i, j] + " ");
            }
            Console.Write("\n"); 
        }
        DisplayDivider();
    }
    public void DisplayMenuOptions(string[] items)
    { 
        byte index = 0;
        DisplayDivider('~');
        Console.WriteLine("Select an action from menu");
        foreach (string s in items)
        {
            Console.WriteLine(index++ + ") " + s);
        }
        DisplayDivider('~');
    }
    public void DisplayDivider(char myChar = '|')
    {
        String myDivider = new String(myChar, 30);
        
        DisplayMessage(myDivider);
    }

    // ============================
    // INPUT/OUTPUT
    // ============================
    private int GetMenuInput(string[] myOptions)
    {
        int choice;
        bool isValid = false;
        int lowerBound = 0;
        int upperBound = myOptions.Length - 1;

        DisplayMenuOptions(myOptions);

        do
        {
            isValid = (GetValidInput<int>("Enter choice> ", int.TryParse, out choice, lower: lowerBound, upper: upperBound));
        } while (!isValid);
        return choice;
    }

    public int GetUserAction()
    {
        string[] menuOptions;
        int choice = -1;

        //TODO: implement logic that Chooses which MenuStage we're at.

        menuOptions = DecideMenuOptions();
        DisplayMenuOptions(menuOptions);

        choice = GetMenuInputGetInputFromRange(0, menuOptions.Length - 1);

        if (choice == 0)
        {
            _MyMenuStage = MenuStage.MENU_EXIT;
        }

        return choice;
    }

    private int GetInputFromRange(int lowerBound, int upperBound)
    {
        int choice;
        bool isValid = false;

        do
        {
            isValid = (GetValidInput<int>("Enter choice> ", int.TryParse, out choice, lower: lowerBound, upper: upperBound));
        } while (!isValid);
        return choice;
    }

    /// <summary>
    /// Determines which menu options need to be displayed
    /// </summary>
    /// <returns>Menu options that need to be displayed</returns>
    private string[] DecideMenuOptions()
    {
        string[] menuCRUD = new string[] { "Exit", "Create", "Read", "Update", "Delete" };
        string[] menuTables = new string[] { "Quit", "Men", "Locations" };
        string[] myMenuChoices = null;
        
        if (_MyMenuStage == MenuStage.MENU_CRUD)
        {
            myMenuChoices = menuCRUD;
        }
        else if (_MyMenuStage == MenuStage.MENU_TableSelect)
        {
            myMenuChoices = menuTables;
        }

        return myMenuChoices;
    }
}

11/20/12212 9:01 CST: Improved seperation of logic in UserInterface class and Program Class. At least I think so. 11/21/12 6:46 CST: Improved UserInterface class slightly.

public class UserInterface
{
    public enum MenuStage
    {
        MENU_EXIT,
        MENU_CRUD,
        MENU_TableSelect
    }
    public MenuStage _MyMenuStage{ get; private set; }

    public UserInterface()
    {
        _MyMenuStage = MenuStage.MENU_CRUD;
    }

    public T GetInput<T>(string message, Converter<string, T> transform)
    {
        DisplayPrompt(message);
        return transform(Console.ReadLine());
    }
    public bool GetValidInput<T>(string message, InputValidation.TryParse<T> typeValidator, out T result, int upper = -1, int lower = -1)
    {
        InputValidation myInputValidator = new InputValidation();
        bool isValid = false;
        bool shouldTestRange = (upper != -1 && lower != -1);
        string input;
        
        input = GetInput(message, x => (x.Trim()));
        
        isValid = myInputValidator.ValidateInputType(input, typeValidator, out result);

        if (!isValid)
        {
            DisplayMessage("Error, invalid input type entered, type required is " + typeof(T).ToString());
            return false;
        }

        if (shouldTestRange)
            isValid = myInputValidator.ValidateInputRange(ref result, lower, upper);

        if (!isValid)
        {
            DisplayMessage("Error, input is out of range.  Range: " + lower.ToString() + " - " + upper.ToString());
            return false;
        }

        return isValid;
    }

    // ============================
    // OUTPUT FUNCTIONS
    // ============================
    public void DisplayMessage(string message)
    {
        Console.WriteLine(message);
    }
    public void DisplayPrompt(string message)
    {
        Console.Write(message);
    }
    public void DisplayRecords(string[,] message)
    {
        if (message == null)
            return;

        DisplayDivider();
        for (int i = 0; i < message.GetLength(0); i++)
        {
            for (int j = 0; j < message.GetLength(1); j++)
            {
                Console.Write(message[i, j] + " ");
            }
            Console.Write("\n"); 
        }
        DisplayDivider();
    }
    public void DisplayMenuOptions(string[] items)
    { 
        byte index = 0;
        DisplayDivider('~');
        Console.WriteLine("Select an action from menu");
        foreach (string s in items)
        {
            Console.WriteLine(index++ + ") " + s);
        }
        DisplayDivider('~');
    }
    public void DisplayDivider(char myChar = '|')
    {
        String myDivider = new String(myChar, 30);
        
        DisplayMessage(myDivider);
    }

    // ============================
    // INPUT/OUTPUT
    // ============================
    private int GetMenuInput(string[] myOptions)
    {
        int choice;
        bool isValid = false;
        int lowerBound = 0;
        int upperBound = myOptions.Length - 1;

        DisplayMenuOptions(myOptions);

        do
        {
            isValid = (GetValidInput<int>("Enter choice> ", int.TryParse, out choice, lower: lowerBound, upper: upperBound));
        } while (!isValid);
        return choice;
    }

    public int GetUserAction()
    {
        string[] menuOptions;
        int choice = -1;

        //TODO: implement logic that Chooses which MenuStage we're at.

        menuOptions = DecideMenuOptions();

        choice = GetMenuInput(menuOptions);

        if (choice == 0)
        {
            _MyMenuStage = MenuStage.MENU_EXIT;
        }

        return choice;
    }

    private string[] DecideMenuOptions()
    {
        string[] menuCRUD = new string[] { "Exit", "Create", "Read", "Update", "Delete" };
        string[] menuTables = new string[] { "Quit", "Men", "Locations" };
        string[] myMenuChoices = null;
        
        if (_MyMenuStage == MenuStage.MENU_CRUD)
        {
            myMenuChoices = menuCRUD;
        }
        else if (_MyMenuStage == MenuStage.MENU_TableSelect)
        {
            myMenuChoices = menuTables;
        }

        return myMenuChoices;
    }
}

11/20/122 9:01 CST: Improved seperation of logic in UserInterface class and Program Class. At least I think so.

public class UserInterface
{
    public enum MenuStage
    {
        MENU_EXIT,
        MENU_CRUD,
        MENU_TableSelect
    }
    public MenuStage _MyMenuStage{ get; private set; }

    public UserInterface()
    {
        _MyMenuStage = MenuStage.MENU_CRUD;
    }
    /// <summary>
    /// Prompts user for input with given message, and converts input to type T
    /// </summary>
    /// <typeparam name="T">Value type to convert to, and return</typeparam>
    /// <param name="message">Message to be printed to console</param>
    /// <param name="transform">The type conversion function to use on user's input</param>
    /// <returns>Type T</returns>
    public T GetInput<T>(string message, Converter<string, T> transform)
    {
        DisplayPrompt(message);
        return transform(Console.ReadLine());
    }
    /// <summary>
    /// Asks the user for valid input
    /// </summary>
    /// <typeparam name="T">The type of result to return as out parameter</typeparam>
    /// <param name="message">The message to prompt the user with</param>
    /// <param name="errorMessage">The message to Display to user with if input is invalid</param>
    /// <param name="typeValidator">The TryParse function to use to test the input.</param>
    /// <returns>True if input is valid as per function given to TypeValidator, Result as type T</returns>
    public bool GetValidInput<T>(string message, InputValidation.TryParse<T> typeValidator, out T result, int upper = -1, int lower = -1)
    {
        InputValidation myInputValidator = new InputValidation();
        bool isValid = false;
        bool shouldTestRange = (upper != -1 && lower != -1);
        string input;
        
        input = GetInput(message, x => (x.Trim()));
        
        isValid = myInputValidator.ValidateInputType(input, typeValidator, out result);

        if (!isValid)
        {
            DisplayMessage("Error, invalid input type entered, type required is " + typeof(T).ToString());
            return false;
        }

        if (shouldTestRange)
            isValid = myInputValidator.ValidateInputRange(ref result, lower, upper);

        if (!isValid)
        {
            DisplayMessage("Error, input is out of range.  Range: " + lower.ToString() + " - " + upper.ToString());
            return false;
        }

        return isValid;
    }

    public void DisplayMessage(string message)
    {
        Console.WriteLine(message);
    }
    public void DisplayPrompt(string message)
    {
        Console.Write(message);
    }
    public void DisplayRecords(string[,] message)
    {
        if (message == null)
            return;

        DisplayDivider();
        for (int i = 0; i < message.GetLength(0); i++)
        {
            for (int j = 0; j < message.GetLength(1); j++)
            {
                Console.Write(message[i, j] + " ");
            }
            Console.Write("\n"); 
        }
        DisplayDivider();
    }
    public void DisplayMenuOptions(string[] items)
    { 
        byte index = 0;
        DisplayDivider('~');
        Console.WriteLine("Select an action from menu");
        foreach (string s in items)
        {
            Console.WriteLine(index++ + ") " + s);
        }
        DisplayDivider('~');
    }
    public void DisplayDivider(char myChar = '|')
    {
        String myDivider = new String(myChar, 30);
        
        DisplayMessage(myDivider);
    }

    public int GetUserAction()
    {
        string[] menuOptions;
        int choice = -1;

        //TODO: implement logic that Chooses which MenuStage we're at.

        menuOptions = DecideMenuOptions();
        DisplayMenuOptions(menuOptions);

        choice = GetInputFromRange(0, menuOptions.Length - 1);

        if (choice == 0)
        {
            _MyMenuStage = MenuStage.MENU_EXIT;
        }

        return choice;
    }

    private int GetInputFromRange(int lowerBound, int upperBound)
    {
        int choice;
        bool isValid = false;

        do
        {
            isValid = (GetValidInput<int>("Enter choice> ", int.TryParse, out choice, lower: lowerBound, upper: upperBound));
        } while (!isValid);
        return choice;
    }

    /// <summary>
    /// Determines which menu options need to be displayed
    /// </summary>
    /// <returns>Menu options that need to be displayed</returns>
    private string[] DecideMenuOptions()
    {
        string[] menuCRUD = new string[] { "Exit", "Create", "Read", "Update", "Delete" };
        string[] menuTables = new string[] { "Quit", "Men", "Locations" };
        string[] myMenuChoices = null;
        
        if (_MyMenuStage == MenuStage.MENU_CRUD)
        {
            myMenuChoices = menuCRUD;
        }
        else if (_MyMenuStage == MenuStage.MENU_TableSelect)
        {
            myMenuChoices = menuTables;
        }

        return myMenuChoices;
    }
}

11/20/12 9:01 CST: Improved seperation of logic in UserInterface class and Program Class. At least I think so. 11/21/12 6:46 CST: Improved UserInterface class slightly.

corrected date of edit
Source Link
Matt Rohde
  • 455
  • 2
  • 6
  • 17

11/20/122 9:01 CST: Improved seperation of logic in UserInterface class and Program Class. At least I think so.

9:01 CST: Improved seperation of logic in UserInterface class and Program Class. At least I think so.

11/20/122 9:01 CST: Improved seperation of logic in UserInterface class and Program Class. At least I think so.

Improved seperation of logic in UserInterface and Program class
Source Link
Matt Rohde
  • 455
  • 2
  • 6
  • 17
Loading
corrected some naming conventions
Source Link
Matt Rohde
  • 455
  • 2
  • 6
  • 17
Loading
added 118 characters in body
Source Link
Matt Rohde
  • 455
  • 2
  • 6
  • 17
Loading
Improved clarity of question
Source Link
Matt Rohde
  • 455
  • 2
  • 6
  • 17
Loading
Source Link
Matt Rohde
  • 455
  • 2
  • 6
  • 17
Loading