Model-View-Controller in ASP.NET Core

Model-View-Controller (MVC) is a design pattern used in ASP.NET Core and other frameworks to seperate responsibilities and to allow for parallel development. This pattern seperates the user interface (View) from methods that accepts and responds to input (Controller) and from data structures and classes that handles data processing (Models).

MVC makes views, controllers and models independent of each other. This means that developers can work simultaneously on different parts of the application and that these components can be reused in other projects.

A web application in ASP.NET Core application consists of a Program class, a StartUp class, middleware, models, views and controllers. The Program class is the entry point of the application and the StartUp class handles the configuration of the application. Middleware is set up to handle requests and responses on an application wide basis.

Model

A model can be a data structure or a class that handles data in the application. You can seperate data structures from classes that handles data in your application. Name data structures as models and name classes that handles data as repositories. A model is a data structure and it can have methods that makes it easier to get and set data for properties.

public class AdDocument
{
    #region Variables

    public string id { get; set; }
    public string model_type { get; set; }
    public string web_domain_name { get; set; }
    public Int32 sort_value { get; set; }
    public IDictionary<string, string> ads;

    #endregion

    #region Constructors

    public AdDocument()
    {
        // Set values for instance variables
        this.id = Guid.NewGuid().ToString();
        this.model_type = "ad";
        this.web_domain_name = "";
        this.sort_value = 100;
        this.ads = new Dictionary<string, string>();

    } // End of the constructor

    #endregion

    #region Get methods

    public string Get(string key)
    {
        // Create the string to return
        string value = "";

        // Check if the dictionary contains the key
        if (this.ads.ContainsKey(key))
        {
            value = this.ads[key];
        }

        // Return the post
        return value;

    } // End of the Get method

    public override string ToString()
    {
        return JsonConvert.SerializeObject(this);

    } // End of the ToString method

    #endregion

} // End of the class

Repository

A repository is a class that handles data in an application. A repository can be generic and it has methods that takes input and returns output. A repository can be used in a controller or another repository. Below is an example of a repository that handles Ads, it implements an interface.

public class AdRepository : IAdRepository
{
    #region Variables

    private readonly ICosmosDatabaseRepository cosmos_database_repository;

    #endregion

    #region Constructors

    public AdRepository(ICosmosDatabaseRepository cosmos_database_repository)
    {
        // Set values for instance variables
        this.cosmos_database_repository = cosmos_database_repository;

    } // End of the constructor

    #endregion

    #region Add methods

    public async Task<bool> Add(AdDocument item)
    {
        // Create a document
        return await this.cosmos_database_repository.Add<AdDocument>(item);

    } // End of the Add method

    #endregion

    #region Update methods

    public async Task<bool> Upsert(AdDocument item)
    {
        // Upsert a document
        return await this.cosmos_database_repository.Upsert<AdDocument>(item);

    } // End of the Upsert method

    public async Task<bool> Update(AdDocument item)
    {
        // Replace a document
        return await this.cosmos_database_repository.Update<AdDocument>(item.id, item);

    } // End of the Update method

    #endregion

    #region Get methods

    public async Task<ModelItem<AdDocument>> GetById(string id)
    {
        // Return the post
        return await this.cosmos_database_repository.GetById<AdDocument>(id, id);

    } // End of the GetById method

    public async Task<ModelPage<AdDocument>> GetByWebDomainName(string web_domain_name, string sort_field, string sort_order, Int32 page_size, string ct)
    {
        // Make sure that sort variables are valid
        sort_field = GetValidSortField(sort_field);
        sort_order = GetValidSortOrder(sort_order);

        // Create the sql string
        string sql = $"SELECT VALUE a FROM a WHERE a.model_type = @model_type AND a.web_domain_name = @web_domain_name ORDER BY a.{sort_field} {sort_order}";

        // Create parameters
        SqlParameterCollection parameters = new SqlParameterCollection()
        {
            new SqlParameter("@model_type", "ad"),
            new SqlParameter("@web_domain_name", web_domain_name),
        };

        // Return the tuple
        return await this.cosmos_database_repository.GetListByQuery<AdDocument>(sql, parameters, page_size, ct);

    } // End of the GetByWebDomainName method

    public async Task<ModelPage<AdDocument>> GetBySearch(string keywords, string sort_field, string sort_order, Int32 page_size, string ct)
    {
        // Make sure that sort variables are valid
        sort_field = GetValidSortField(sort_field);
        sort_order = GetValidSortOrder(sort_order);

        // Check if there is keywords
        bool keywords_exists = string.IsNullOrEmpty(keywords) == false ? true : false;

        // Create the sql string
        string sql = "SELECT VALUE a FROM a WHERE a.model_type = @model_type ";
        if(keywords_exists == true)
        {
            sql += $"AND a.web_domain_name = @keywords ";
        }
        sql += $"ORDER BY a.{sort_field} {sort_order}";
                
        // Create parameters
        SqlParameterCollection parameters = new SqlParameterCollection();
        parameters.Add(new SqlParameter("@model_type", "ad"));
        if (keywords_exists == true)
        {
            parameters.Add(new SqlParameter("@keywords", keywords));
        }

        // Return the list
        return await this.cosmos_database_repository.GetListByQuery<AdDocument>(sql, parameters, page_size, ct);

    } // End of the GetBySearch method

    #endregion

    #region Delete methods

    public async Task<bool> DeleteOnId(string id)
    {
        // Delete a document
        return await this.cosmos_database_repository.DeleteOnId(id, id);

    } // End of the DeleteOnId method

    #endregion

    #region Validation

    public string GetValidSortField(string sort_field)
    {
        // Make sure that the sort field is valid
        if (sort_field != "sort_value" && sort_field != "web_domain_name")
        {
            sort_field = "web_domain_name";
        }

        // Return the string
        return sort_field;

    } // End of the GetValidSortField method

    public string GetValidSortOrder(string sort_order)
    {
        // Make sure that the sort order is valid
        if (sort_order != "ASC" && sort_order != "DESC")
        {
            sort_order = "ASC";
        }

        // Return the string
        return sort_order;

    } // End of the GetValidSortOrder method

    #endregion

} // End of the class

Controller

A controller is a middleman between models and views. A controller is responsible of returning views on HttpGet requests and to handle HttpPost requests from views. A controller has http methods, it handles requests, processes data by using models and responds by returning views or other data. You can transfer data from a controller to a view by using ViewBag, ViewData or TempData. The folder name for views normally matches the name of the controller to which they belong (home). A method name in a controller often matches the name of a view (index corresponds to index.cshtml).

public class homeController : Controller
{
    #region Variables

    private readonly IStaticPageRepository static_page_repository;
    private readonly IGroupRepository group_repository;
    private readonly IFinalRepository final_repository;

    #endregion

    #region Constructors

    public homeController(IStaticPageRepository static_page_repository, IGroupRepository group_repository, IFinalRepository final_repository)
    {
        // Set values for instance variables
        this.static_page_repository = static_page_repository;
        this.group_repository = group_repository;
        this.final_repository = final_repository;

    } // End of the constructor

    #endregion

    #region View methods

    [HttpGet]
    public IActionResult index()
    {
        // Get the start page
        StaticPage staticPage = this.static_page_repository.GetOneByConnectionId(1);
        staticPage = staticPage != null ? staticPage : new StaticPage();

        // Set form values
        ViewBag.BreadCrumbs = new List<BreadCrumb>(0);
        ViewBag.StaticPage = staticPage;

        // Return the view
        return View();

    } // End of the index method

    [HttpGet]
    public IActionResult search()
    {
        // Create the bread crumb list
        List<BreadCrumb> breadCrumbs = new List<BreadCrumb>(2);
        breadCrumbs.Add(new BreadCrumb("Startsidan", "/"));
        breadCrumbs.Add(new BreadCrumb("Sökresultat", "/home/search"));

        // Set form values
        ViewBag.BreadCrumbs = breadCrumbs;

        // Return the view
        return View("search");

    } // End of the search method

    [HttpGet]
    public IActionResult page(string id = "")
    {
        // Get the static page
        StaticPage staticPage = this.static_page_repository.GetOneByPageName(id);

        // Make sure that the static page not is null
        if (staticPage == null)
        {
            return NotFound();
        }

        // Create the bread crumb list
        List<BreadCrumb> breadCrumbs = new List<BreadCrumb>(2);
        breadCrumbs.Add(new BreadCrumb("Startsidan", "/"));
        breadCrumbs.Add(new BreadCrumb(staticPage.link_name, "/home/page/" + staticPage.page_name));

        // Set form values
        ViewBag.BreadCrumbs = breadCrumbs;
        ViewBag.StaticPage = staticPage;

        // Return the view
        return View();

    } // End of the page method

    [HttpGet]
    public IActionResult group(string id = "")
    {
        // Get the group
        Group post = this.group_repository.GetOneByPageName(id);
            
        // Make sure that the post not is null
        if (post == null)
        {
            return NotFound();
        }

        // Create the bread crumb list
        List<BreadCrumb> breadCrumbs = new List<BreadCrumb>(2);
        breadCrumbs.Add(new BreadCrumb("Startsidan", "/"));
        breadCrumbs.Add(new BreadCrumb(post.title, "/home/group/" + post.page_name));

        // Set form values
        ViewBag.BreadCrumbs = breadCrumbs;
        ViewBag.Group = post;

        // Return the view
        return View();

    } // End of the group method

    [HttpGet]
    public IActionResult error(string id = "")
    {
        // Set the connection id
        byte connectionId = 2;
        if (id == "404")
        {
            connectionId = 3;
        }
        else if(id == "401")
        {
            connectionId = 4;
        }
        else
        {
            connectionId = 2;
        }
               
        // Get the error page
        StaticPage staticPage = this.static_page_repository.GetOneByConnectionId(connectionId);
        staticPage = staticPage != null ? staticPage : new StaticPage();

        // Create the bread crumb list
        List<BreadCrumb> breadCrumbs = new List<BreadCrumb>(2);
        breadCrumbs.Add(new BreadCrumb("Startsidan", "/"));
        breadCrumbs.Add(new BreadCrumb(staticPage.link_name, "/home/error/" + id.ToString()));

        // Set form values
        ViewBag.BreadCrumbs = breadCrumbs;
        ViewBag.StaticPage = staticPage;

        // Return the view
        return View();

    } // End of the error method

    #endregion

    #region Post methods

    [HttpPost]
    public IActionResult search(IFormCollection collection)
    {
        // Get the keywords
        string keywordString = collection["txtSearch"];

        // Return the url with search parameters
        return Redirect("/home/search?kw=" + WebUtility.UrlEncode(keywordString));

    } // End of the search method

    #endregion

    #region Ajax methods

    [HttpGet]
    public IActionResult get_group_standings(Int32 id = 0)
    {
        // Get all the groups
        IList<Group> groups = this.group_repository.GetActiveByStaticPageId(id, "sort_value", "ASC");

        // Add data to the form
        ViewBag.Groups = groups;

        // Return the partial view
        return PartialView("_group_standings");

    } // End of the get_group_standings method

    [HttpGet]
    public IActionResult get_final_games(Int32 id = 0)
    {
        // Get final games
        IList<Final> finals = this.final_repository.GetActiveByStaticPageId(id, "sort_value", "ASC");

        // Add data to the form
        ViewBag.Finals = finals;

        // Return the partial view
        return PartialView("_final_games");

    } // End of the get_final_games method

    [HttpGet]
    public IActionResult get_group_details(Int32 id = 0)
    {
        // Get the group
        Group group = this.group_repository.GetOneById(id);

        // Get teams and games
        ViewBag.Teams = this.group_repository.GetTeamsFromXml(group.data_content);
        ViewBag.Games = this.group_repository.GetGamesFromXml(group.data_content);
        ViewBag.Matches = this.group_repository.GetUpcomingMatchesFromXml(group.data_content);

        // Return the partial view
        return PartialView("_group_details");

    } // End of the get_group_details method

    [HttpGet]
    public async Task<string> get_news_as_html(string kw = "")
    {
        // Create the string to return
        string html = "";

        // Create the culture
        CultureInfo cultureInfo = new CultureInfo("sv-SE");

        // Get news items
        IList<NewsItem> newsItems = await this.static_page_repository.GetNewsFromRSS(kw);

        // Loop the array
        for (int i = 0; i < newsItems.Count; i++)
        {
            // Get the date
            DateTime date = DateTime.UtcNow;

            try
            {
                date = DateTime.ParseExact(newsItems[i].pub_date, "ddd, d MMM yyyy HH:mm:ss GMT", CultureInfo.InvariantCulture);
            }
            catch (Exception ex)
            {
                string exMessage = ex.Message;
            }

            // Create the html
            html += "<div class=\"annytab-news-item-container\">";
            html += "<a href=\"" + newsItems[i].link + "\" target=\"_blank\" class=\"annytab-news-item-title\">" + newsItems[i].title + "</a><br />";
            html += "<div class=\"annytab-news-item-date\">" + date.ToString("d MMMM yyyy HH:mm:ss", cultureInfo) + "</div>";
            html += "<div class=\"annytab-basic-bread-text\">" + newsItems[i].description + "</div>";
            html += "</div>";
        }

        // Powered by Google News
        html += "<div style=\"padding: 5px;margin:0px 0px 20px 0px;font-size:14px;\">Nyheterna kommer från <a href=\"https://news.google.com/news/search/section/q/" + kw + "/" + kw + "?hl=sv&gl=SE&ned=sv_se\">Google News</a></div>";

        // Return the string
        return html;

    } // End of the get_news_as_html method

    #endregion

} // End of the class

View

A view is responsible of presenting information to end users and to handle input from end users. You can use Html, Razor and JavaScript in views. A view usually depends on a layout view and it can include partial views. A view can get data from a controller via ViewBag, ViewData or TempData. A controller determines the method to transfer data to a view.

@using Annytab.Models
@{
    // Get form values
    StaticPage staticPage = ViewBag.StaticPage;

    // Set the web address
    string web_address = "http://www.fotbollstabeller.nu";

    // Set meta data
    ViewBag.Title = staticPage.title;
    ViewBag.MetaDescription = staticPage.meta_description;
    ViewBag.MetaKeywords = staticPage.meta_keywords;
    ViewBag.MetaCanonical = web_address;
    ViewBag.MetaRobots = staticPage.meta_robots;

    // Set the layout for the page
    Layout = "/Views/shared_front/_standard_layout.cshtml";
}

@*Title*@
<h1>@ViewBag.Title</h1>

@*Share content*@
<div class="annytab-share-container">
    <div class="annytab-share-button"><div class="fb-like" data-layout="button" data-action="like" data-show-faces="false" data-share="true" data-href="@web_address"></div></div>
    <div class="annytab-share-button"><a href="https://twitter.com/share" data-count="none" class="twitter-share-button" data-href="@web_address" data-lang="sv">Tweet</a></div>
</div>

@*Google ad*@
<div class="annytab-ad-slot-container-top">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <!-- fotbollstabeller.nu - responsive -->
    <ins class="adsbygoogle"
         style="display:block"
         data-ad-client="ca-pub-3070633924070834"
         data-ad-slot="4984893802"
         data-ad-format="auto"></ins>
    <script>
        (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>

@*Groups*@
<div id="groupsContainer" data-id="@staticPage.id" class="annytab-list-container">
    <div class="annytab-basic-loading-container"><i class="fas fa-spinner fa-pulse fa-4x fa-fw"></i><div style="margin-top:10px;">Laddar tabeller...</div></div>
</div>

@*Description*@
<h2 class="annytab-basic-title-container">Beskrivning</h2>
<div class="annytab-basic-bread-text" style="padding:5px;">@Html.Raw(staticPage.main_content) </div>

@*Google ad*@
<div class="annytab-ad-slot-container-bottom">
    <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
    <!-- fotbollstabeller.nu - responsive -->
    <ins class="adsbygoogle"
         style="display:block"
         data-ad-client="ca-pub-3070633924070834"
         data-ad-slot="4984893802"
         data-ad-format="auto"></ins>
    <script>
        (adsbygoogle = window.adsbygoogle || []).push({});
    </script>
</div>

@*News*@
<div id="newsContainer" data-search="@staticPage.news_search_string" style="display:none;">
    <h2 class="annytab-basic-title-container">Nyheter</h2>
</div>

@section scripts {
    <script type="text/javascript">

        // Get containers
        var newsContainer = $('#newsContainer');
        var groupsContainer = $('#groupsContainer');

        // Get group standings
        $.ajax({
            type: 'GET',
            url: '/home/get_group_standings/' + groupsContainer.data('id'),
            dataType: 'html',
            success: function (data) {
                groupsContainer.html(data);
            },
            error: function (xhr) {
                groupsContainer.html('');
            } 
        });

        // Get news from google
        if (newsContainer.data("search") != "")
        {
            $.ajax({
                type: 'GET',
                url: '/home/get_news_as_html?kw=' + encodeURIComponent(newsContainer.data("search")),
                dataType: 'html',
                success: function (data) {
                    newsContainer.append(data);
                    newsContainer.slideDown(2000);
                },
                error: function (xhr) {
                    newsContainer.html('');
                }
            });
        }
    </script>
}

Leave a Reply

Your email address will not be published. Required fields are marked *