The Catharsis Example Project
This document gives a brief overview of the Firm.SmartControls
example solution for the Catharsis framework. Catharsis is a powerful framework which uses a collection of best practice design patterns built around a solid architecture using MVC.
Once you have installed Catharsis, this document serves as a logical next step in gaining knowledge of how this framework can help you build robust applications quickly. When you have read this document, the next one in this series gives you a chance to put the framework into practice by showing you how to add new entities in a simple to follow step by step fashion.
The author of Catharsis, Radim Köhler, has added many CodeProject documents on the Catharsis ASP.NET MVS Framework which can be found here.
The source code for Catharsis can be found here.
Introduction
The demo solution contains entities such as Agent
and AgentContract
. In this section we will examine how these entities are integrated into the Catharsis framework.
Let’s look at the Insurance Agent
entity. First we look at the database table to see what information is stored for an insurance agent and then we will examine the data layer to see how the table is mapped into the application. Here we see the NHibernate mapping file and the DAO (Data Access Object) in the solution.
Here is the database table create script:
CREATE TABLE [dbo].[InsuranceAgent](
[InsuranceAgentId] [int] IDENTITY(1,1) NOT NULL,
[Code] [nvarchar](150) NOT NULL,
[IsInternal] [bit] NOT NULL,
[Rating] [smallint] NOT NULL,
[CommissionRate] [decimal](18, 10) NOT NULL,
[CommissionPeak] [decimal](18, 2) NOT NULL,
[CurrencyId] [smallint] NOT NULL,
[CountryId] [smallint] NOT NULL,
[GenderId] [tinyint] NOT NULL,
[Started] [datetime] NOT NULL,
[Finished] [datetime] NULL,
CONSTRAINT [PK_InsuranceAgent] PRIMARY KEY CLUSTERED
(
[InsuranceAgentId] ASC
)
Here is the HNibernate mapping file for the InsuranceAgent
:
='1.0' ='utf-8'
<hibernate-mapping xmlns='urn:nhibernate-mapping-2.2'
namespace='Firm.SmartControls.Entity.Insurance'
assembly='Firm.SmartControls.Entity'>
<class name='Agent' table='InsuranceAgent' lazy='true' >
<id name='ID' column='InsuranceAgentId' >
<generator class='native'></generator>
</id>
<property not-null='true' name='Code' />
<property not-null='true' name='IsInternal' />
<property not-null='true' name='Rating' />
<property not-null='true' name='CommissionRate' />
<property not-null='true' name='CommissionPeak' />
<property not-null='true' name='Started' />
<property not-null='false' name='Finished' />
<many-to-one name='Currency' column='CurrencyId' lazy='false' ></many-to-one>
<many-to-one name='Country' column='CountryId' lazy='false' ></many-to-one>
<many-to-one name='Gender' column='GenderId' lazy='false' ></many-to-one>
<bag name='Contracts' inverse='true' lazy='true'
cascade='all-delete-orphan' >
<key column='InsuranceAgentId' />
<one-to-many class='AgentContract' />
</bag>
</class>
</hibernate-mapping>
Note that lazy="true"
means the object will only be instantiated when it is needed.
The first seven parameters mentioned in the mapping table are simple mappings. The Currency
, Country
and Gender
are one to many mappings. These are ‘Entity-types’ rather than simple ‘Value-types’.
They are handled as CodeLists in the Catharsis solution.
Now we will look at the Dao (Data Access Object), this is the object which reads the information from the database via NHibernate. It is used as part of the searching functionality to return all database rows that match a certain set of criteria. It also takes care of Order-By clauses.
Here is an extract from the file:
public override IList<Agent> GetBySearch(ISearch searchObject)
{
Check.Require(searchObject.Is(), “Use only AgentSearch not-null instance
for searching Agent entities”);
var search = searchObject as AgentSearch;
if (search.Is())
{
Criteria.CreateEqual(Str.Common.ID, search.IDSearch);
Criteria.CreateLike(Str.Common.Code, search.Example.Code);
Criteria.CreateEqual(Str.Business.Agent.IsInternal, search.IsInternalSearch);
Criteria.CreateGe(Str.Business.Agent.Rating, search.RatingFrom);
Criteria.CreateGe(Str.Business.Agent.CommissionRate, search.CommissionRateFrom);
Criteria.CreateGe(Str.Business.Agent.CommissionPeak, search.CommissionPeakFrom);
Criteria.CreateGeDate(Str.Business.Agent.Started, search.StartedFrom);
Criteria.CreateGeDate(Str.Business.Agent.Finished, search.FinishedFrom);
Criteria.CreateLe(Str.Business.Agent.Rating, search.RatingTo);
Criteria.CreateLe(Str.Business.Agent.CommissionRate, search.CommissionRateTo);
Criteria.CreateLe(Str.Business.Agent.CommissionPeak, search.CommissionPeakTo);
Criteria.CreateLeDate(Str.Business.Agent.Started, search.StartedTo);
Criteria.CreateLeDate(Str.Business.Agent.Finished, search.FinishedTo);
Criteria.CreateEqual(Str.Controllers.Currency, search.Example.Currency);
Criteria.CreateEqual(Str.Controllers.Country, search.Example.Country);
}
return GetListByCriteria(searchObject);
}
The Str
class is a list of constants which are used instead of string
s to avoid errors and to make the code more maintainable. These files can be found at the following location:
Here is a file extract showing the Agent
class:
public partial class Str
{
public partial class Business
{
#region Agent
public class Agent
{
protected Agent() { }
public const string IsInternal = "IsInternal";
public const string Rating = "Rating";
The GetBySearch
method above is called using a SearchObject
as we can see in the method signature:
public override IList<Agent> GetBySearch(ISearch searchObject)
{
Check.Require(searchObject.Is(), “Use only AgentSearch not-null
instance for searching Agent entities”);
var search = searchObject as AgentSearch;
We will now look at this SearchObject
.
Here is how the search object is set up:
public class AgentSearch : BaseSearch<Agent>, ISearch<Agent>
{
#region properties
public virtual bool? IsInternalSearch { get; set; }
public virtual DateTime? StartedFrom { get; set; }
public virtual DateTime? StartedTo { get; set; }
public virtual DateTime? FinishedFrom { get; set; }
public virtual DateTime? FinishedTo { get; set; }
public virtual short? RatingFrom { get; set; }
public virtual short? RatingTo { get; set; }
public virtual decimal? CommissionRateFrom { get; set; }
public virtual decimal? CommissionRateTo { get; set; }
public virtual decimal? CommissionPeakFrom { get; set; }
public virtual decimal? CommissionPeakTo { get; set; }
#endregion properties
}
It is important to note that each item in the search can be nullable, as indicated by the question mark after the data type. This means that the search parameters are optional and supplying null
for any nullable search parameter is accepted.
Let’s look at how the List screen in the application finds and displays the Agents in the database:
The aspx file which creates the page above is found in the Web project:
Setting up the grid to display the data is handled by the framework. The framework will automatically create the list. Adding additional columns to the list can be done in the OnBeforeList()
method in the Controller
of the entity in question.
Here is the constructor in the codebehind List.aspx.cs.
namespace Firm.SmartControls.Web.Views.Agent
{
public partial class List : ViewPageBase<IAgentModel>
{
}
}
We can see that the IAgentModel
is used to create the list.
The constructor of this interface is shown here:
[ConcreteType("Firm.SmartControls.Models.Insurance.AgentModel,
Firm.SmartControls.Models")]
public interface IAgentModel : IBaseEntityModel<Agent>
{
new AgentSearch SearchParam { get; set; }
}
You can see that a lot of the code will not need to be changed much in a lot of applications, the framework and the Guidance create a lot of the files required automatically.
Let’s look at the entity used by this interface, the Agent
entity.
This is an important file so we will look at each part of it in detail:

Members
#region members
IList<AgentContract> _contracts;
#endregion members
Each Agent
can have many Contracts
, this relationship is discussed in a later document.
Some of the code for properties is shown here:
#region properties
public virtual string Code { get; set; }
public virtual bool IsInternal { get; set; }
public virtual short Rating { get; set; }
public virtual decimal CommissionRate { get; set; }
public virtual decimal CommissionPeak { get; set; }
public virtual Currency Currency { get; set; }
Getter and Setter methods exist for each property. Note that the datatype for Currency
is ‘Currency
’ because another entity is used for this. By this, I mean that the Currency
property of Agent is an entity-type of type Currency
.
This next code extract is particular to this entity, an agent can have one or many contracts.
#region contract list
public virtual IList<AgentContract> Contracts
{
get
{
if (_contracts.IsNull())
{
_contracts = new List<AgentContract>();
}
return _contracts;
}
protected set { _contracts = value; }
}
#endregion contract list
Every entity in Catharsis has some overwritten abstract
methods which can help to identify the entity. The property returned in these methods (Code in the example below) can be set while adding an entity using the Catharsis Guidance.
#region override abstract
public override string ToDisplay()
{
return Code;
}
protected override string GetDomainObjectSignature()
{
return Code;
}
#endregion override abstract
The concrete class of the AgentModel
looks like this:
public class AgentModel : BaseEntityModel<Agent>, IAgentModel
{
public AgentModel()
: base()
{
ExportModel.ShowExportAction = true;
}
public new AgentSearch SearchParam
{
get { return base.SearchParam as AgentSearch; }
set { base.SearchParam = value; }
}
}
Summary
This concludes our brief look at the Catharsis example solution. In this document, we have touched on some of the important components of the framework.
A practical approach to learning more about how the Catharsis framework work is to actually add new entities and manipulate them. This will be done in the next chapter, the document is called Extending the Catharsis Demo Project.
The Next Step
Now that you have installed Catharsis and examined the demo project, it is a good idea to take a look at the detailed step by step guide on how to add entities to the demo project. With this knowledge, you will very quickly be able to create your own applications.
Read the document here.