I am using C# and the .NET entity framework, and I'm also learning how to better use OOP concepts in my program.  My code is all displayed below. I would like to ensure my logic is properly organized before I try to implement the next step in my program.My goal is twofold:Ensure my code is SOLID, OOP, reusable, etc.Enable my code to select via a menu, like the one that currently exists in program, which table it uses CRUD functions on.  Because have 2 entities in my entity model, and the code currently uses only the Man entity.  But I'm hoping I don't need additional copies of the CRUD functions for alternative entities.  I'm hoping that someone in StackExchange knows how to implement CRUD methods that will take any entity I give it.I think that my code is organized in an MVC fashion, where Program is the controller, UserInterface is the view, and DataAccess is the Model.Program classclass Program{    private enum MenuStage    {        MENU_EXIT,        MENU_CRUD        //MENU_TableSelect    }    static private MenuStage _MyMenuStage;    static void Main(string[] args)    {                  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);                MenuSwitch(iChoice);            }            if (iChoice == 0)            {                _MyMenuStage = MenuStage.MENU_EXIT;            }        }    }    static private void MenuSwitch(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;        UserInterface MyUI = new UserInterface();        DataAccess MyDA = new DataAccess();        do        {            bIsValidID = MyUI.GetValidTypeInput<int>(Enter ID: , Error: ID must be an integer, int.TryParse, out myID);        }        while (!bIsValidID);        sNewName = MyUI.GetInput<string>(Enter Name:, x => x.Trim());        MyDA.CreateMan(dbEntities, new Man() {ManID = myID, Name = sNewName });        SaveChanges(dbEntities);    }    static private void DoRead()    {        var dbEntities = new TestDatabaseEntities();        UserInterface MyUI = new UserInterface();        DataAccess MyDA = new DataAccess();        var query = from person in dbEntities.Men                    where true                    select person;        MyDA.ReadMan(dbEntities, query);    }    static private void DoUpdate()    {        int myID;        var dbEntities = new TestDatabaseEntities();        string sNewName = ;        UserInterface MyUI = new UserInterface();        DataAccess MyDA = new DataAccess();        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;        MyDA.UpdateMan(dbEntities, query, new Man() { ManID = myID, Name = sNewName });        SaveChanges(dbEntities);    }    static private void DoDelete()     {        int myID;        bool bValidInput;        var dbEntities = new TestDatabaseEntities();        UserInterface MyUI = new UserInterface();        DataAccess MyDA = new DataAccess();        do        {            bValidInput = MyUI.GetValidTypeInput<int>(Enter ID to delete: , ID Invalid, please re-enter, int.TryParse, out myID);        } while (!bValidInput);        var Query =            from person in dbEntities.Men            where person.ManID == myID            select person;        MyDA.DeleteMan(dbEntities, Query);        SaveChanges(dbEntities);    }    // ============================    // SAVECHANGES FUNCTION    // ============================    private static void SaveChanges(TestDatabaseEntities dbEntities)    {        DataAccess MyDA = new DataAccess();        MyDA.TryDataBase(dbEntities, Changes saved successfully, () => dbEntities.SaveChanges());    }}UserInterface Classpublic class UserInterface{    /// <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 input, out T output);    /// <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 GetValidTypeInput<T>(string message, string errorMessage, TryParse<T> TypeValidator, out T result, int upper = -1, int lower = -1)    {        bool bIsValid = false;        bool bTestValidRange = (upper != -1 && lower != -1);        DisplayPrompt(message);        bIsValid = TypeValidator(Console.ReadLine(), out result);        if (!bIsValid)            DisplayDBMessage(errorMessage);        if (bTestValidRange && bIsValid)        {            bIsValid = isValidRange(int.Parse(result.ToString()), lower, upper);            if (!bIsValid)                DisplayDBMessage(Input out of valid range);        }        return bIsValid;    }    public bool isValidRange(int item, int Lower, int Upper)      {         return (Lower <= item && item <= Upper);    }    // ============================    // DISPLAY FUNCTIONS    // ============================    public void DisplayDBMessage(string Msg)    {        Console.WriteLine(Msg);    }    public void DisplayPrompt(string Msg)    {        Console.Write(Msg);    }    public void DisplayRecord(string [] Msg)    {        if (Msg == null)            return;        foreach (string s in Msg)        {            Console.Write(s +  );        }        Console.Write(\n);     }    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);        }    }    public void DisplayDivider(char cCharacter = '|')    {        String sDivider = new String(cCharacter, 30);        DisplayDBMessage(sDivider);    }    // ============================    // MENU PROMPTING FOR USER INPUT    // ============================    public int GetMenuInput(string[] sChoices)    {        int selection;        bool bValid = false;                   UserInterface MyUI = new UserInterface();        DisplayMenuItems(sChoices);        do        {            bValid = (MyUI.GetValidTypeInput<int>(Enter> , Error: Numbers only> , int.TryParse, out selection, lower: 0, upper: sChoices.Length - 1));        } while (!bValid);        return selection;    }}DataAccess Classpublic class DataAccess{    // ============================    // CRUD FUNCTIONS for MAN TABLE    // ============================    public bool CreateMan(TestDatabaseEntities dbEntities, Man M)    {        return TryDataBase(dbEntities, Man created successfully,            () =>            {                dbEntities.Men.Add(new Man { ManID = M.ManID, Name = M.Name });            });    }    public bool UpdateMan(TestDatabaseEntities dbEntities, IQueryable<Man> query, Man man)    {        return TryDataBase(dbEntities, Man updated successfully,            () =>            {                foreach (Man M in query)                {                    M.Name = man.Name;                }            });    }    public bool DeleteMan(TestDatabaseEntities dbEntities, IQueryable myQuery)    {        return TryDataBase(dbEntities, Man deleted successfully,            () =>            {                foreach (Man M in myQuery)                {                    dbEntities.Men.Remove(M);                }            });    }    public bool ReadMan(TestDatabaseEntities dbEntities, IQueryable myQuery)    {        UserInterface MyUI = new UserInterface();        bool bSuccessful;        bSuccessful = TryDataBase(dbEntities, Records read successfully,            () =>            {                   MyUI.DisplayDivider();                foreach (Man m in myQuery)                {                    MyUI.DisplayRecord(new string[] { m.ManID.ToString(), m.Name });                }                MyUI.DisplayDivider();            });        return bSuccessful;    }    // ============================    // TRY FUNCTION    // ============================    public bool TryDataBase(TestDatabaseEntities MyDBEntities, string SuccessMessage,  Action MyDBAction)    {        UserInterface MyUI = new UserInterface();        try        {            MyDBAction();            MyUI.DisplayDBMessage(SuccessMessage);            return true;        }        catch (Exception e)        {            MyUI.DisplayDBMessage(e.ToString());            return false;        }    }}Man Class (auto-generated)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 (auto-generated)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; }}
Preparing code for more versatile CRUD functions
c#;entity framework;crud
A few comments on your code as follows :Your UI code should not contain a SaveChanges methods as per SRP, it should be in EF itself.Your UserInterface is actually just a utility method. Encapsulate your validationand prompt logic different places. Although Utility method is not considered as good but it is okay to use in this case although their responsibility should be defined properly. Your whole architecture is like UI => BL =>EF so EF layer should not interact with UI Code. As TryDataBase methods is showing the errors in prompt. This is not good idea as this concerns to UI not EF or BL. Throw known exception in case of error do not handle it but log it inside BLL or EF.I would advise you to write test cases even before writing any line of code. By writing unit test properly you can ensure that you followed the SOLID pattern or design pattern. So do this re-factoring part using Unit Test. By EOD eventually you will start following all applicable design pattern.Create a layer between EF and BLL to improve the code reuse (put them in separate projects, this will help you keep the code in the correct place).