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.  and adjusting my code has felt like a game of whack-a-mole. (if that makes any sense)

## My problem is I need: ##

 - clear separation of logic
 - code reusability
 - cleanliness
 - single responsibility principle
 - to make code that a real coding jedi master would produce.

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

Info on classes
-------

 - **Program** - I'm trying to put all controlling logic here
 - **DataAccess** - I'm trying to put my Data-accessing logic here, away from everything else.  
 - **UserInterface** - I'm trying to put my Interface logic here, away from everything else.  Such as the way information gets displayed to the user, the way info gets received from user.
 - **InputValidation** - I want to put all logic that validates user input here, ranging from Type validation to input range validation.
 - **DbResult** - a repository wrapper class containing a string message, and a boolean.  Used to report failure/success results with a message like "success!" or whatever exception.ToString() prints.  *I am thinking this class should be removed*?
 - **Man** - *auto-generated class by entity framework.*
 - **Location** - *auto-generated class by entity framework.* I plan to implement this class in my code as soon as I can get working clean logic for the CRUD functions that handle the Man class.

## Notes ##

 - I'd like to enable CRUD functions to handle more than one entity class, without copying and pasting 4 CRUD functions for each new entity.
 - I'm considering removing DbResult, but not sure how to without keeping logic seperate.

## Program Class ##

    class Program
    {
        private enum MenuStage
        {
            MENU_EXIT,
            MENU_CRUD
            //MENU_TableSelect
        }
        static private MenuStage _MyMenuStage;
        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);
                }
                DoUICycle();
            }
            Console.ReadLine();
        }

        // ================================
        // MENU DISPLAY AND PROCESSING
        // ================================
        private static void DoUICycle()
        {   
            string [] sCRUDMenu = new string[] {"Exit", "Create", "Read", "Update", "Delete" };
            string [] sTableMenu = new string[] {"Quit", "Men", "Locations"};
            string[] sMyMenuChoices;
            UserInterface MyUI = new UserInterface();
            int iChoice = -1;
            
            _MyMenuStage = MenuStage.MENU_CRUD;
            while (_MyMenuStage != MenuStage.MENU_EXIT)
            {
                if (_MyMenuStage == MenuStage.MENU_CRUD)
                {
                    sMyMenuChoices = sCRUDMenu;
                }
                else //if (MyStage == MenuStage.MENU_Tables)
                {
                    sMyMenuChoices = sTableMenu;
                }
                while (iChoice != 0)
                {
                    iChoice = MyUI.GetMenuInput(sMyMenuChoices);
                    DecideFromMenuChoice(iChoice);
                }
                if (iChoice == 0)
                {
                    _MyMenuStage = MenuStage.MENU_EXIT;
                }
            }
        }
        
        static private void DecideFromMenuChoice(int selection)
        {
            if (_MyMenuStage == MenuStage.MENU_CRUD)
            {
                switch (selection)
                {
                    case 1: DoCreate(); break;
                    case 2: DoRead(); break;
                    case 3: DoUpdate(); break;
                    case 4: DoDelete(); break;
                    default:
                        break;
                }
            }
            /* else if (_MyStage == MenuStage.MENU_TableSelect)
            {
                // should i really use enum? or IAmAnEntity interface variable
                switch (selection)
                {
                    // 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 bIsValidID;
            var dbEntities = new TestDatabaseEntities();
            string sNewName;

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

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

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

            CreationResult = MyDA.Create(new Man() {ManID = myID, Name = sNewName });
            _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 sNewName = _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 = sNewName;

                    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 sNewName = "";
            DataAccess MyDA = new DataAccess(dbEntities);
            DbResult UpdateResult, SaveResult;

            myID = _MyUI.GetInput<int>("Enter ID to update: ", int.Parse);
            sNewName = _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 bValidInput;
            var dbEntities = new TestDatabaseEntities();
            DataAccess MyDA = new DataAccess(dbEntities);
            DbResult DeleteResult, SaveResult;

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

            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);
        }

    }

## DataAccess Class ##

    public class DataAccess
    {
        private readonly TestDatabaseEntities _MyDBEntities;

        public DataAccess(TestDatabaseEntities entities)
        {
            _MyDBEntities = entities;
        }


        public DbResult DatabaseExists()
        {
            DbResult MyResult;

            if (_MyDBEntities.Database.Exists())
                MyResult = DbResult.Success("Database Found");
            else
                MyResult = DbResult.Failed("Database Not Found");

            return MyResult;
        }

        // ============================
        // CRUD FUNCTIONS for MAN TABLE
        // ============================

        public DbResult Create(Man M)
        {
            DbResult DbRes;

            try
            {
                _MyDBEntities.Men.Add(new Man { ManID = M.ManID, Name = M.Name });
                DbRes = DbResult.Success("Record created");
            }
            catch (Exception e)
            {
                DbRes = DbResult.Failed(e.ToString());   
            }            
            return DbRes;
        }

        public DbResult Update(IQueryable<Man> myQuery, Man man)
        {
            DbResult DbRes;

            try
            {
                foreach (Man M in myQuery)
                {
                    M.Name = man.Name;
                }
                DbRes = DbResult.Success("Record updated");
            }
            catch (Exception e)
            {
                DbRes = DbResult.Failed(e.ToString());   
            }

            return DbRes;
        }

        public DbResult Delete(IQueryable myQuery)
        {
            DbResult DbRes;

            try
            {
                foreach (Man M in myQuery)
                {
                    _MyDBEntities.Men.Remove(M);
                }
                DbRes = DbResult.Success("Record deleted");
            }
            catch (Exception e)
            {
                DbRes = DbResult.Failed(e.ToString());                
            }

            return DbRes;
        }

        public DbResult Read(IQueryable myQuery, out string[,] Records)
        {
            DbResult DbRes;
            Records = null;
            try
            {
                List<Man> men = myQuery.OfType<Man>().ToList();
                Records = new string[men.Count, 2];
                for (int i = 0; i < men.Count; i++)
                {
                    Records[i, 0] = men[i].ManID.ToString();
                    Records[i, 1] = men[i].Name;
                }
                DbRes = DbResult.Success("Read Success");
            }
            catch (Exception e)
            {
                DbRes = DbResult.Failed(e.ToString());
            }

            return DbRes;
        }

        // ============================
        // SAVECHANGES FUNCTION
        // ============================
        public DbResult SaveChanges()
        {
            DbResult DbRes;
            try
            {
                _MyDBEntities.SaveChanges();
                DbRes = DbResult.Success("Saved successfully");
            }
            catch (Exception e)
            {
                DbRes = DbResult.Failed(e.ToString());
            }

            return DbRes;
        }
    }

## UserInterface Class ##

    public class UserInterface
    {
        // ============================
        // INPUT FUNCTIONS
        // ============================
        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 TResult, int upper = -1, int lower = -1)
        {
            InputValidation MyInputValidator = new InputValidation();
            bool bIsValid = false;
            bool bTestValidRange = (upper != -1 && lower != -1);
            string sInput;
            
            sInput = GetInput(message, x => (x.Trim()));
            
            bIsValid = MyInputValidator.ValidateInputType(sInput, TypeValidator, out TResult);

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

            if (bTestValidRange)
                bIsValid = MyInputValidator.ValidateInputRange(ref TResult, lower, upper);

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

            return bIsValid;
        }

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

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

        // ============================
        // INPUT/OUTPUT
        // ============================
        public int GetMenuInput(string[] sChoices)
        {
            int selection;
            bool bValid = false;
            int LowerBound = 0;
            int UpperBound = sChoices.Length - 1;

            DisplayMenuItems(sChoices);

            do
            {
                bValid = (GetValidInput<int>("Enter> ", int.TryParse, out selection, lower: LowerBound, upper: UpperBound));
            } while (!bValid);
            return selection;
        }
    }

## InputValidation Class ##

    public class InputValidation//<T> where T: class
    {
        /// <summary>
        /// Delegate that matches the signature of TryParse, method defined for all primitives.
        /// </summary>
        /// <typeparam name="T">Output type of This Delegate</typeparam>
        /// <param name="input">input for this Delegate to translate to type T</param>
        /// <param name="output">The translated variable to return via out parameter</param>
        /// <returns>Whether the Parse was successful or not, and output as output</returns>
        public delegate bool TryParse<T>(string sInput, out T output);
        public bool ValidateInputType<T>(string sInput, TryParse<T> TypeValidator, out T result)
        {
            return TypeValidator(sInput, out result);
        }
        public bool ValidateInputRange<T>(ref T result, int lower, int upper)
        {
            // How can I use relational operators like > < = on T? without forcing T result to an int?
            return isValidRange(int.Parse(result.ToString()), lower, upper);
        }
        public bool isValidRange(int item, int Lower, int Upper)
        {
            return (Lower <= item && item <= Upper);
        }
    }

## DbResult (wrapper class) ##

    public class DbResult
    {
        public bool bSuccess { get; private set; }
        public String Message { get; private set; }

        private DbResult(bool success, string message)
        {
            this.bSuccess = success;
            this.Message = message;
        }

        public static DbResult Failed(string message)
        {
            return new DbResult(false, message);
        }

        public static DbResult Success(string message)
        {
            return new DbResult(true, message);
        }
    }

## Man Class ##

    public partial class Man
    {
        public Man()
        {
            this.Locations = new HashSet<Location>();
        }
    
        public int ManID { get; set; }
        public string Name { get; set; }
    
        public virtual ICollection<Location> Locations { get; set; }
    }

## Location Class ##

    public partial class Location
    {
        public Location()
        {
            this.Men = new HashSet<Man>();
        }
    
        public int PlaceID { get; set; }
        public string Place { get; set; }
    
        public virtual ICollection<Man> Men { get; set; }
    }