For the past couple of months I’ve been doing ASP.NET Core coding projects – building the backend and frontend of various websites for our customers. This has been a fun experience, but at times also very frustrating when things aren’t really working out. One thing is to follow examples and tutorials based on “perfect world” scenarios, another is to deal with real-world data and requirements. So this will be my first post in hopefully a series of ASP.NET Core posts to share some tips and tricks when things get a little more complicated. I assume you have some knowledge of MVC / ASP.NET already.

Disclaimer: Even though I have a developer background, building ASP Core MVC websites from the ground up is somewhat new to me, so don’t view this as the best approach, just one approach out of many to tackle the challenges. Also, these tips is just where I “hit the wall”, but could be common knowledge to you. Feel free to make a comment saying: “lol you could just add [insert some awesome code] to make it work!”

Context

The examples I will be showing here is based on an MVC website built in .NET Core 2.0 using standard CRUD (Create/Read/Update/Delete) operations to a predefined database. Though it doesn’t really matter if you are doing a code-first approach building the database from scratch or you have something already for these tips to work. This is one of the first real-world headaches you could say: because the database might have been designed for a different purpose in mind and does not necessarily fit into the purpose of your website – in other words: having consistent names/Id’s, proper relationships etc. should not be taken for granted ^^.

To reverse-engineer an existing database and turn the tables into model classes, you can simply use this command within Entity Framework:

Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=<Your Database>;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -o Models

 

Edit viewmodels in viewmodels

So lets say you have a page on your website that contains user input (Edit/Create view), but part of this user input is something that needs to be re-used again in different views. This “sub” – user input could be a class/model in it’s own – in my case it’s a Settings model with a lot of different setting properties.

To show this you could make a partial view that gets called or, in .NET Core, make a View Component. I will not go in details on how to make view components (but it’s explained in the linked post). This is all fine if you just want to show information, but if you want to send the information (the input) back to a post method in the controller and save it, you’ll need to understand some basics of how this is handled.

So to get some context. I have a Computer viewmodel that contains the model Settings:

image

And the base class contains these properties:

image

 

The Settings model contains hundreds of columns/properties (I did not design this Smile ) – that the user should be able to edit.

image

The view

So to expose these properties and the settings model in the view, we could simply do it like this:

image

Here I have Id as a hidden property, exposing the Description property to the user and the view component (named SettingsComputer conformed to kebab-case = settings-computer) is then called and will expose all the settings for the particular computer. Here’s how it looks:

image

image

The user is then able to enter values in the Description field (part of ComputerIdentityViewModel ) and the computer value fields (part of Settings (the sub viewmodel)). But what happens when you press save? ERROR!

The reason for this can be seen in the HTML code behind:

image

When we take a look at the input for UserDomain the name attribute is just called UserDomain and the Id attribute is UserDomain. So without any modification to the way the view component is called it looks like the property UserDomain is part of the ComputerIdentityViewModel – which is not good.

Here’s our post controller

image

The controller only has Id, Description and Settings property binded (and nothing else exist on the ViewModel). Sidenote: You would probably make some repository pattern, errorhandling etc., but for now it just uses Entity Framework to update and save to the database. I also use automapper for mapping between the viewmodel and the actual table-model in the data access layer.

But the question is – how do we map UserDomain to the settings property?

The answer is simply that we need to prefix the name attribute with the name of the sub-model and a dot. In my case Settings. – so we would like the html to look like this:

image

The important part here is the name attribute. It’s prefixed with Settings and a dot to indicate the sub propertyname.  But as you can see the id attribute does not contain a dot but an underscore _. You could make it into a dot, but it’s good practice not to have dots in the id attribute since jquery (and perhaps other javascript libraries) treat dot as a class selector unless you alter the syntax for grapping the id. As you will see when using inbuilt methods in the framework, it also underscores the id.

How do we solve it then? You have a few options to select from:

Option 1:

One quick way to solve this is simply to prefix html attributes by inserting the following code above your view component:

image

This will only apply to the current html section you insert into. It works, but I do lean more towards option 2, because with this you’ll have to remember to insert the prefix code every time the view component is called – hence the modularized / separation of concern principle is tampered a bit and room for error could arise. But again.. depends on the situation I guess.

Option 2:

This one require a little bit more of setup, but once it’s done you can simply call the view component like this and achieve the same:

image

As you can see my arguments are the same as in the view components (ignore the arguments/values, it’s just what I used here), but it’s not a html taghelper anymore. I would prefer it to be a taghelper to make it more consistent and flexible in terms of frontend development, but the tradeoff is that it’s easier to re-use on different pages.

First create a new View inside Shared -> EditorTemplates -> <NameOfYourView>. The EditorTemplates needs to be created and is a reserved name.

image

Within this view you simply place your view component:

image

The arguments is retrieved via @ViewData and it needs to be casted to the correct type you have defined.

Option 3:

I consider this more to be a work-around, but I still think it’s important to emphasize that when in trouble: javascript is your friend. I find myself often using javascript/jQuery to manipulate stuff on frontend level, probably because I don’t have the full vocabulary of what the asp framework can do. But I do know my way around using jquery Smile

What you do is to make a scripting section in your view like this:

image

And then make the logic for replacing the names and Id’s. Notice I am targeting a division within the html with the id templateSection.

image

You could also have the script in a seperate js file and then call it from the view:

<script defer="defer" src="yourscript.js"></script>

 

Remember to use defer to make sure other libraries like jQueries has been loaded before loading your script.

Dynamically listing properties in a view

As mentioned previously I had a table with 100+ columns. Making html for displaying this would take a long time and since the table could change in the future it would also create a reliability. So instead you can dynamically get the property names from the model and then list it.

Here’s how I did it using reflection. I’m also excluding some properties from the model (Id and Type) that I don’t want to show to the user. Behind the scene I have placed all my values for the settings inside a dictionary so I can quickly index on it and retrieve the value. The dictionary is placed in the viewbag.

@model DMSDAL.Models.Settings
@using System.Reflection

<table class="table table-striped">
        <thead>
            <tr>
                <th>
                    Setting
                </th>
                <th>
                    Template Value
                </th>
                <th>
                    Computer Value
                </th>
                <th>
                    Description
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
          <tr style="display:none">
              <td>
                  <input type="hidden" asp-for="Id" class="form-control settings-computer" />
                  <input type="hidden" asp-for="Type" class="form-control settings-computer" />
            </td>
          </tr>
@foreach (var prop in typeof(DMSDAL.Models.Settings).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead && p.Name != "Id" && p.Name != "Type"))
{
    string propName = prop.Name;
    string propNameWithPrefix = Html.Name(prop.Name);
    string propDisplayName = Html.DisplayName(prop.Name);
    string propValue = Html.Value(prop.Name);
    <tr>
        <td>
            @propDisplayName
        </td>
        @* ROLE COLUMN *@
            <td> 
                <input asp-for="@propNameWithPrefix" value="@if (@ViewBag.IsRole){ @ViewBag.RoleSettings[propName]}" class="form-control settings-template" disabled="disabled" readonly="readonly" />
            </td>
        <td>
            @Html.Editor(propName, new { htmlAttributes = new { @class = "form-control settings-computer" } })
            @Html.ValidationMessage(propName, "", new { @class = "text-danger" })         
        </td>
        <td>
            <text>@if (ViewBag.SettingsDescriptions.ContainsKey(propDisplayName)) { @ViewBag.SettingsDescriptions[propDisplayName] }</text>
        </td>
    </tr>
}

 

That’s it for now, hope you could use some of it 🙂

Until next time..