Click here to Skip to main content
15,860,943 members
Articles / Web Development / ASP.NET

Web-Application Framework - Catharsis - part X. - Model layer

Rate me:
Please Sign up or sign in to vote.
4.38/5 (3 votes)
5 Mar 2009CPOL12 min read 26.5K   13  
Catharsis web-app framework - Model layer in MVC design pattern

Enter into Catharsis - adding new Entity

This article is next step in Catharsis documented tutorial. Catharsis is web-application framework gathering best-practices, using ASP.NET MVC 1.0, NHibernate 2.1. All needed source code you can find on

http://www.codeplex.com/Catharsis/

Catharsis-title.png


No.ChapterNo.ChapterNo.Chapter

I New solution VI CatharsisArchitectureXI Controller layer

II Home page observationVII Entity layer XII UI Web layer

III Roles and usersVIII Data layer XIII Tracking, PAX design pattern

IV Localization, translationsIX Business layerXIV Catharsis Dependency Injection (DI, IoC)

V Enter into Catharsis, new EntityX Model layer XV (Code Lists - undone)

Model layer  

This story is based on the version 1.0 and in comparison with previous (some almost half a year old) it would be correct even in the future versions. The framework has grown a lot and at this moment covers all features and layers in the real best-practice manner. This circumstance was essential for this article about Model layer.

What is the Model layer 

Model layer – ours current topic – is the part of the complex MVC design pattern. In fact, all these lines could be true even without ASP.NET MVC infrastructure, because it is about the Model-View-Controller design pattern. I have experience with three MVC implementations and at least this allows me to say: “lucky .NET developers how can finally implement MVC design pattern on ASP.NET MVC framework!”.
The Catharsis place in this world is simple. Put all available best-practice solutions into one compact, reliable framework. If you spend some time using Catharsis, adjust it her, understand her, you would come up with only one result: why to implement it another way? Everything you need Catharsis has. And what is more: She’s not only open-source – she’s distributed as open source code which can be CHANGED for any special project needs.

Model place in the MVC frame

MVC ver. MVP. I guess that many readers and users of MVC will recruit from the MVP practice based on ASP.NET web-forms. It leads to an assumption: If I do know MVP than only P (presenter) is the difference, so I would learn only C (controller). Wrong? We will see.

MVC circle or subsequence

web_layerII.png MVC is more the subsequence then a circle. Yes, user interacts with MVC and all these items are called over and over again. But if any rounded comparison should be used then better is a spiral. I simply would like to say: Let’s try to describe the MVC as the directed process with the named beginning and the end.
Well than, first of all, we should name who is playing the supplier’s role and who the consumer’s role. Why? It will not only help us to find out were the beginning and where the end is. It also clarify how (and for whom) to design the Model. The answer is simple: View consumes all the information, previously supplied by controller. Therefore the View needs will decide how to design the IModel interface. And of course, next it will force the controller to fill it.
As we will see in the next chapter, the View, in fact, sees nothing but IModel interface. Really, take a look on references. No Models project, no Controllers project. Only the interfaces exposed in Common library.
MVC ver. MVP. The consequences are so significant that the MVP coders should firstly stair and shake theirs heads. The standalone View is 100% dependent on the IModel interface - only. In fact, the get {} accessors only, because the next step (final stage) is the rendered page. Setting anything into the IModel is senseless.
The similar, upside-down, approach is on the controller side. There is the known interface IModel which should be supplied (using set{}) with values and than passed to the View. Wait a second. Why View? Why not IView!
The answer is simple again. There is NO relationship between the Controller and View. View (as we’ve already seen) is an orphan, cooperating with the get {} accessors from its known IModel. Controller is the simple-minded (do not tell him!) workhorse, reading the Forms & QueryStrings coming from response, calling IFacades and filling the IModel.

  • There is none handsome “presenter” patiently filling the IView.
  • There are no IView set {} accessors available.
  • There is independent, self-sufficient, smart View, reading the IModel information, which is next rendered and provided to the user’s browser.

MVC ver. MVP? Is anybody still in a position that they differ only in the P ver. C?

Catharsis IModels battery

ModelBatteryII.png Catharsis has built in IModel battery, which is displayed on the snap.
These interfaces create the link, the glue of the whole framework. The contract they grant is the core element of the simplicity for the project development, your day-to-day real coding.
Now in the more detail:
When you run the Guidance first time and create the solution, you can than run the DB SQL scripts, click F5 and see the Home screen (View).
Even If you run the Guidance recipe to generate the new entity structure, create the related table in the DB, extend the “Menu.config” with new entity access rights, append Str.Controllers.Entity const, click F5 – you can access the new entity Search, Detail and List views.
That all is working because of the IModel interfaces battery. The base classes (e.g. EntityController, MasterBase …) simply knows the contract granted by ProjectBase.Core.Web.Models interfaces. They are filled (by controllers) and read & rendered (by views) and therefore provide the “allover same application stuff”.
Every page (better is the View) needs (and gets) the contracted IModels to create the TreeView menu, BreadCrumb control, Buttons, Page title, Action links and so on.
I believe that the naming of these IModels and the comments appended to theirs property declarations are all self-describing. Some of them (e.g. ITreeViewModel) are so interesting that I wish to dedicate them separate article (we will see).
But in general their meaning is really simple. The information set needed on user-friendly View is demanded via the IModel interface contract. The workhorse (controller) must contact all the IFacades and Providers to supply the contracted information.
Well, it could take some time to get in the Catharsis implemented code (which is mainly nested in the ProjectBase). But trust me: There are no tricks! Calls are simple as described above: Controller is calling IFacade, which asks IDao to get data. Returned values are supplied to the IModel. ASP.NET MVC framework than calls the View to be rendered with passed IModel. Howgh.

Catharsis IModel usage example

The best suitable for an example is the IActionsModel. This interface grants only one property (except the base ICoreModel implementation see below):

C#
[ConcreteType("ProjectBase.Mvc.Web.Models.Master.ActionsModel, ProjectBase.Mvc")]
public interface IActionsModel : ICoreModel
{
    IList<IActionItem> Actions { get; set; }
}

The IActionItem declaration:

C#
public interface IActionItem
    {
        /// <summary>
        /// Name which identifies actionItem by action name
        /// </summary>
        string ActionName { get; set; }
        /// <summary>
        /// Controller on which is Action hosted
        /// </summary>
        string ControllerName { get; set; }
        /// <summary>
        /// The value (if any) which should be pass as parameter
        /// </summary>
        int? ActionValue { get; set; }
        /// <summary>
        /// String key which should be used for Localization.
        /// This is used if string.IsNullOrEmpty(TextLocalized)
        /// </summary>
        string Text { get; set; }
        /// <summary>
        /// Already localized and Text for user
        /// </summary>
        string TextLocalized { get; set; }
        /// <summary>
        /// Where should the anchor navigate to
        /// </summary>
        string Href { get; set; }
        /// <summary>
        /// What target for anchor should be used.
        /// if string.IsNullOrEmpty(Target) current is used
        /// </summary>
        string Target { get; set; }
        /// <summary>
        /// Onclick event
        /// </summary>
        string OnClick { get; set; }
    }

So, for every Action you want to be available in the rendered View (as a hyperlink) append an IActionItem to the Model.MasterModel.ActionsModel.Actions collection. Some are appended by default by the Catharsis framework. For example the default View for every controller - “List” - calls the OnBeforeList() method which among others calls extension method to append some basic actions:

C#
protected virtual void OnBeforeEdit(int id)
{
    ...  // sets the basic Actions
    this.AddAction(Constants.Actions.Common.New, Url)
        .AddAction(Constants.Actions.Common.Search, Url) 
        .AddAction(Constants.Actions.Common.List, Url
            , Constants.Actions.Targets.Self); 
    ...
} 

Even if the IActionItem is really rich (8 properties declared), the developer’s built-in laziness leads to simple call: .AddAction(ActionName, UrlHelper). It will supply the Action interface with all needed values, but on a very rough level.
Of course, you can extend them. Them means, virtual OnBeforeList() or controller dependent override OnBeforeList(), or the extension method AddAction(). That is upon you. The goal: supply the IActionsModel with information must be fulfilled anyway. The consumer awaits it.
The consumer in this example of that IActionsModel interface is the Master piece - WebControl named ActionsWC.ascx. This one is so simple that we can take a closer look on its whole implementation:

ASP.NET
<%@ Control Language="C#" AutoEventWireup="true" 
CodeBehind="ActionsWC.ascx.cs" 
Inherits="Firm.Product.Web.Controls.Master.Blocks.ActionsWC" %>
<div>
<% 
if ( ActionsModel.Is() // redundant check, MasterModel constructor creates that instance 
  && ActionsModel.Actions.Count > 0 ) 
{ 
    foreach (var action in ActionsModel.Actions) 
    {
      string href = action.Href ?? 
          Url.Action(action.ActionName, action.ControllerName
              , new { id = action.ActionValue });
      string text = action.TextLocalized ?? 
          GetLocalized(action.Text);
      string onclick = string.IsNullOrEmpty(action.OnClick) 
          ? string.Empty 
          : " onclick=\"" + action.OnClick + "\""; %>
    <p><a href="<%= href %>" title="<%= text %>"
          <%= onclick %> ><%= text %></a></p>
<%  }  
} %>
</div>

ActionsWC is a smart control in the manner of the IActionsModel. It checks if there are any actions to be rendered. Than for every item in the collection tries to consume its declared properties and provide them to the user. Nice and easy. And simple.
The simplicity is the gift coming form the strict separation of concern. There are no mixed operations in the Model-View-Controller instances.

  • Model declares get {} and set {} accessors and does not provide any operation!
  • Controller does its best to supply all these hungry set {} properties
  • View (the band front man) shows the friendly face to user by consuming the IModel information.

MVC ver MVP. MVC approach is maintainable and reliable by its nature. The responsibility is so pure and separated, that the bug-hunting is usually the question of seconds (Ou, I see, I forgot to fill “that IModel property” in that “Controller” and even to append the fundamental .Is() check on the “View” consumer). You’ll like it.

Catharsis ICoreModel

Ancestor of every IModel in the Catharsis framework is the ICoreModel interface (ProjectBase.Core.Mvc.ICoreModel). Its declaration contains

C#
using System;                            // ===================================
using System.Collections.Generic;        // Guidance generated code © Catharsis
using System.Linq;                       // ===================================
using System.Text;

namespace ProjectBase.Core.Mvc
{
    /// <summary>
    /// The application Fundamental Model containing Messages
    /// </summary>
    public interface ICoreModel
    {
        /// <summary>
        /// Gets or sets the messages repository.
        /// </summary>
        /// <value>The messages.</value>
        IList<ApplicationMessage> Messages { get; set; }
    }
}

Spartan, but the fundamental pillar. There are two main impacts of the ICoreModel interface:
1) Every model in the Catharsis framework is created via Factories e.g. ModelFactory.CreateModel<IActionsModel>(). For security reasons (to disable returning let’s say IActionsFacade) there is a check on the provided generic ‘T’ template (in our case IActionsModel).

C#
public static T CreateModel<T>()
            where T : ICoreModel 

2) ICoreModel, as the granted Ancestor, provides the Messages collection. This is so crucial element of the Catharsis framework that it needs separate article (in the future). For now just briefly:
Is there any trouble you have to solve? Business error, incorrect date-time format coming from UI in the Forms collection, unauthorized access … Put the Message in the Messages collection. Messages can have three types of severity: Information, Warning and Error (including Exception).
When the framework founds any message in the collection – renders the localize content to the user (red background for Error, orange for Warning and green for Information).
What’s more, if the controller’s action was decorated with the [Transaction] attribute and Messages collection contains at least one Message with severity Error – transaction is rolled back. Simple, effective, easy to learn and use. And reliable.
More detailed this concept will be described in the special article (in the future). Last note goes two the IFacade.Messages ver. IModel.Messages. They must be the same instance. Therefore if you are creating any IFacade using factory, you are forced to provide Messages collection – provide the Model.Messages. This is MUST.

Catharsis IMyEntityModel

Catharsis guidance is helpful in times, when you are extending your application with the new entities (Person, Debtor, Contract, License…). To recapitulate the impact of these generators on the Models layer (project) let’s mention two events:
1) The Common project is extended with a new interface IMyEntityModel. This is the right place for extending the contract demanded by View and supplied by Controller.
2) The Models project is extended with a new class called MyEntityModel.cs implementing the IMyEntityModel. Example shows the simple LanguageModel.

C#
public class LanguageModel : BaseEntityModel<Language>, ILanguageModel
   {
       /// <summary>
       /// Sets the ShowExportAction option to true;
       /// Enables the Excel exporting
       /// </summary>
       public LanguageModel()
           : base()
       {
           ExportModel.ShowExportAction = true;
       }
       /// <summary>
       /// Search object containing Example of this entity
       /// and can be extended with other more powerfull properties for searching
       /// </summary>
       public new LanguageSearch SearchParam
       {
           get { return base.SearchParam as LanguageSearch; }
           set { base.SearchParam = value; }
       }
   }

First look reveals that two obvious settings are there: 1) Enabling the MS Excel export Action on the “ListView, 2) casting base.SearchParam property to more specialized (and declared) LanguageSearch. Others can be also uncovered: 3) the “base()” call which for you fills all the nested models with ready to use instances (e.g. ItemModel). 4) On a different place (controller’s AOP filter) there is provided the crucial Messages “store & restore in session management”. For now count on it (or observe it), it will be explained elsewhere.
If your entity will need any information, which is not presented in the base implementation, simply extend IMyEntityModel. Then use Controller to supply data and let View to consume them. And of curse extend the MyEntityModel with implementation of the newly declared properties.

IModels instances on Views and Controllers

Where to find the model instances and how to access them in the Catharsis framework?

Controller

Every controller in the application is derived from the ICoreController, which declares the property ICoreModel Model {get; set; }.
If you are using the Guidance to create the infrastructure for new entities, than you are by default provided with the casting mechanism. Every MyEntityController declares the new Model property which exposes the IMyEntityModel properties and hides the base. Therefore you can access all the properties declared on IMyEntityModel via the controller’s property Model. Easy and practical, because the base classes can still interact with their known interfaces (e.g. IEntityModel), which are the same instance of course.
The real added value is hidden in the base Controllers implementation. The base classes like WebController and EntityController know its IModels and can do lot of common stuff for you. It means fill the ButtonsModel, ItemModel, TreeViewModel, ActionsModel etc. (but you can still change it by overriding OnBeforeActions() etc.)

View

The View must provide the type of the IModel needed for correct behavior in the generic declaration of its CodeBehind:

C#
public partial class ActionsWC : ViewWebControlBase<IWebModel> { } 

Current Implementation of the ASP.NET MVC RC1 provides this strongly typed instance in the property named Model. (It’s expectable, but not presented in previous versions. The Catharsis framework had this feature always). That’s very handy, because you can again access the IMyEntityModel properties via calling the strongly typed property Model on any View (page or control).
Some Models are nested e.g. the ActionsModel can be navigated by Model.MasterModel.ActionsModel. Therefore there are two special base classes for your CodeBehind implementation:
ViewWebControlBase (non entity Views)
ViewEntityControlBase (derived from ViewWebControlBase)
They provide some properties and methods for easier handling of the IModels instances. Because they are again strongly typed

C#
public class ViewWebControlBase<TModel> : ViewUserControl<TModel>
        where TModel : class, IWebModel 

they can access and exposed and known interface in a smart way like:

C#
public IActionsModel ActionsModel { get { return Model.MasterModel.ActionsModel; } }

The guidance automatically uses these ControlBase classes for generated code. If you append new Control manually (which could be quite usual) do not forget to append them. They can help you a lot (the more we will see on the View layer chapter).
And it does not have to be an end. You can, for example, create for some group of entities special middle-base-class with more precious interface like IMyColoredHavingEntityModel and so on. There are now boundaries for generalization (inheritance) and you as a best-practice applying developer should use it.

Models summary

MVC ver. MVP. The Models in these abbreviations are only the homophone. They sound same, but they are totally different.
Catharsis provides the solution which leads you, as a developer, to the best-practice coding concepts. Layers – even the parts of the whole MVC layer – are separated (proven above). They do depend on contracts provided via IModels. The Views concern than on their “show-performance” for user, consuming the stored information from the IModels. Controller’s, these lovely simply-minded workhorses, link ants carefully round around to get all needed cargo and store it in the IModels. As in reality, division of labor is effective, but the cream is only for few – visible Views.

Enjoy Catharsis
Radim Köhler

Sources

All needed source code you can find on  

http://www.codeplex.com/Catharsis/

How do I see the MVC ver. MVP:http://www.codeproject.com/KB/aspnet/AspNetMvcMvp.aspx

History

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Czech Republic Czech Republic
Web developer since 2002, .NET since 2005

Competition is as important as Cooperation.

___

Comments and Discussions

 
-- There are no messages in this forum --