65.9K
CodeProject 正在变化。 阅读更多。
Home

Catharsis 教程 02 - Catharsis 示例

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2009年6月26日

CPOL

4分钟阅读

viewsIcon

17615

downloadIcon

92

了解 Catharsis 如何帮助您快速开发健壮的企业级应用程序。

概述

Catharsis 是一款强大的 RAD (快速应用程序开发) 工具,它使用一套最佳实践设计模式,围绕采用 MVC 的稳固架构构建。通过 Guidance,该框架在稳健的企业级架构上提供了 RAD 功能。

在阅读本文档之前,请参阅此处提供的 Catharsis 安装指南: catharsis_tutorial_01.aspx

安装 Catharsis 后,本文档将作为下一步,帮助您了解该框架如何帮助您快速构建健壮的应用程序。阅读本文档后,本系列的下一篇将通过逐步介绍如何添加新实体,让您有机会将该框架付诸实践。

Catharsis 的作者 Radim Köhler 在 CodeProject 上发布了许多关于 Catharsis ASP.NET MVS 框架的文档,您可以在此处找到: https://codeproject.org.cn/info/search.aspx?artkw=catharsis&sbo=kw

本文档将考察 Catharsis 框架的 Firm.SmartControls 示例解决方案。

引言

示例解决方案包含 `Agent` 和 `AgentContract` 等实体。在本节中,我们将考察这些实体如何集成到 Catharsis 框架中。

让我们看看保险代理人 (Insurance Agent) 实体。首先,我们查看数据库表,了解存储了哪些关于保险代理人的信息,然后我们将考察数据层,了解该表如何映射到应用程序。这里我们看到 NHibernate 映射文件和解决方案中的 DAO (数据访问对象)。

catharsis_tutorial_02_001.png

这是数据库表创建脚本

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
)

这是 `InsuranceAgent` 的 nHibernate 映射文件

<?xml version='1.0' encoding='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>

请注意,`lazy="true"` 表示对象仅在需要时才会被实例化。

映射表中提到的前七个参数是简单的映射。`Currency`、`Country` 和 `Gender` 是一对多映射。它们是“实体类型”,而不是简单的“值类型”。

它们在 Catharsis 解决方案中被处理为代码列表 (CodeLists)。

catharsis_tutorial_02_002.png

现在我们来看 DAO (数据访问对象)。这是通过 nHibernate 从数据库读取信息的对象。它被用作搜索功能的一部分,以返回所有符合特定条件集的数据库行。它还负责处理 Order-By 子句。

这是文件中的一个片段

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);//Search for ID         
    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);
}

`Str` 类是一个常量列表,用于替代字符串,以避免错误并使代码更易于维护。这些文件可以在以下位置找到:

catharsis_tutorial_02_003.png

这是显示 `Agent` 类的文件片段

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";

如方法签名所示,DAO 中的 `GetBySearch` 方法使用 `SearchObject` 调用。

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;

现在我们来看搜索对象。

catharsis_tutorial_02_004.png

这是搜索对象的设置方式。

public class AgentSearch : BaseSearch<Agent>, ISearch<Agent>
{
    #region properties
    /// <summary>
    /// Crucial is to provide DIFFERENT name then the EXAMPLE has:
    /// IsInternal -- IsInternalSearch!
    /// Bind methods binds both: Search object + Example!
    /// Example IsInternal is bool not bool? - which could cause demage, 
    /// when null is sent to IsInternal (ment to be IsInternalSearch)
    /// </summary>
    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
}

需要注意的是,搜索中的每个项目都可以为空,如数据类型后的问号所示。这意味着搜索参数是可选的,为任何可空搜索参数提供 `null` 也是被接受的。

让我们看看应用程序中的列表屏幕如何查找和显示数据库中的代理人。

创建上述页面的 ASPX 文件位于 Web 项目中。

catharsis_tutorial_02_006.png

框架负责设置显示数据的网格。框架会自动创建列表。可以在相关实体的 `Controller` 的 `OnBeforeList()` 方法中添加额外的列。

这是代码隐藏文件 *List.aspx.cs* 中的构造函数。

namespace Firm.SmartControls.Web.Views.Agent
{
    public partial class List : ViewPageBase<IAgentModel>
    {
    }
}

我们可以看到 `IAgentModel` 被用于创建列表。

此接口的构造函数在此处显示。

[ConcreteType("Firm.SmartControls.Models.Insurance.AgentModel, 
     Firm.SmartControls.Models")]
public interface IAgentModel : IBaseEntityModel<Agent>
{
    /// <summary>
    /// Search parameters for filtering the list of the 'Agent'
    /// </summary>
    new AgentSearch SearchParam { get; set; }
}

您可以看到,在许多应用程序中,许多代码不需要进行太多更改,框架和 Guidance 会自动创建许多所需的文件。

让我们看看此接口使用的实体,即 `Agent` 实体。

catharsis_tutorial_02_007.png

这是一个重要的文件,我们将详细查看它的每个部分。

catharsis_tutorial_02_008.png

成员

#region members
IList<AgentContract> _contracts;
  // NO members for default value - That's done
  // via PROTOTYPE design pattern on Facade.CreateNew()
#endregion members

每个代理人可以有多个合同,这种关系稍后将讨论。

此处显示了属性的一些代码。

#region properties
/// Unique Code
public virtual string Code { get; set; }
/// IsInternal switch
public virtual bool IsInternal { get; set; }
/// small int as Rating (to show the format abilities)
public virtual short Rating { get; set; }
/// decimal places for counting the commision (to show the format abilities)
public virtual decimal CommissionRate { get; set; }
/// decimal value to keep info about highest Commission (to show the format abilities)
public virtual decimal CommissionPeak { get; set; }
/// codelist
public virtual Currency Currency { get; set; }

每个属性都有 Getter 和 Setter 方法。请注意,`Currency` 的数据类型是 `Currency`,因为这是另一个实体。

下面的代码特定于此实体,一个代理人可以有一个或多个合同。

#region contract list
/// List of contracts (uploaded files)
public virtual IList<AgentContract> Contracts
{
     get 
     {
          if (_contracts.IsNull())
          {
             _contracts = new List<AgentContract>();
             // be lazy as long as possible! 
          }
          return _contracts; 
      }
      protected set { _contracts = value; }
}
#endregion contract list

Catharsis 中的每个实体都有一些被重写的抽象方法,可以帮助识别实体。这些方法中返回的属性(示例如下)可以在使用 Catharsis Guidance 添加实体时设置。

#region override abstract
/// Provides quick, string description about the 'Agent' instance
public override string ToDisplay()
{
    // This method is often used on View
    // there for provide clear and understandable
    // set of properties (separated by spaces ' ')
    // which will quickly provide information about this instance
     return Code;
}
/// Returned value should be handled as business unique key.
/// Equal method (implemented in Tracked base class) will use it 
/// to compare two Agents
protected override string GetDomainObjectSignature()
{
    // TODO return the property (or list of properties separated by "|")
    // which will allow to distinguish among entities
   return Code;
}
#endregion override abstract

`AgentModel` 的具体类如下所示。

/// <summary>
/// All data and binding properties for 'Agent' Views
/// </summary>
public class AgentModel : BaseEntityModel<Agent>, IAgentModel
{
    /// <summary>
    /// Sets ShowExportAction == true (enables the Excel export)
    /// </summary>
    public AgentModel()
       : 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 AgentSearch SearchParam
    {
       get { return base.SearchParam as AgentSearch; }
       set { base.SearchParam = value; }
     }
}

学习 Catharsis 框架工作原理的实用方法是实际添加新实体并对其进行操作,这将在下一章中进行。

本系列的所有教程都可以在此处找到: https://codeproject.org.cn/script/Articles/MemberArticles.aspx?amid=4013680

© . All rights reserved.