Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An Introduction to a Post-relational Database for .NET: Matisse - Part 5

0.00/5 (No votes)
29 Mar 2004 2  
Object-oriented programming with a post-relational database

Introduction

Part 3 and Part 4 in this series of articles introduced .NET programming with the Matisse post-relational database; inserting objects using the "Object APIs" and retrieving objects from the database using ADO.NET and SQL without using mapping tools, respectively. In this article, I will show how to use the Object APIs for updating and deleting objects, looking up objects using an index, and for relationship navigation.

The Object APIs provide object-oriented interfaces to manipulate Matisse objects. The APIs are generated by the mt_stbgen code generation tool. When should we use the Object APIs (or ADO.NET)? Here are my answers:

  • When you use data-aware controls, you need to use ADO.NET.
  • Use the Object APIs for creation, update, and deletion of objects. You may also use SQL stored procedures for that purpose. I will talk about this in an upcoming article.
  • Use ADO.NET for retrieving objects. However, if you can use an index to look up objects, use the Object APIs for indexes for better performance.
  • If you can navigate from an object to another, use the Object APIs for navigation. It is much faster than issuing another SQL query to get the same object.

Most of the time, the Object APIs perform better than ADO.NET. Also, the Object APIs minimize the risk of errors in your program because a lot of things can be checked at compile time, not at run time.

Here is the list of previous articles:

  • Part 1: Overview of Matisse
  • Part 2: Schema definition
  • Part 3: Using .NET programming to insert objects
  • Part 4: Using ADO.NET to retrieve objects

Updating Attributes

The following code segment is part of the class Project that was generated by the mt_stbgen code generation tool. (I keep using the same database schema as the one I have been using in the previous articles.) No need to focus on the details of the code, just note that it defines a C# property.

namespace MatisseApp {
    using System;
    using com.matisse.reflect;
    using com.matisse.Data;
    using com.matisse;  
   
    //  A class generated by Matisse
    /// <summary>
    /// Project is a schema class generated by Matisse.
    /// </summary>
    public class Project : MtObject {
 
        //  Generated constructor
        /// <summary>
        /// Default constructor provided as an example.  
        /// You may modify or delete this constructor
        /// </summary>
        public Project(MtDatabase db) : 
                base(GetClass(db)) {
        }   
      
        //  Generated property, do not modify
        /// <summary>
        /// Property for attribute 'Budget'
        /// </summary>
        public System.Decimal Budget {
            get {
                return GetNumeric(GetBudgetAttribute(Database));
            }
            set {
                SetNumeric(GetBudgetAttribute(Database), value);
            }
        }

Since the class Project has been defined with an attribute Budget of type decimal in the database, the generated C# class has the property Budget as shown above. When you update an attribute value of a Matisse object, you simply use the set accessor of the property:

Project prj;

// Retrieve a Project object from the database and assign it to prj
// Update the value of the project's budget
prj.Budget = prj.Budget + 200000;

Relationship Navigation and Update

One of the big advantages of using a post-relational database is that you can navigate from object to object effortlessly, e.g., get a Manager object from a Project object. The next code is part of the class Project that defines properties to access two relationships (Members and ManagedBy). Again, let us not focus on the details of the code, but please notice that it defines two C# properties.

//  Generated property, do not modify
/// <summary>
/// Property 'Members': Gets and sets the 'Members' successors
/// </summary>
public Employee[] Members {
    get {
        return ((Employee[])
        (GetSuccessors(GetMembersRelationship(Database), typeof(Employee))));
    }
    set {
        SetSuccessors(GetMembersRelationship(Database), value);
    }
}

//  Generated property, do not modify
/// <summary>
/// Property 'ManagedBy': Gets the 'ManagedBy' successor
/// </summary>
public Manager ManagedBy {
    get {
        return ((Manager)(GetSuccessor(GetManagedByRelationship(Database))));
    }
    set {
        SetSuccessor(GetManagedByRelationship(Database), value);
    }
}

The following program retrieves a manager and members of a project, and then set other manager and members for the project:

Project prj;
// Retrieve a Project object from the database and assign it to prj
Manager mgr = prj.ManagedBy;
Employee[] members = prj.Members;

// Update the relationships
Manager otherManager;
Employee[] otherMembers;
prj.ManagedBy = otherManager;
prj.Members = otherMembers;

Other APIs for Relationships

There are a few more APIs to access relationships generated by the code generator. The list below is the methods generated for the Members relationship of the class Project.

APIs for the relationship Members Description
MembersSize Returns the number of objects (successors) in the relationship, i.e., the number of members of the project
MembersEnumerator Returns an enumerator to iterate over the objects in the relationship
PrependMembers Add new member(s) to the relationship at the beginning
AppendMembers Add new member(s) to the relationship at the end
RemoveMembers Remove member(s)
ClearMembers Remove all the members in the relationship

Looking Up Objects with an Index

The Matisse SQL optimizer utilizes indexes to optimize query performance, but you may also use the index APIs generated by the code generator. The index APIs perform slightly faster than dynamic SQL because they do not require SQL statement compilation, and your program becomes simpler. However, you can use the APIs only when your search criteria are contained in a single index; otherwise, you need to use SQL queries.

Suppose you have defined an index ProjNameIndex on the ProjectName attribute. The next line of code returns a Project object whose name is "Whidbey":

Project prj = Project.LookupProjNameIndex (dbconn, "Whidbey");

This is simple. Note that there are also other APIs that allow you to do range query.

Full-Text Search

Matisse has a full-text indexing feature, which is called "Entry point dictionary". For example, if you need to perform full-text search on the project description attribute, define an entry point dictionary as follows:

CREATE ENTRY_POINT DICTIONARY proj_desc_ep_dict
  ON Project (Description)
  MAKE_ENTRY "make-full-text-entry";

The next code retrieves all the projects whose description contains the work ".NET":

foreach (Project prj in Project.Proj_desc_ep_dictEnumerator(
    dbconn, ".NET") {
    // ...
}

Summary and Next Article

This article showed the basics of database programming with Matisse using the "Object APIs". Although almost anything can be done by using either ADO.NET or Object APIs, ADO.NET is usually better suited for querying while the Object APIs are more compact and efficient for the rest.

In my next article, I will show a few examples of ASP.NET programming with Matisse.

<< Back | Next >>

History

  • 30th March, 2004: Initial version

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below. A list of licenses authors might use can be found here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here