C# In Practice - Building Tech Support App

One of the best ways to learn any programming language is, of course, through real practice. Starting from this article, in the next few posts, we will try to cover a number of topics of the C# language in real practice that remains obscure, and that many people are afraid to use or generally have difficulty determining where to use it.

  You can download the codes from here. Don't forget to enable migration as the app is built with EF code first logic.

PS : The codes are not yet final, they will be refactored as posts are written and additional functionality will be added to the system.

By reading this series of posts and examining the codes provided, you will find answers to the following questions,

  1. Where should I use the attributes?
  2. What are method extensions and their application in practice
  3. When did I create the interfaces?
  4. Where and why did I create abstract classes?
  5. How do I write neat code and how do I optimize?
  6. Importance of UserControls and places of creation
  7. Location of static members in the project
  8. Reflection in practice
  9. Events and practical usage of delegates and what else.

You can get acquainted with the technical task here.

The process of analysis and projection are not reflected in the article . Since the goal is to understand the listed topics of .NET, the issues were not considered from the architectural aspect.

Let’s get started:

Let's do the registration part first.

private void btn_Register_Click(object sender, EventArgs e) {
    User user = this.MapDataToModel("txbx_").WithDefaults();
    bool hasUser = true;
    if (this.IsValid(objectToValidate: user, errorsOn: lbl_error)) {
        TicketsDbContext tdc = null;
        try {
            tdc = new TicketsDbContext(); {
                if (!tdc.Users.HasUser(user.Email)) {
                    tdc.Users.Add(user);
                    tdc.SaveChanges();
                    hasUser = false;
                }
            }
            if (hasUser) MessageBox.Show("This user already exists!");
        } catch (Exception exp) {
            //TODO: write to log
            MessageBox.Show(exp.Message);
        } finally {
            tdc.Dispose();
        }
    }
}

The steps involved in the registration process are as follows,

  1. Information received from the user is modeled.
  2. The model is validated
  3. If everything is in order, it is checked whether there is such a user in the database.
  4. If there is no user, it is registered

We prepare the registration window as a separate User Control. This will help to reuse that control.

We write a mini automapper extension in order not to write the collection of textbox data entered from the form to the class every time. Through this extension, we collect the textbox data in the form into a class.

public static T MapDataToModel(this Form form, string withPrefix) {
    var typeProperties = typeof(T).GetProperties();
    var allTextBoxes = GetAllTextBoxes(form.Controls);
    T obj = Activator.CreateInstance();
    foreach(var item in typeProperties) {
        string propertyValue = allTextBoxes.Where(x => x.Name == $ "{withPrefix}{item.Name}").FirstOrDefault()?.Text;
        item.SetValue(obj, propertyValue);
    }
    return obj;
}

The working principle of the Extension method is very simple. It always finds the textboxes on the form, checking if their name matches the properties of the class to be mapped. As I customarily name textboxes with "txbx_", our method to write its own data to the Name property of the "txbx_Name" element requires an ignore part (here "txbx_") and an extension called WithDefaults() to populate the members of the class that do not come from other textbox we have written a method.

public static User WithDefaults(this User registeredUser) {
    registeredUser.InvalidTry =
        default;
    registeredUser.IsActive = true;
    registeredUser.RegisterDate = DateTime.Now;
    registeredUser.RoleType =
        default;
    return registeredUser;
}

Since several forms (loginForm and RegisterForm) also check the correctness of the entered data, we have written a separate extension method. This method performs validation by calling EntityValidator (our class). If there are validation errors, then these errors are written in the text of the control we pass in the second argument.

public interface IValidator {
    IEnumerable Validate(object objecTToValidate);
}
public class EntityValidator: IValidator {
    public IEnumerable Validate(object objecTToValidate) {
        ValidationContext c = new ValidationContext(objecTToValidate, null, null);
        List errorResults = new List();
        if (!Validator.TryValidateObject(objecTToValidate, c, errorResults, true)) {
            return errorResults;
        } else return default (List);
    }
}
public static class FormExtensions {
    public static bool IsValid(this Form form, object objectToValidate, Control errorsOn) {
        EntityValidator entityValidator = new EntityValidator();
        errorsOn.Text = "";
        IEnumerable validationResults = entityValidator.Validate(objectToValidate);
        //if has error..
        if (validationResults != null) {
            DisplayErrors(form, validationResults, errorsOn);
            return false;
        } else return true;
    }
}

After successful validation, it is checked whether there is such an email in the database and finally the user is added to the database.

public static class DbSetExtensions {
    public static bool HasUser(this DbSet usersSet, string email) {
        User user = usersSet.Where(x => x.Email.Equals(email, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
        return user == null ? false : true;
    }
    public static bool HasUser(this DbSet usersSet, LoginModel lg) {
        User user = usersSet.Where(x => x.Email.Equals(lg.Email, StringComparison.OrdinalIgnoreCase) && x.Password.Equals(lg.Password, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
        return user == null ? false : true;
    }
}
private void btn_Register_Click(object sender, EventArgs e) {
    ..................
    if(this.IsValid(objectToValidate: user, errorsOn: lbl_error)) {
        TicketsDbContext tdc = null;
        try {
            tdc = new TicketsDbContext(); {
                if (!tdc.Users.HasUser(user.Email)) {
                    tdc.Users.Add(user);
                    tdc.SaveChanges();
                    hasUser = false;
                }
            }
            if (hasUser) MessageBox.Show("This user already exists!");
        } catch (Exception exp) {
            //TODO: write to log
            MessageBox.Show(exp.Message);
        } finally {
            tdc.Dispose();
        }
    }
}

C# in practice : Building tech support app


Similar Articles