help.axcms.netAxinom Logo
Save Save Chapter Send Feedback

Creating the Article Object and Database Structure

In this part we will create the Article class and the corresponding database structures. This tutorial article also gives you an idea how simple the usage of AxCMS.net persistence framework is.

Data Model

Product catalog describes different products (this term will be used interchangeably with ‘articles’ throughout this tutorial). These products are divided into different product groups that have a certain hierarchical structure. Each product has a description, a price and a thumbnail that gives the user a better overview of the product.

In the context of this tutorial article, a product is not some kind of special page, but an independent entity.

 

Database

Let’s add a new table to the database. In addition to the Name, Description and Price fields we also need a unique ID defined as a primary key. 

CREATE TABLE dbo.Article (
    [AxID] bigint NOT NULL,
    [Name] nvarchar(50) NOT NULL,
    [Description] ntext NULL,
    [Price] money NOT NULL,
    URL nvarchar(150) NOT NULL,
    PublicationState tinyint NOT NULL DEFAULT ((4)),
    Created datetime NULL,
    Published datetime NULL,
    PublishingRequested smalldatetime NULL
    CONSTRAINT PK_Article PRIMARY KEY ([AxID])
)

To follow the AxCMS naming convention, let’s name the identifier AxID. The data type for this column should be bigint. The reason for this choice will be explained later in this tutorial. Notice that we are not using the IDENTITY property for the AxID column. This is because AxCMS is using a special sequence mechanism for identification.  There is a Sequence table in the database where are all given IDs and their purposes are saved.

For our product we also need to add such a sequence:

INSERT INTO dbo.Sequence
    (Name, NextVal, MinVal, MaxVal, Cache, Cycle, IncrementBy)
VALUES
    ('Article', 1, 1, 2147483646, 10, 0, 1)

All IDs have values between 1 and 2147483646. 10 of them will be cached (1 DB query for 10 values).

We define all text columns as Unicode with the n-prefix (nvarchar, ntext, nchar). Always follow these rules so later you won't have any problems with the encoding or comparison of fields with different data types.

The last step is to register our new element type, so that AxCMS.net would support it better. All element types that AxCMS.net recognizes are defined in the AxElementType table. Let’s add a new type there:

INSERT INTO dbo.AxElementType
    (ElementType, Name, Text)
VALUES
    (101, 'Article', 'Artikel')

You can choose any numeric value for the ID, but try not to use any value smaller than 100, since those ID's are reserved for later versions of AxCMS.

There are more columns in the AxElementType table. These will be explained later in this tutorial.

We advise you to put all the database scripts as part of your application into the project. In Axinom projects such scripts are kept in the project resources under DB/Create. 

Business Logic

Now let's implement some classes. These classes will be based on the persistence framework that you get together with AxCMS.net. This framework helps you to create and manage queries to the database without the need to write much code, so it allows you to work on the SQL level.

To simplify things, let’s reuse the code example in the Generic Adapter pattern article, so the Article class we create will be identical with the one in that example.

You should now create the Article class, inherit it from the AxDbBaseObject and add the AxDbMapToTable attribute to it:

[AxDbMapToTable("Article")]
public class Article : AxDbBaseObject { ... }

For each variable, define its property: 

     private string _name;
    …
    [AxDbMapToField]
    public string Name
    {
        get { return _name; }
        set
        {
            if (value != _name) SetDirty();
            _name = value;
        }
    }


AxID also gets one additional attribute:

    private int _axID; 

    [AxDbPrimaryKey]
    [AxDbMapToField]
    public int AxID
    {
        get { return _axID; }
        set
        {
            if (value != _axID) SetDirty();
            _axID = value;
        }
    }

Now you can start loading and saving products. You can see an example in this UnitTest:

[Test]
public void TestArticle()
{
      string connString = "SERVER=localhost;DATABASE="AxCMS_Sample;UID=XXX;PWD=YYY";
      IConnectionManager connectionManager = new ConnectionManager(new SqlDbFactory(), connString);

     try
    {
        AxDbAdapter adapter = new AxDbAdapter(typeof(Article),connectionManager);
    // create new article

        Article article = new Article();
       article.Name = "Article" + Guid.NewGuid().ToString();
        article.Description = "Article Description";
        article.Price = 199.90m;
        adapter.Save(article);
        Assert.IsTrue(article.AxID > 0);

    // load article
        Article loadedArticle = (Article)
        adapter.Load(article.AxID);

        Assert.IsNotNull(loadedArticle);
        Assert.AreEqual(article.Name, loadedArticle.Name);

    // load all articles
       ArrayList articles = adapter.LoadAll();
        Assert.IsTrue(articles.Count > 0);

    // delete article
        adapter.Delete(article);
    }
    finally
    {
        connectionManager.CloseConnection();
    }
}

When you save a new product, it automatically gets a new AxID from the sequence and sets it to the AxID property for this product. Now let's implement the IClassifiable interface in order to make the Article class suitable for our CMS:

public IClassifiable
{
      short Type;
      long ID;

Each object should have two arguments: ID and Type. The name IClassifiable shows us that the object can be categorized. It’s worth taking the time to implement this interface, because its use is not just limited to this functionality.

It's easy to do. Just take the AxID that we have (now you should realize why we have chosen (C#: long) and not int (C#: int)). We have already defined the element type in the database so let's just add one more enumeration into BL level in order to except ‘magic numbers’.

public enum SampleElementType
{
      Article = 101
}

Now the Article class looks like this:

public class Article : AxDbBaseObject, IClassifiable
{
       ...
      public long ID
      {
            get{return this.AxID;}
            set{this.AxID = value;}
      }
      public long Type
      {
            get{return (short) SampleElementType.Article;}
            set{}
      }
}

We need to make our product publishable so our object will be available in the live system. Let's implement the IPublishable interface:

public IPublishable
    {
        DateTime Created;
        string Name;
        PublicationState PublicationState;
        DateTime Published;
        string Url;

        void SetPublicationStateDirty();
    }

Now the Article class looks like this:

[AxDbMapToTable("Article", "A", "Article")]
public class Article : AxDbBaseObject, IClassifiable, IPublishable
   {
    ...

        private DateTime _created = DateTime.MinValue;
        private PublicationState _publicationState = PublicationState.NeverPublished;
        private DateTime _published = DateTime.MinValue;
        private DateTime _publishingRequested;
        private string _url;
   
    ...

     #region IPublishable Members

        private string ConstructUrl()
        {
            return "/" + EncodeName() + CMSConfigurationSettings.CmsExtension;
        }

        protected virtual string EncodeName()
        {
            return AxEncodingHelper.AxEncodeFileName(_name);
        }

        [AxDbMapToField]
        public DateTime Created
        {
            get { return _created; }
            set
            {
                if (_created != value) SetDirty();
                _created = value;
            }
        }

        [AxDbMapToField]
        public PublicationState PublicationState
        {
            get { return _publicationState; }
            set
            {
                if (_publicationState != value) SetDirty();
                _publicationState = value;
            }
        }

        [AxDbMapToField]
        public DateTime Published
        {
            get { return _published; }
            set
            {
                if (_published != value) SetDirty();
                _published = value;
            }
        }

        [AxDbMapToField]
        public virtual DateTime PublishingRequested
        {
            get
            {
                return _publishingRequested;
            }
            set
            {
                if (_publishingRequested != value) SetDirty();
                _publishingRequested = value;
            }
        }

        [AxDbMapToField]
        public string Url
        {
            get
            {

                if (string.IsNullOrEmpty(_name))
                {
                    return null;
                }
                string currentUrl = ConstructUrl();
                if (string.Compare(_url, currentUrl, true) != 0)
                {
                    if (!string.IsNullOrEmpty(_url))
                    {
                        SetPublicationStateDirty();
                    }
                    _url = currentUrl;
                    SetDirty();
                }
                return _url;
            }
            set
            {
                if (_url != value) SetDirty();
                _url = value;
            }
        }

        public void SetPublicationStateDirty()
        {
            switch (this.PublicationState)
            {
                case PublicationState.Active:
                    this.PublicationState = PublicationState.Changed;
                    break;
                case PublicationState.NeverPublished:
                    break;
                case PublicationState.Changed:
                    break;
                case PublicationState.Deleted:
                    this.PublicationState = PublicationState.Changed;
                    break;
                default:
                    break;
            }
            this.PublishingRequested = DateTime.MinValue;
        }

        #endregion

   }

User Interface

For the user interface in live system you are responsible yourself. For example in order to output a list with all the products you need to load a product with adapter.LoadAll() and render it with a Repeater.

It would be helpful for AxCMS users if all the pages for product management had a common look. AxCMS has all necessary instruments for this purpose. In the following chapters you will learn how to create extensions for the management system to manage articles.


Next article: Creating the Article Overview Page