Documentation

Using Axisbase as an embedded database

You can use Axisbase functionality in your own applications - either the building blocks, the data store, or both.

DLLs and Namespaces

You can redistribute the Axisbase DLLs, normally installed in c:\Program Files\Axisbase1. These are:

  • Axis1.Util.dll - Utility classes required for all other layers

  • Axis1.Win32.dll - Classes that depend on Win32 (If you are using Mono instead of Windows, these classes are not used.)

  • Axis1.Local.dll - This is the basic database driver for local files, used by the Data layer.

  • Axis1.Data.dll - This layer implements the AxPond class, which is the database. You could include Data, Local, Win32, and Util in a project, which total about 300 k, and have an embedded database with your own user interface.

  • Axis1.Forms.dll - This layer contains all the "building blocks" (called "nuts" in code), and uses all of the above layers. You can include this in a project to take advantage of the building blocks in your application, such as lists and reports.

  • Axisbase.exe - This layer is the client executable.

The c# namespace for each DLL is the same as the DLL name.

Please see separate documentation pages for the reference to each namespace.

Fish Metaphor

In the Axisbase code, there is a metaphor of "fish in a pond". The fish are records and the pond is the database. The terminology helps differentiate different levels of abstraction. A "fish net" is a way to catch some of the fish from the pond, what is called a data subset in the UI.

Naming scheme

Column names: Much of the naming scheme becomes apparent when using the Axisbase client. Typically a data subset creates in-memory tables whose column names follow the pattern AXISID.recordtype.property, such as "IRIS.task.percentcomplete". When a user commits changes, the system figures out what records to save back to based on the column names.

Type names: Depending on the context, record types may either be referenced as qualified with the Axis ID (such as "IRIS.task") or just the simple name ("task").

Parameters

When you enter parameter values in Axisbase, it is using an instance of ParameterShed. (The name comes from the term "watershed", but it is just a collection of parameters.) Each parameter has a name, prompt, data type, and optional value. In persisted form, a parametershed has no values; the values are always supplied at run time.

Parameter values can be entered by the user, supplied by code, or supplied by arguments when one building block calls another.

Parameter values are used by the AxPond.CatchFish method when it loads data sets based on a fishnet (data subset) that defines parameters.

In the Axisbase client, each window displaying one building block has its own parametershed. However, in code, you could just have one global parametershed, or any number.

Using the Axis1.Data namespace

The AxPond class is the database. To create or open a local file database, create an instance of AxLocalPond. To open a remote connection to a database server, create an instance of AxRemotePond. AxLocalPond and AxRemotePond are both derived from AxPond.

These code samples show how to connect to databases:

  //connect to databases
AxLocalPond pondA = new AxLocalPond(@"c:\data\iris.axis1", true);
pondA.Close();

AxRemotePond pondB = new AxRemotePond("pluto.axisbase.com", 3569, "IRIS", true, true);
pondB.Close();

Opening a database consumes significant resources, because it loads type information. In a web context where you need to open a connection repeatedly for short durations, you would use a connection pool. In a connection pool, the AxRemotePond instance is re-used when possible for successive calls to web pages. Example usage:

  //connect using a connection pool
AxRemotePond pondC = RemotePondPool.Get("pluto.axisbase.com", 3569, "IRIS", "bob", "34&Hw");
try
{
//do stuff
}
finally
{
RemotePondPool.Release(pondC);
}

Once you have an AxPond instance, you can query it for the available types, display types, linked ponds...

  //query a pond for types
string[] typeNames = pondC.GetAllTypeNames();
string[] displayTypeNames = pondC.DisplayTypes.GetNames();

//query a pond for linked ponds
string[] allIDs = pondC.LinkedPonds.GetLinkedAxisIDs(false, true);

A record type is represented by an instance of AxCompoundType. When you have a record type, you can obtain the names and primitive types of its fields.

  //2 ways to get a record type
AxCompoundType t1 = pondC.GetLocalAxisType("employee");
AxPond pondD;
AxCompoundType t2 = pondC.GetLinkedAxisType("IRIS.employee", out pondD);

//investigate a record type
string propertyName = t1.Properties[0].Name;
List<AxFieldInfo> fis = pondC.GetFieldInfo(t1);
AxPrimitive prim = fis[0].ExtPrimitive;

You can create or change types.

  //create a type
AxProposedCompoundType proposed = new AxProposedCompoundType();
proposed.Name = "employee";
proposed.Properties.Add(new AxProperty("name", "string", "Name", "", 1, 100, false));
proposed.Properties.Add(new AxProperty("salary", "decimal", "Salary", ""));
pondC.AddType(proposed);

//change a type
AxProposedCompoundType t2p = new AxProposedCompoundType(t2); //base on t2
t2p.Properties[0].MaxLength = 3000; //change one property
pondC.ChangeType(t2, t2p, null, null);

If you know the key of a record, you can load it, save changes, and delete it. Fish (that is, records) are expressed in hashtables, where the hash keys are the simple unqualified names (the name given in the AxFieldInfo instance.)

  //load, save, delete a fish with a known key
int key1 = -2387462;
Hashtable fish1 = pondC.LoadFish(t1, key1);
fish1["active"] = true; //change one property value
pondC.SaveFish(t1, fish1); //save it
pondC.DeleteFish(t1, key1); //delete it

To load records when you don't know the key, you can create an instance of AxFishnet. This has all the capabilities of the data subset building block in the UI. You can then use AxPond.CatchFish() to load the data set based on the fishnet.

  //load fish using a fishnet (long method)
AxFishnet net = new AxFishnet();
AxTypedTableDef td = new AxTypedTableDef(net, "IRIS.employee");
net.AllTables.Add(td);
AxNarrowTableStep narrow = new AxNarrowTableStep(td);
narrow.Columns = new List<string>();
narrow.Columns.Add("IRIS.employee.name");
td.Steps.Add(narrow);
AxDataSet ds1 = pondC.CatchFish(net);

A simpler way to use fishnets in code is by using FishnetBuilder.

  //load fish using a fishnet (short method)
FishnetBuilder fb1 = new FishnetBuilder("IRIS.employee");
fb1.Narrow(0, "IRIS.employee.name");
AxDataSet ds2 = pondC.CatchFish(fb1.Fishnet);

Once you have a data set (possibly obtained from AxPond.CatchFish, or any other source), and you've made changes to it, you can save changes in bulk. The overload of SaveFish that takes a data set argument uses column names to map the changes back to record types and record properties.

  //change and save a data set
ds2.Tables[0].Rows[5]["IRIS.employee.name"] = "Mathilda Cummins";
pondC.SaveFish(ds2);

When a property of one record references a record of another type, Axisbase can cache the references using AxFishRef and AxTypeRef. AxTypeRef is a container of cached references to one type, while AxFishRef encapsulates one individual reference. The cached references can be used to quickly display text representing the record, in place of the key value.

  //use AxFishRef / AxTypeRef
AxTypeRef t1refs = pondC.GetCachedReferences(t1);
int key2 = -23498734;
string display2 = t1refs.Get(key2).Display; //converts an unreadable key into a readable display for the record

You can map fields in c# objects to fields in Axisbase records, to allow your objects to be persisted to the database. This is often called "object relational mapping". If you derive persistent classes from AxObjectBase like this:

  public class Vendor : AxObjectBase
{
public string CompanyName, Address1, City, State, Zip, Notes;
public int ShipCode;
public AxDate LastPurchase;
public bool Active = true;

public Vendor() : base() { }
public Vendor(AxPond pond, AxCompoundType t, Hashtable fish) : base(pond, t, fish) { }
}

... then you can create, load, and save objects like this:

  AxLocalPond pond = new AxLocalPond(@"c:\data\iris.axis1", true);
AxCompoundType vendorT = pond.GetLocalAxisType("vendor");

//load all vendors in Santa Fe
FishnetBuilder fb = new FishnetBuilder("IRIS.vendor");
fb.FilterEquals(0, "IRIS.vendor.city", "Santa Fe", null);
AxDataSet ds = pond.CatchFish(fb.Fishnet);

//convert data set into a list of Vendor objects
List<Vendor> vendorList = new List<Vendor>();
((AxDataTable)ds.Tables[0]).ToObjects("IRIS.vendor", typeof(Vendor), vendorList);

//change and save the first vendor in the list
vendorList[0].LastPurchase = new AxDate(2006, 10, 30);
vendorList[0].Save(pond, vendorT);

//delete all the vendors in the list
foreach (Vendor v in vendorList)
{
pond.DeleteFish(vendorT, v.AxisKey);
MessageBox.Show("Deleted vendor " + v.CompanyName);
}

//create & save a new vendor
Vendor dave = new Vendor();
dave.CompanyName = "Dave's Appliances";
dave.Notes = "A great place to get spare parts";
// ... set the rest of the fields
dave.Save(pond, vendorT);

An alternative to object relational mapping is serializing c# objects to byte arrays, and saving them in blob properties of Axisbase records. Axisbase provides utility methods for this:

  //assuming a serializable class Foo and a record type
//"foo" with a byte[] property named "serial", save
//an instance of Foo to the database
AxCompoundType fooT = pondC.GetLocalAxisType("foo");
Foo foo = new Foo();
foo.DoSomething();
Hashtable fish = new Hashtable();
fish["serial"] = AxUtil.ObjectToBlob(foo);
pondC.SaveFish(fooT, fish);

//load all foo records, and convert the first row to an instance of Foo class
AxDataSet dsFoo = pondC.CatchFish("foo", int.MaxValue, null);
byte[] blob = (byte[]) dsFoo.Tables[0].Rows[0]["IRIS.foo.serial"];
Foo foo2 = (Foo) AxUtil.BlobToObject(blob);

Using the Axis1.Forms namespace

The Axis1.Forms namespace contains classes for persistent building blocks. These are called "nuts" in code. It contains separate classes for forms and controllers, generally following the Model-View-Controller architecture.

All the nuts (building blocks) derive from NutBase. You can create nuts in code, and save them to the database, and they will appear in the Building Blocks window.

  AxLocalPond pond = new AxLocalPond(@"c:\data\iris.axis1", true);

//create and save a report definition
ReportNut reportDef = new ReportNut();
reportDef.Name = "my report";
CellNest cell = new CellNest();
cell.Hyperlink = new Hyperlink();
cell.Hyperlink.Target = new NutBase.UsedNut("list 1");
reportDef.StretchBand.Cells.Add(cell);
reportDef.Save(pond);

The nut classes mainly contain public fields, and few methods.

The NutGroup class is a container for nuts. The Axisbase client only instantiates one instance of NutGroup, no matter how many linked ponds there are. This allows nuts to refer to nuts in other ponds. (The metaphors get strange at times.) You can create the NutGroup instance like this:

  //obtain a nut from a nutgroup
NutGroup group = new NutGroup(pond);
reportDef = group.GetNut("IRIS", "my report", typeof(ReportNut));

To show one of the Axisbase forms (such as a list or report) in your application, call the form's Execute method. You must implement INutWindowManager and IHelpProvider; often, the main form or MDI form is the best class for this.

  //show the report
INutWindowManager myWindowManager = this;
IHelpProvider myHelpProvider = this;
ReportViewForm viewForm = new ReportViewForm();
viewForm.Execute(NutEditingContext.ResultsOnly, group, reportDef,
this, myWindowManager, myHelpProvider, null, null);

The forms can access the AxPond instances via NutGroup.MainPond.

(c) 2014-2015 Divergent Labs, Inc.