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

使用 MyGeneration 框架和 Visual Studio C# .NET 进行面向对象应用程序开发的完整指南

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (16投票s)

2013年8月14日

CPOL

16分钟阅读

viewsIcon

67208

downloadIcon

5380

一篇关于使用 MyGeneration 代码生成框架进行应用程序开发的完整指南。本文将开发一个会议室预订 Web 应用程序。

简介  

在本文中,我将解释在 MyGeneration 代码生成平台的基础上开发 Microsoft.NET Web 应用程序(此处为 ASP.NET C#)所涉及的步骤。我将通过一个小型 MS SQL 2008 Express 数据库开发一个示例应用程序。虽然本文使用 Web 应用程序和 Microsoft SQL Server,但该解决方案也适用于 MySQL 和其他 UI 类型,如 Windows、控制台应用程序。最重要的是,我对这种方法充满信心,因为我已将其用于商业应用程序中的大型项目,并且运行良好。 

第一部分 - 理解应用程序需求

理解需求 

这是软件开发中最重要的方面之一。它与医生在开始任何治疗之前诊断疾病同样重要。一旦您正确地获得了需求,开发就会变得更加清晰。我不会在这里花一整天的时间,因为这是一个庞大的领域,您可以在网上或图书馆找到大量的文章、书籍等。提到这一点的主要原因是只是为了强调需求收集的重要性。  

本文的示例应用程序 - 会议室预订系统

除了教程,我们还将开发一个基于 Web 的应用程序,用于管理公司内部的会议室预订。为了保持简单和简洁,我们将出于时间和可用性的考虑,不追求过于花哨的功能。    

 我们应用程序的需求  

  • 可通过内部 intranet 网络访问的 Web 应用程序。 
  • 列出房间、房间类型和房间预订  
  • 允许添加/编辑/删除房间、房间类型和预订
  •  存储每次预订与会者的姓名和电子邮件地址 
  • 向与会者发送提醒和取消通知 
  • 由于我们假设该应用程序仅在内部可用,因此任何人都可以访问或修改预订详细信息。   

第二部分 - MyGeneration 框架

MyGeneration 框架是一个开源的、基于模板的代码生成软件。一旦您根据需求配置了模板,其余的应用程序开发就会变得非常容易,您可以节省宝贵的时间并消除人为错误。有关此框架的更多详细信息,请关注以下链接。在您能够轻松理解本文的其余部分之前,理解框架非常重要。   

MyGeneration 下载链接  

MyGeneration 框架快速参考指南

通用教程和文章

有用的 MySQL & .NET 文章

我希望您在阅读了上述链接和文章后对 MyGeneration 有了一个很好的了解。因此,在第三部分,我们将着手进行编程的核心部分,但在那之前,让我们将模板配置为满足我们的需求,以便我们可以生成所需的代码。      

 

MyGeneration 模板配置 

我已经打包了预配置的模板,它们可以与 MS SQL 2008 和 C#.NET 4.0 一起使用。应该也可以与更高版本的 .NET 框架一起使用,但我没有在任何其他 .NET 框架版本上进行测试。模板可在上面的链接中下载。您应该将这些解压缩的文件放在 *C:/Programe Files/Mygeneration13/* 目录下,这样在您使用 MyGeneration 客户端时就可以更轻松地浏览。每次尝试打开模板时,MyGeneration 客户端都会查看此目录。这些模板文件以 .vbgen 扩展名保存。   

供您参考,我很久以前从 MyGeneration 下载了这些模板,并对其进行了修改,使其能够与 C#.NET 4.0 和 MS SQL 2008 一起使用。我添加了一些额外的代码生成方法,例如用于 LINQ 的泛型列表创建、列名列表等。随意根据您的需求修改它们,如果您发现任何更好的方法,请与大家分享。  

在 MyGeneration 客户端中设置连接字符串 

MyGeneration 客户端可以与多个连接一起使用,您可以保存任意多个连接。只需选择您需要使用的连接,然后单击右上角的保存按钮。这将是当前上下文中用于使用所需模板创建所有代码的数据库。  

第三部分 - 应用程序开发  

决定应用程序架构  

在这一部分,我们将开发前面讨论的实际应用程序。当涉及到应用程序开发时,应用程序架构是一个关键步骤。架构应该对其他程序员也易于理解。在这种情况下,我们有应用程序的问题域和需求。实际上,我们在这方面开发了一个非常简单的应用程序,但我们将演示多层方法。当我说多层应用程序时,这意味着我们将分离数据、业务逻辑和表示层。在我们的例子中,我们将应用程序分成以下几层。 

  • 数据层或数据库     
  • 数据访问层   
  • 业务逻辑层 
  • 实用工具层 - (注意:此层未使用,但包含在源项目代码中)
  • GUI 层(Windows 或 Web) 
  • 基础框架 - 您不必真正创建此项,因为它已包含在文章中。您可以将其作为项目引用或直接 DLL 引用添加到解决方案中的项目中。   

数据层  

如本文前面所述,我们将使用 MS SQL Server 2008 Express 版本作为我们的数据库引擎。(您也可以使用 MySQL,但那样的话,您将需要使用 MySQL 模板来生成代码,如本文后面所述)。为简单起见,我们将使数据模型非常简单。 正如您从下面的模型中看到的,我们将只有四个表来管理我们的预订。 

  • RoomType:此表将存储公司内的所有可能房间类型。 
  • Room:包含房间数据的表。
  • Booking:此表将包含预订日期和时间等数据。  
  • RoomAttendee:存储每次预订的与会者详细信息。

创建存储过程  

我们将使用 *MyGen_Template_SQL_StoredProcs.vbgen* 模板来创建存储过程。打开 MyGeneration 软件客户端,然后选择用于创建存储过程的已配置模板。

 

选择我们数据库“RoomData”下的所有表,如上图所示。一旦生成了 SQL 脚本,您就可以直接在 MS SQL IDE 中使用 Management Studio 运行它,或者如果您更喜欢在应用程序平台内进行数据库任务,则可以使用 Visual Studio。在大多数情况下,我倾向于直接将 SQL 脚本从 MyGeneration 输出复制到 MS SQL Management Studio 并按 F5 键。   

注意您永远不应手动修改或重命名这些存储过程,因为当您再次使用此模板时,它们将被重新创建,并且存储过程在数据访问层中按原样使用。如果您需要在数据库中自定义 SQL 脚本,您应该将它们与这些自动生成的脚本明确分开。

完成的数据模型应与下图类似...

一旦创建了存储过程,我们就完成了此应用程序的数据模型。下一步是开发应用程序本身。   

Visual Studio 解决方案 

为了将概念模型转化为实际形状,让我们开始在 Visual Studio 解决方案中创建以下项目。如本文架构部分前面所述,我们将把解决方案分成 5 个不同的层,每个层通常是一个项目,如下图所示。 

MyGeneration.doodads_2005 - (C#.NET 类库) 随源代码提供的基础库。您通常不需要为此项目进行修改,除非您需要进一步扩展它。  

RoomBooking.DAL - (C#.NET 类库) - 数据访问层 - 包含由代码生成自动创建的 抽象 类的项目。请注意,您**不得**直接修改这些类,因为代码生成将始终覆盖它们。您必须通过继承在业务逻辑层中使用这些类。  

RoomBooking.BLL - (C#.NET 类库) - 业务逻辑层 - 包含继承自数据访问层的类的项目。这是您的主要工作区域项目。请注意,这些类**不会**被代码生成覆盖,因此您可以大胆地在这些类中编写自定义方法。 

RoomBooking.Util - (C#.NET 库) - 包含实用程序或帮助类(如发送电子邮件的代码、读取 XML 文件等)的项目。将其作为独立项目使用的原因是,如果需要,它可以被解决方案中的任何其他项目使用。  

RoomBookingUI - (ASP.NET C# Web 项目) - 这是我们的用户界面。我们将使用 Microsoft Visual Studio 提供的 ASP.NET Web 应用程序。业务逻辑应该发生在上面讨论的层中。界面应仅用于 Web 元素,并利用其他层完成的智能工作。请将您的全部注意力集中在使其成为一个美观且响应迅速的 Web 应用程序上。  

 

数据访问层  

如上文所述,此项目将包含抽象类。要创建这些类,请打开 MyGeneration 应用程序。通过浏览到模板目录打开模板,然后选择 *MyGen_Template_CSharp_SQL_dOOdads_AbstractClass*。单击运行(播放按钮)或按 F5。一旦您的连接字符串设置为正确的数据库,您将看到如下对话框。  

 

输出文件路径:这必须是您的 DAL 项目,如前面所述。如果您愿意,也可以将这些类放在子文件夹下。   

 

命名空间:类的命名空间。(通常是类库名称)。您可以更改它,但建议将其保留为默认项目名称,因为这是 Visual Studio 的默认行为。

选择您希望创建类的数据库和表。我建议保留“在文件名下划线前添加下划线”的勾选状态,因为这将清楚地将抽象类(数据访问层)与具体类(业务逻辑层)区分开。单击“确定”,您应该会在项目目录中看到创建的类。刷新解决方案或使用“添加现有项”到项目,然后手动将这些类添加到 DAL 项目。 

您不应手动修改此项目中的任何类,因为当您再次使用上述模板时,这些类将被覆盖。   

如果您想一窥代码,下面是为 RoomType 表创建的类。 正如您所见,该类已经准备就绪,开箱即用地提供了所有必需的方法和属性。  

/*
'===============================================================================
'  Generated From - MyGen_Template_CSharp_SQL_dOOdads_AbstractClass.vbgen
' 
'  Author: Gurdeep Singh
'  Email: gurdeeptoor@yahoo.ie
'  
'  ** IMPORTANT  ** 
'  How to Generate your stored procedures:
' 
'  SQL        = SQL_StoredProcs.vbgen
'  ACCESS     = Access_StoredProcs.vbgen
'  ORACLE     = Oracle_StoredProcs.vbgen
'  FIREBIRD   = FirebirdStoredProcs.vbgen
'  POSTGRESQL = PostgreSQL_StoredProcs.vbgen
'
'  The supporting base class SqlClientEntity is in the Architecture directory in "dOOdads".
'  
'  This object is 'abstract' which means you need to inherit from it to be able
'  to instantiate it.  This is very easilly done. You can override properties and
'  methods in your derived class, this allows you to regenerate this class at any
'  time and not worry about overwriting custom code. 
'
'  NEVER EDIT THIS FILE.
'
'  public class YourObject :  _YourObject
'  {
'
'  }
'
'===============================================================================
*/

// Generated by MyGeneration Version # (1.3.1.1)

using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Specialized;
using MyGeneration.dOOdads;
using System.Collections.Generic;

namespace RoomBooking.DAL
{
    public abstract class _RoomType : SqlClientEntity
    {
        public _RoomType()
        {
            this.QuerySource = "RoomType";
            this.MappingName = "RoomType";

        }

        public List<string> ColumnsNamesList()
        {
            List<string> ColList = new List<string>();
            ColList.Add("RoomTypeID");
            ColList.Add("RoomTypeName");
            ColList.Add("RoomTypeDesc");
            ColList.Add("Active");

            return ColList;
        }

        //=================================================================
        //  public Overrides void AddNew()
        //=================================================================
        //
        //=================================================================
        public override void AddNew()
        {
            base.AddNew();
        }

        public override void FlushData()
        {
            this._whereClause = null;
            this._aggregateClause = null;
            base.FlushData();
        }

        //=================================================================
        //  	public Function LoadAll() As Boolean
        //=================================================================
        //  Loads all of the records in the database, and sets the currentRow to the first row
        //=================================================================
        public bool LoadAll()
        {
            ListDictionary parameters = null;

            return base.LoadFromSql("[" + this.SchemaStoredProcedure + 
                    "proc_RoomTypeLoadAll]", parameters);
        }

        //=================================================================
        // public Overridable Function LoadByPrimaryKey()  As Boolean
        //=================================================================
        //  Loads a single row of via the primary key
        //=================================================================
        public virtual bool LoadByPrimaryKey(int RoomTypeID)
        {
            ListDictionary parameters = new ListDictionary();
            parameters.Add(Parameters.RoomTypeID, RoomTypeID);


            return base.LoadFromSql("[" + this.SchemaStoredProcedure + 
                    "proc_RoomTypeLoadByPrimaryKey]", parameters);
        }

        #region Parameters
        protected class Parameters
        {

            public static SqlParameter RoomTypeID
            {
                get
                {
                    return new SqlParameter("@RoomTypeID", SqlDbType.Int, 0);
                }
            }

            public static SqlParameter RoomTypeName
            {
                get
                {
                    return new SqlParameter("@RoomTypeName", SqlDbType.NVarChar, 50);
                }
            }

            public static SqlParameter RoomTypeDesc
            {
                get
                {
                    return new SqlParameter("@RoomTypeDesc", SqlDbType.NVarChar, 100);
                }
            }

            public static SqlParameter Active
            {
                get
                {
                    return new SqlParameter("@Active", SqlDbType.Bit, 0);
                }
            }

        }
        #endregion

        #region ColumnNames
        public class ColumnNames
        {
            public const string RoomTypeID = "RoomTypeID";
            public const string RoomTypeName = "RoomTypeName";
            public const string RoomTypeDesc = "RoomTypeDesc";
            public const string Active = "Active";

            static public string ToPropertyName(string columnName)
            {
                if (ht == null)
                {
                    ht = new Hashtable();

                    ht[RoomTypeID] = _RoomType.PropertyNames.RoomTypeID;
                    ht[RoomTypeName] = _RoomType.PropertyNames.RoomTypeName;
                    ht[RoomTypeDesc] = _RoomType.PropertyNames.RoomTypeDesc;
                    ht[Active] = _RoomType.PropertyNames.Active;

                }
                return (string)ht[columnName];
            }

            static private Hashtable ht = null;
        }
        #endregion

        #region PropertyNames
        public class PropertyNames
        {
            public const string RoomTypeID = "RoomTypeID";
            public const string RoomTypeName = "RoomTypeName";
            public const string RoomTypeDesc = "RoomTypeDesc";
            public const string Active = "Active";

            static public string ToColumnName(string propertyName)
            {
                if (ht == null)
                {
                    ht = new Hashtable();

                    ht[RoomTypeID] = _RoomType.ColumnNames.RoomTypeID;
                    ht[RoomTypeName] = _RoomType.ColumnNames.RoomTypeName;
                    ht[RoomTypeDesc] = _RoomType.ColumnNames.RoomTypeDesc;
                    ht[Active] = _RoomType.ColumnNames.Active;

                }
                return (string)ht[propertyName];
            }

            static private Hashtable ht = null;
        }
        #endregion

        #region StringPropertyNames
        public class StringPropertyNames
        {
            public const string RoomTypeID = "s_RoomTypeID";
            public const string RoomTypeName = "s_RoomTypeName";
            public const string RoomTypeDesc = "s_RoomTypeDesc";
            public const string Active = "s_Active";

        }
        #endregion

        #region Properties

        public virtual int RoomTypeID
        {
            get
            {
                return base.Getint(ColumnNames.RoomTypeID);
            }
            set
            {
                base.Setint(ColumnNames.RoomTypeID, value);
            }
        }

        public virtual string RoomTypeName
        {
            get
            {
                return base.Getstring(ColumnNames.RoomTypeName);
            }
            set
            {
                base.Setstring(ColumnNames.RoomTypeName, value);
            }
        }

        public virtual string RoomTypeDesc
        {
            get
            {
                return base.Getstring(ColumnNames.RoomTypeDesc);
            }
            set
            {
                base.Setstring(ColumnNames.RoomTypeDesc, value);
            }
        }

        public virtual bool Active
        {
            get
            {
                return base.Getbool(ColumnNames.Active);
            }
            set
            {
                base.Setbool(ColumnNames.Active, value);
            }
        }


        #endregion

        #region String Properties

        public virtual string s_RoomTypeID
        {
            get
            {
                return this.IsColumnNull(ColumnNames.RoomTypeID) ? 
                  string.Empty : base.GetintAsString(ColumnNames.RoomTypeID);
            }
            set
            {
                if (string.Empty == value)
                    this.SetColumnNull(ColumnNames.RoomTypeID);
                else
                    this.RoomTypeID = base.SetintAsString(ColumnNames.RoomTypeID, value);
            }
        }

        public virtual string s_RoomTypeName
        {
            get
            {
                return this.IsColumnNull(ColumnNames.RoomTypeName) ? 
                  string.Empty : base.GetstringAsString(ColumnNames.RoomTypeName);
            }
            set
            {
                if (string.Empty == value)
                    this.SetColumnNull(ColumnNames.RoomTypeName);
                else
                    this.RoomTypeName = base.SetstringAsString(ColumnNames.RoomTypeName, value);
            }
        }

        public virtual string s_RoomTypeDesc
        {
            get
            {
                return this.IsColumnNull(ColumnNames.RoomTypeDesc) ? 
                  string.Empty : base.GetstringAsString(ColumnNames.RoomTypeDesc);
            }
            set
            {
                if (string.Empty == value)
                    this.SetColumnNull(ColumnNames.RoomTypeDesc);
                else
                    this.RoomTypeDesc = base.SetstringAsString(ColumnNames.RoomTypeDesc, value);
            }
        }

        public virtual string s_Active
        {
            get
            {
                return this.IsColumnNull(ColumnNames.Active) ? 
                  string.Empty : base.GetboolAsString(ColumnNames.Active);
            }
            set
            {
                if (string.Empty == value)
                    this.SetColumnNull(ColumnNames.Active);
                else
                    this.Active = base.SetboolAsString(ColumnNames.Active, value);
            }
        }


        #endregion

        #region Where Clause
        public class WhereClause
        {
            public WhereClause(BusinessEntity entity)
            {
                this._entity = entity;
            }

            public TearOffWhereParameter TearOff
            {
                get
                {
                    if (_tearOff == null)
                    {
                        _tearOff = new TearOffWhereParameter(this);
                    }

                    return _tearOff;
                }
            }

            #region WhereParameter TearOffs
            public class TearOffWhereParameter
            {
                public TearOffWhereParameter(WhereClause clause)
                {
                    this._clause = clause;
                }


                public WhereParameter RoomTypeID
                {
                    get
                    {
                        WhereParameter where = 
                          new WhereParameter(ColumnNames.RoomTypeID, Parameters.RoomTypeID);
                        this._clause._entity.Query.AddWhereParameter(where);
                        return where;
                    }
                }

                public WhereParameter RoomTypeName
                {
                    get
                    {
                        WhereParameter where = 
                          new WhereParameter(ColumnNames.RoomTypeName, Parameters.RoomTypeName);
                        this._clause._entity.Query.AddWhereParameter(where);
                        return where;
                    }
                }

                public WhereParameter RoomTypeDesc
                {
                    get
                    {
                        WhereParameter where = 
                          new WhereParameter(ColumnNames.RoomTypeDesc, Parameters.RoomTypeDesc);
                        this._clause._entity.Query.AddWhereParameter(where);
                        return where;
                    }
                }

                public WhereParameter Active
                {
                    get
                    {
                        WhereParameter where = new WhereParameter(ColumnNames.Active, Parameters.Active);
                        this._clause._entity.Query.AddWhereParameter(where);
                        return where;
                    }
                }


                private WhereClause _clause;
            }
            #endregion

            public WhereParameter RoomTypeID
            {
                get
                {
                    if (_RoomTypeID_W == null)
                    {
                        _RoomTypeID_W = TearOff.RoomTypeID;
                    }
                    return _RoomTypeID_W;
                }
            }

            public WhereParameter RoomTypeName
            {
                get
                {
                    if (_RoomTypeName_W == null)
                    {
                        _RoomTypeName_W = TearOff.RoomTypeName;
                    }
                    return _RoomTypeName_W;
                }
            }

            public WhereParameter RoomTypeDesc
            {
                get
                {
                    if (_RoomTypeDesc_W == null)
                    {
                        _RoomTypeDesc_W = TearOff.RoomTypeDesc;
                    }
                    return _RoomTypeDesc_W;
                }
            }

            public WhereParameter Active
            {
                get
                {
                    if (_Active_W == null)
                    {
                        _Active_W = TearOff.Active;
                    }
                    return _Active_W;
                }
            }

            private WhereParameter _RoomTypeID_W = null;
            private WhereParameter _RoomTypeName_W = null;
            private WhereParameter _RoomTypeDesc_W = null;
            private WhereParameter _Active_W = null;

            public void WhereClauseReset()
            {
                _RoomTypeID_W = null;
                _RoomTypeName_W = null;
                _RoomTypeDesc_W = null;
                _Active_W = null;

                this._entity.Query.FlushWhereParameters();

            }

            private BusinessEntity _entity;
            private TearOffWhereParameter _tearOff;

        }

        public WhereClause Where
        {
            get
            {
                if (_whereClause == null)
                {
                    _whereClause = new WhereClause(this);
                }

                return _whereClause;
            }
        }

        private WhereClause _whereClause = null;
        #endregion

        #region Aggregate Clause
        public class AggregateClause
        {
            public AggregateClause(BusinessEntity entity)
            {
                this._entity = entity;
            }

            public TearOffAggregateParameter TearOff
            {
                get
                {
                    if (_tearOff == null)
                    {
                        _tearOff = new TearOffAggregateParameter(this);
                    }

                    return _tearOff;
                }
            }

            #region AggregateParameter TearOffs
            public class TearOffAggregateParameter
            {
                public TearOffAggregateParameter(AggregateClause clause)
                {
                    this._clause = clause;
                }


                public AggregateParameter RoomTypeID
                {
                    get
                    {
                        AggregateParameter aggregate = 
                          new AggregateParameter(ColumnNames.RoomTypeID, Parameters.RoomTypeID);
                        this._clause._entity.Query.AddAggregateParameter(aggregate);
                        return aggregate;
                    }
                }

                public AggregateParameter RoomTypeName
                {
                    get
                    {
                        AggregateParameter aggregate = 
                          new AggregateParameter(ColumnNames.RoomTypeName, Parameters.RoomTypeName);
                        this._clause._entity.Query.AddAggregateParameter(aggregate);
                        return aggregate;
                    }
                }

                public AggregateParameter RoomTypeDesc
                {
                    get
                    {
                        AggregateParameter aggregate = 
                          new AggregateParameter(ColumnNames.RoomTypeDesc, Parameters.RoomTypeDesc);
                        this._clause._entity.Query.AddAggregateParameter(aggregate);
                        return aggregate;
                    }
                }

                public AggregateParameter Active
                {
                    get
                    {
                        AggregateParameter aggregate = 
                          new AggregateParameter(ColumnNames.Active, Parameters.Active);
                        this._clause._entity.Query.AddAggregateParameter(aggregate);
                        return aggregate;
                    }
                }


                private AggregateClause _clause;
            }
            #endregion

            public AggregateParameter RoomTypeID
            {
                get
                {
                    if (_RoomTypeID_W == null)
                    {
                        _RoomTypeID_W = TearOff.RoomTypeID;
                    }
                    return _RoomTypeID_W;
                }
            }

            public AggregateParameter RoomTypeName
            {
                get
                {
                    if (_RoomTypeName_W == null)
                    {
                        _RoomTypeName_W = TearOff.RoomTypeName;
                    }
                    return _RoomTypeName_W;
                }
            }

            public AggregateParameter RoomTypeDesc
            {
                get
                {
                    if (_RoomTypeDesc_W == null)
                    {
                        _RoomTypeDesc_W = TearOff.RoomTypeDesc;
                    }
                    return _RoomTypeDesc_W;
                }
            }

            public AggregateParameter Active
            {
                get
                {
                    if (_Active_W == null)
                    {
                        _Active_W = TearOff.Active;
                    }
                    return _Active_W;
                }
            }

            private AggregateParameter _RoomTypeID_W = null;
            private AggregateParameter _RoomTypeName_W = null;
            private AggregateParameter _RoomTypeDesc_W = null;
            private AggregateParameter _Active_W = null;

            public void AggregateClauseReset()
            {
                _RoomTypeID_W = null;
                _RoomTypeName_W = null;
                _RoomTypeDesc_W = null;
                _Active_W = null;

                this._entity.Query.FlushAggregateParameters();

            }

            private BusinessEntity _entity;
            private TearOffAggregateParameter _tearOff;

        }

        public AggregateClause Aggregate
        {
            get
            {
                if (_aggregateClause == null)
                {
                    _aggregateClause = new AggregateClause(this);
                }

                return _aggregateClause;
            }
        }

        private AggregateClause _aggregateClause = null;
        #endregion

        protected override IDbCommand GetInsertCommand()
        {

            SqlCommand cmd = new SqlCommand();
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "[" + this.SchemaStoredProcedure + "proc_RoomTypeInsert]";

            CreateParameters(cmd);

            SqlParameter p;
            p = cmd.Parameters[Parameters.RoomTypeID.ParameterName];
            p.Direction = ParameterDirection.Output;

            return cmd;
        }

        protected override IDbCommand GetUpdateCommand()
        {

            SqlCommand cmd = new SqlCommand();
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "[" + this.SchemaStoredProcedure + "proc_RoomTypeUpdate]";

            CreateParameters(cmd);

            return cmd;
        }

        protected override IDbCommand GetDeleteCommand()
        {

            SqlCommand cmd = new SqlCommand();
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "[" + this.SchemaStoredProcedure + "proc_RoomTypeDelete]";

            SqlParameter p;
            p = cmd.Parameters.Add(Parameters.RoomTypeID);
            p.SourceColumn = ColumnNames.RoomTypeID;
            p.SourceVersion = DataRowVersion.Current;


            return cmd;
        }

        private IDbCommand CreateParameters(SqlCommand cmd)
        {
            SqlParameter p;

            p = cmd.Parameters.Add(Parameters.RoomTypeID);
            p.SourceColumn = ColumnNames.RoomTypeID;
            p.SourceVersion = DataRowVersion.Current;

            p = cmd.Parameters.Add(Parameters.RoomTypeName);
            p.SourceColumn = ColumnNames.RoomTypeName;
            p.SourceVersion = DataRowVersion.Current;

            p = cmd.Parameters.Add(Parameters.RoomTypeDesc);
            p.SourceColumn = ColumnNames.RoomTypeDesc;
            p.SourceVersion = DataRowVersion.Current;

            p = cmd.Parameters.Add(Parameters.Active);
            p.SourceColumn = ColumnNames.Active;
            p.SourceVersion = DataRowVersion.Current;


            return cmd;
        }
    }
}

下面是数据访问层的类图 

业务逻辑层

现在您已经完成了数据层和数据访问层。下一步是业务逻辑层,您将在其中实际开始手动编写代码。但为了让您开始使用这些类,有一个模板可以为您创建一些开箱即用的方法。要创建这些类,请从模板目录中选择 *MyGen_Template_CSharp_SQL_dOOdads_ConcreteClass*,然后运行此模板。您将看到如下对话框。  

 

输出文件路径:使用 BAL 项目目录来放置您的具体类。(注意:如果您决定将数据访问和业务逻辑层合并到单个项目中,这可能与 DAL 项目相同。)

业务命名空间:业务逻辑层中的类使用的命名空间。(通常是项目名称)。 

数据访问命名空间:数据访问层类使用的数据访问命名空间。 

选择表并单击“确定”。将这些创建的类导入您的业务逻辑层(RoomBooking.BLL)项目。如果任何类已存在,此模板**不会覆盖**旧类。您可以按需扩展这些类。下面是 Class Room 的示例,即数据库中的 Room 表。注意其从对应数据访问类的 _Room 继承。另请注意,我在此类中手动编写的自定义方法,用于获取此类 RoomType 对象。 

//Generator Framework: MyGeneration Version: # (1.3.1.1)
//Author: Gurdeep Singh 
//Email:  gurdeeptoor@yahoo.ie

using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using MyGeneration.dOOdads;
using RoomBooking.DAL;

namespace RoomBooking.BLL
{
    public class Room : _Room
    {


        //Search Type Enum
        public enum SearchType
        {
            Equal,
            StartsWith,
            EndsWith,
            Wild,
        }

        //Sort Direction Type Enum
        public enum SortDirection
        {
            Asc,
            Desc,
        }

        /// <summary>
        /// Construct the class with passing Dynamic connection string.
        /// </summary>
        /// <param name="ConnectionString">Connection string 
        ///             from web.config or app.config file</param>
        public Room(string ConnectionString)
        {
            this.ConnectionString = ConnectionString;
        }

        //Load all with applying sort
        public bool LoadAll(bool ApplySort)
        {
            bool _Loadall = true;

            _Loadall = this.LoadAll();

            if (_Loadall && ApplySort)
            {
                this.Sort = Room.ColumnNames.RoomName;
            }

            return _Loadall;
        }

        /// <summary>
        /// Load by a Single Column Value
        /// </summary>
        /// <param name="ColumnName">Name of Column</param>
        /// <param name="ColumnValue">Value to be Searched</param>
        /// <param name="SearchType">Search Type Enum</param>
        /// <returns></returns>
        public bool LoadByColumnValue(string ColumnName, string ColumnValue, SearchType SearchTypeOption)
        {
            bool _Loadall = true;

            _Loadall = this.LoadAll();

            if (_Loadall)
            {
                switch (SearchTypeOption)
                {
                    case SearchType.Equal:
                        this.Filter = ColumnName + "='" + ColumnValue + "'";
                        return _Loadall;

                    case SearchType.StartsWith:
                        this.Filter = ColumnName + " Like '%" + ColumnValue + "'";
                        return _Loadall;

                    case SearchType.EndsWith:
                        this.Filter = ColumnName + " Like '" + ColumnValue + "%'";
                        return _Loadall;

                    case SearchType.Wild:
                        this.Filter = ColumnName + " Like '%" + ColumnValue + "%'";
                        return _Loadall;
                }
            }

            return _Loadall;
        }


        /// <summary>
        /// ConvertToList : You must load the object before converting to list.
        /// </summary>
        /// <param name="InputObject">Input Object pre populated</param>
        /// <returns>generic list of Room collection</returns>
        public List<Room> ConvertToList(Room InputObject)
        {
            List<Room> List = new List<Room>();
            List<DataRow> dlist = new List<DataRow>();
            dlist = InputObject.DefaultView.ToTable().AsEnumerable().ToList<DataRow>();

            foreach (DataRow row in dlist)
            {
                Room InsObject = new Room(this.ConnectionString);
                InsObject.AddNew();
                //This method below is in BusinessEntity class in MyGeneration framework
                DataRowToObject(InsObject, row);
                List.Add(InsObject);
            }

            return List;
        }

        /// <summary>
        /// ConvertToList : You must load the object before converting to list.
        /// </summary>
        /// <param name="InputObject">Input Object pre populated</param>
        /// <param name="sort">Column to Sort</param>
        /// <param name="sortcolumn">Sort direction</param>
        /// <param name="page">Page number to display</param>
        /// <param name="numRows">Total number of records to be displayed on page</param>
        /// <returns>Sorted generic collection of Continent limited to one page data</returns>
        public List<Room> ConvertToList(Room InputObject, 
          string sortcolumn, SortDirection sortDir, int page, int numRows)
        {
            List<Room> List = new List<Room>();
            //Sor this object first
            if (sortcolumn != null)
                InputObject.Sort = string.Format("{0} {1}", sortcolumn, sortDir.ToString());

            List = ConvertToList(InputObject);
            return List.AsQueryable().Skip((page - 1) * numRows).Take(numRows).ToList<Room>();
        }

        /// <summary>
        /// Get RoomType for this Room
        /// </summary>
        /// <returns></returns>
        public RoomType GetRoomType()
        {
            RoomType RoomType = new RoomType(this.ConnectionString);
            RoomType.LoadByPrimaryKey(this.RoomTypeID);
            return RoomType;
        }

    }
} 

下面是业务逻辑层的类图 

实用工具层

此项目是为了方便起见,并且为了保持简单,在本例中我们没有使用它。您可以将任何可重用的方法包含在此项目中,例如,我们本可以将发送电子邮件的代码放在这里。  

UI 层 - ASP.NET Web 应用程序 

此时,您更像是一名 Web 设计师,而不是应用程序开发人员。在一个更大的团队中,这部分应该与上述层并行完成。在正常情况下,设计概念会提前与最终用户达成一致。无论如何,假设只有您一个人在工作,除了需求之外,还没有为这部分做任何事情。   

注意:您可以使用任何方法论或方法来设计 Web 应用程序,例如 MVC 等。本文将坚持使用 Microsoft ASP.NET Web 应用程序结合 C#.NET 的常规方法。  

在 Web 应用程序开发方面,我是用户控件的忠实拥护者。由于这是一个非常简单的应用程序,我们只需要三个主要部分,即 Room、RoomType 和 Bookings(这里指的是对象)。因此,您可以假设 Room 和 Room 类型是常规的列表和编辑操作。因此,让我们开始列出和编辑 RoomTypes。

列出/编辑 RoomTypes: 要列出 RoomTypes,我们将使用 Web 窗体上的 DataGridView。加载时,此页面将显示系统中可用的所有房间类型。要通过代码实现这一点,我们将使用业务逻辑层的 List 对象,即 RoomTypes 的列表,并将其绑定到 GridView。请看下面的代码片段...  

private void LoadRoomTypeList()
{
    RoomType RoomType = new RoomType(AppGlobals.ConnectionStrings.cstrRoomData);
    RoomType.LoadAll(true);
    gvRoomType.DataSource = RoomType.DefaultView;
    gvRoomType.DataBind();
}

现在,通过查看上面的代码,您可能会提出的明显问题是,“AppGlobals.ConnectionStrings.ctrRoomdata”是什么?它只是传递给业务对象的连接字符串。 业务逻辑层中创建的每个业务对象都将其作为初始化参数。 AppGlobals 是一个静态类,用于存储此类静态值以在应用程序中使用。您可以想象我们必须多少次使用这些连接字符串,并且将它们硬编码在各处可能不是一个好主意。

下面是在 Web 窗体上显示的房间类型列表。 

要进行编辑,我们将使用用户控件,并通过 Ajax Popup extender 以弹出窗口的形式在页面上可用。用户控件本质上是一个表格形式。请注意,此用户控件会引发两个自定义事件:SaveClicked 和 CancelClicked。这样做是为了处理 Ajax Popup Extender,因为 Popup Extender 只能(至少很容易地)从创建它的页面进行控制。  

从用户控件处理弹出扩展程序:每次用户单击“保存”或“取消”时,我们都需要隐藏弹出窗口并显示主页面。我们将在用户控件上创建两个自定义事件,并在主页面上处理它们。  

public EventHandler SaveClicked;
public EventHandler CancelClicked; 

protected void btnSave_Click(object sender, EventArgs e)
{
    //validate the input...
    Validate();

    RoomType RoomType = new RoomType(AppGlobals.ConnectionStrings.cstrRoomData);

    if (Page.IsValid)
    {
        if (lblRoomTypeID.Text == "0")
        {
            RoomType.AddNew();
            RoomType.Active = true;
            RoomType.RoomTypeName = txtRoomTypeName.Text;
            RoomType.RoomTypeDesc = txtRoomTypeDesc.Text;
            RoomType.RoomTypeID = 0;
            RoomType.Save();
        }
        else
        {
            //load the copy from DB
            RoomType.LoadByPrimaryKey(Convert.ToInt32(lblRoomTypeID.Text));

            //Check if description was changed
            if (RoomType.RoomTypeDesc != txtRoomTypeDesc.Text || 
                        RoomType.RoomTypeName != txtRoomTypeName.Text)
            {
                RoomType.RoomTypeName = txtRoomTypeName.Text;
                RoomType.RoomTypeDesc = txtRoomTypeDesc.Text;
                RoomType.Save();
            }
        }

        //raise the event
        SaveClicked(sender, e);
    }

    else
    {
      
    }
}

protected void btnCancel_Click(object sender, EventArgs e)
{
    CancelClicked(sender, e);
} 

在 Web 窗体上,我们将如下注册这些事件。 

//Register user control event handlers
  ucRoomTypeEdit1.SaveClicked += new EventHandler(ucRoomTypeEdit1_SaveClicked);
  ucRoomTypeEdit1.CancelClicked += new EventHandler(ucRoomTypeEdit1_CancelClicked);  

然后像这样处理它们来显示/隐藏 Web 窗体上的编辑弹出窗口。请注意,此方法用于整个应用程序。  

protected void ucRoomTypeEdit1_SaveClicked(object sender, EventArgs e)
{
pnlRoomTypeEdit.Visible = false;
lblInfo.Visible = false;
MPE.Hide();
LoadRoomTypeList();
}

protected void ucRoomTypeEdit1_CancelClicked(object sender, EventArgs e)
{
 pnlRoomTypeEdit.Visible = false;
 lblInfo.Visible = false;
 MPE.Hide();
}

列出/编辑房间:我们将节省时间,因为不再需要再次解释。这与 RoomType 完全相同,只是对象不同。  

列出/编辑预订:我们应用程序 Web 层中最重要的部分。我们需要提供一种简单有效的方式来处理预订。如果您像上面的对象那样列出预订,那么显示预订并不是真正用户友好的方式。开发一些可以处理所有这些出色功能的自定义控件有点挑战。那么为什么不使用 Google 来寻找一些可以满足这些需求的东西呢?花了不长但很长几个小时后,我发现了以下内容,它非常适合我们的需求。

http://www.daypilot.org/calendar.html   

本文档中,我使用了 Daypilot 日历控件的完整功能试用版。它提供了开箱即用的功能,可以处理屏幕编辑、删除和创建日历条目,并为最终用户提供精美的外观。您还可以根据自己的方式对控件进行样式设置。  

数据绑定与 GridView 相同。我们将按房间分割预订显示,以便用户可以选择房间和日期范围来查找或创建相应的预订。下面是将预订数据绑定到日历控件的代码片段。  

int RoomID = ddlRoom.SelectedValue.Length > 0 ? Convert.ToInt32(ddlRoom.SelectedValue) : 0;
int Days = Convert.ToInt32(rdWeekView.SelectedValue) * 5;

Booking Booking = new Booking(AppGlobals.ConnectionStrings.cstrRoomData);
DayPilotCalendar1.Days = Days;
Booking.LoadByColumnValue(Booking.ColumnNames.RoomID, RoomID.ToString(), RoomBooking.BLL.Booking.SearchType.Equal);
DayPilotCalendar1.DataSource = Booking.DefaultView;
DayPilotCalendar1.DataBind();
DayPilotCalendar1.UpdateWithMessage(LoadingMessage); 

上面的代码是完成的预订表单。日历控件的服务器端事件用于处理预订的创建、删除和修改。下面是一些来自此表单的代码片段。 

每当用户选择现有预订时,我们就需要弹出预订编辑表单。

protected void DayPilotCalendar1_EventSelect(object sender, DayPilotEventArgs e)
{
    int SelectedBookingID = Convert.ToInt32(DayPilotCalendar1.SelectedEvents[0].Value);

    //pop-up            
    MPE.Show();
    pnlBookingEdit.Visible = true;
    ucBookingEdit1.LoadData(SelectedBookingID);
}  

除了通过“新建”按钮创建预订外,用户还可以通过拖动鼠标来创建预订,我们需要处理此事件以创建新预订。下面是处理 TimeRangeSelected 事件并将新预订表单显示在弹出窗口中的代码。请注意,我们阻止了过去的日期预订。

protected void DayPilotCalendar1_TimeRangeSelected(object sender, TimeRangeSelectedEventArgs e)
{
    if (e.Start.Date > DateTime.Today.AddDays(-1))
    {
        //pop-up
        MPE.Show();
        pnlBookingEdit.Visible = true;
        ucBookingEdit1.CreateNew(Convert.ToInt32(ddlRoom.SelectedValue), e.Start, e.End, false);
    }
    else
    {
        LoadBookings("New booking for Past date not possible");
    }
}

处理与会者以及通知会议取消和重新安排:与会者与每次预订相关联。正如您所见,这些在预订编辑表单中处理。每次预订创建、移动或删除时,都会通过发送电子邮件通知与会者。请下载源代码以获取详细信息。下面是发送电子邮件的代码,该代码位于 Web 应用程序的 AppGlobals 静态类中。 

public static void SendMeetingEmail(Room Room, Booking RoomBooking, 
           List<RoomAttendee> RoomAttendeeList, string MeetingAction)
{
    int ToEmailCount;
    int DocCount;
    string _EmailRecepients = string.Empty;

    foreach (RoomAttendee roomAttendee in RoomAttendeeList)
    {
        if (_EmailRecepients.Length > 0)
            _EmailRecepients = string.Format("{0};{1}", _EmailRecepients, roomAttendee.Email);
        else
            _EmailRecepients = roomAttendee.Email;
    }

    StringBuilder sb = new StringBuilder();
    sb.AppendLine(string.Format("Hi, <br/><br/> Please Note - " + 
      "Meeting detailed below has been <b>{0}</b> by {1}", 
      MeetingAction, RoomBooking.BookedBy));
    sb.AppendLine("<br/><br/>");
    sb.AppendLine(string.Format("Description: {0}", 
      RoomBooking.BookingNotes.Remove(RoomBooking.BookingNotes.LastIndexOf("-"))));
      //Note we are taking away the user initials
    sb.AppendLine("<br/>");
    sb.AppendLine(string.Format("Location: {0}", Room.RoomName));
    sb.AppendLine("<br/>");
    sb.AppendLine(string.Format("Start Time: {0}", RoomBooking.BookDateFrom));
    sb.AppendLine("<br/>");
    sb.AppendLine(string.Format("End Time: {0}", RoomBooking.BookDateTo));
    sb.AppendLine("<br/>");

    sb.AppendLine("<br/>");
    sb.AppendLine(string.Format("People in this meeting : {0}", RoomAttendeeList.Count));
    sb.AppendLine("<table border='1'>");
    foreach (RoomAttendee roomAttendee in RoomAttendeeList)
    {
        sb.AppendLine(string.Format("<tr><td>{0} {1} {2}" + 
          "</td></tr>", roomAttendee.Title, 
          roomAttendee.FirstName, roomAttendee.LastName));
    }
    sb.AppendLine("</table>");
    sb.AppendLine("<br/>");
    sb.AppendLine("<br/>");
    sb.AppendLine("<i>System generated email, Please DO NOT Reply !!</i>");
    sb.AppendLine("<br/><br/>");
    sb.AppendLine("Best Regards,");
    sb.AppendLine("<br/>");
    sb.AppendLine("Admin");
    sb.AppendLine("<br/>");
    sb.AppendLine("Meeting Room Booking System");

    if (_EmailRecepients.Trim().Length > 0)
    {
        SendEmail(_EmailRecepients,
                  string.Format("Meeting Room Booking System - Meeting {0}", MeetingAction),
                  sb.ToString(),
                  string.Empty,
                   out ToEmailCount,
                  out DocCount);
    }
}

第四部分 - My2ndGeneration:未来的代码生成 

MyGeneration 最近已转向 Web。我还没有详细研究它。看起来是代码生成的未来。更多详情请参见此链接。  

一旦我熟悉了这个新平台,我将在这里发布另一篇文章,请保持联系...!!  

结论       

总而言之,这是开发面向对象应用程序的众多方法之一。就像任何新平台都需要时间来学习一样,MyGeneration 平台也不例外。它需要您花费一些时间来理解它。但可以肯定的是,它是实用的,并且我已经在许多项目中使用了它。它为我们节省了大量的开发时间,而模板的力量使其更加有趣。 如果您认为它对世界有所帮助,请随时提供任何反馈并为本文**投票**。  

编码愉快...!!

© . All rights reserved.