Dealing with Interfaces

In many cases it is helpful to separate information access into an interface. OpenAccess provides the functionality to make interfaces persistent. In contrast to a normal persistent class, an interface contains no fields. Potentially the interface hierarchy and the class inheritance hierarchy are not perfect matches, and an interface could also be implemented by many persistent class hierarchies. This makes the use of interfaces ambiguous wrt. class-table mapping and therefore certain restrictions apply; e.g. it is not possible to include query arguments from an interface in an OQL Query.

The following examples distinguish between “datastore identity” and “application identity”. (Sample Project attached to this kb article)
Let’s start with some samples:

Sample 1: Datastore identity, default HIGHLOW key generator

We have a class Address with a reference to an interface ICity. The implementation of this interface is in the class ACity. Note, that the interface is marked as persistent although it cannot define any fields.

C#

[Persistent]

publicinterface ICity {

string CityName {

get;

set;

}

}

[Persistent]

publicclass ACity : ICity {

privatestring acity;

publicstring CityName {

get { return acity; }

set { acity = value; }

}

}

[Persistent]

publicclass Address {

private ICity iCity;

public ICity ICityProperty {

get { return iCity; }

set { iCity = value; }

}

}

VB.NET

<Persistent()> _

PublicInterface ICity

Property CityName() AsString

EndInterface

<Persistent()> _

PublicClass ACity

Implements ICity

Private acity AsString

PublicProperty CityName() AsString

Get

Return acity

EndGet

Set

acity = value

EndSet

EndProperty

EndClass

<Persistent()> _

PublicClass Address

Private iCity As ICity

PublicProperty ICityProperty() As ICity

Get

Return iCity

EndGet

Set

iCity = value

EndSet

EndProperty

EndClass

The corresponding app.config file needs no special entries. The default mapping information, present in the app.config file works fine.

Sample 2: Datastore identity, HIGHLOW key generator

We advance the model with a second interface that is also declared as persistent:

C#

[Persistent]

publicinterface ICity {

string CityName {

get;

set;

}

}

[Persistent]

publicinterface IStreet {

string StreetName {

get;

set;

}

}

[Persistent]

publicclass ACity : ICity, IStreet{

privatestring acity;

privatestring astreet;

publicstring CityName {

get { return acity; }

set { acity = value; }

}

publicstring StreetName {

get { return astreet;}

set { astreet = value;}

}

}

[Persistent]

publicclass Address {

private ICity iCity;

private IStreet iStreet;

public IStreet IStreetProperty {

get { return iStreet; }

set { iStreet = value; }

}

public ICity ICityProperty {

get { return iCity; }

set { iCity = value; }

}

}

VB.NET

<Persistent()> _

PublicInterface ICity

Property CityName() AsString

EndInterface

<Persistent()> _

PublicInterface IStreet

Property StreetName() AsString

EndInterface

<Persistent()> _

PublicClass ACity

Implements ICity

Implements IStreet

Private acity AsString

Private astreet AsString

PublicProperty CityName() AsString

Get

Return acity

EndGet

Set

acity = value

EndSet

EndProperty

PublicProperty StreetName() AsString

Get

Return astreet

EndGet

Set

astreet = value

EndSet

EndProperty

EndClass

<Persistent()> _

PublicClass Address

Private iCity As ICity

Private iStreet As IStreet

PublicProperty IStreetProperty() As IStreet

Get

Return iStreet

EndGet

Set

iStreet = value

EndSet

EndProperty

PublicProperty ICityProperty() As ICity

Get

Return iCity

EndGet

Set

iCity = value

EndSet

EndProperty

EndClass

In this case also, the default mapping in the app.config file works fine and no special entries are to be made.

Sample 3: Application identity, Primary keys are int

In this sample we have used application identity, and the primary keys are of int type. The only difference between this sample and Sample 2 is the additional declaration of the identity field. Alternatively, a user defined application identity class could be used; please refer to the documentation for more information about multi-field identity classes.

The default app.config file remains and the main program is the same as in the case of Sample 2.

Sample 4: Application identity, GUIDs as primary key

Normally, we would think that using int or GUIDs as primary key would not make a difference. However, if GUIDs are used as a primary key, some information remains missing for the data table generation tool and we get (beside the default address_id and the voa_version columns) the same two columns as before for every interface created:

i_city_class int

i_city_id int

i_street_class int

i_street_id int

However, in this case they are wrong since the columns type of i_city_id and i_street_id must correspond to the primary key type of the target row, in this case a uniqueidentifier.
So – besides the changes of the primary key types like this:

C#

[Persistent]

publicinterface ICity {

string CityName {

get;

set;

}

}

[Persistent]

publicinterface IStreet {

string StreetName {

get;

set;

}

}

[Persistent(IdentityField = “acityid”)]

publicclass ACity : ICity, IStreet {

private Guid acityid; // pk

privatestring acity;

privatestring astreet;

publicstring CityName {

get { return acity; }

set { acity = value; }

}

publicstring StreetName {

get { return astreet; }

set { astreet = value; }

}

public ACity() {

acityid = Guid.NewGuid();

}

}

[Persistent(IdentityField = “addressId”)]

publicclass Address {

private Guid addressId; // pk

private ICity iCity;

private IStreet iStreet;

public IStreet IStreetProperty {

get { return iStreet; }

set { iStreet = value; }

}

public ICity ICityProperty {

get { return iCity; }

set { iCity = value; }

}

public Address() {

addressId = Guid.NewGuid();

}

public Guid AddressId {

get { return addressId; }

set { addressId = value; }

}

}

VB.NET

<Persistent()> _

PublicInterface ICity

Property CityName() AsString

EndInterface

<Persistent()> _

PublicInterface IStreet

Property StreetName() AsString

EndInterface

<Persistent(IdentityField := acityid)> _

PublicClass ACity

Implements ICity

Implements IStreet

Private acityid As Guid

' pk

Private acity AsString

Private astreet AsString

PublicProperty CityName() AsString

Get

Return acity

EndGet

Set

acity = value

EndSet

EndProperty

PublicProperty StreetName() AsString

Get

Return astreet

EndGet

Set

astreet = value

EndSet

EndProperty

PublicSubNew()

acityid = Guid.NewGuid()

EndSub

EndClass

<Persistent(IdentityField := addressId)> _

PublicClass Address

Private addressId As Guid

' pk

Private iCity As ICity

Private iStreet As IStreet

PublicProperty IStreetProperty() As IStreet

Get

Return iStreet

EndGet

Set

iStreet = value

EndSet

EndProperty

PublicProperty ICityProperty() As ICity

Get

Return iCity

EndGet

Set

iCity = value

EndSet

EndProperty

PublicSubNew()

addressId = Guid.NewGuid()

EndSub

PublicProperty AddressId() As Guid

Get

Return addressId

EndGet

Set

addressId = value

EndSet

EndProperty

EndClass

In this case the app.config entries also need to be changed, in order to explain what kind of classes we accept for the interface references:

<mappings current="mssqlMapping">

<mapping id="mssqlMapping">

<namespace name="Sample4">

<class name="Address">

<field name="iCity">

<extension key="valid-class"value="ACity"/>

</field>

<field name="iStreet">

<extension key="valid-class"value="ACity"/>

</field>

</class>

</namespace>

</mapping>

</mappings>

After these changes the correct columns are generated:

i_city_class int

i_city_id uniqueidentifier

i_street_class int

i_street_id uniqueidentifier

And the main program also runs correctly. As of now, the changes in the app.config file need to be done manually.

Sometimes it is better to describe the class type hard coded like this:

<namespace name="Sample5">

<class name="Address">

<field name="iCity">

<extension key="valid-class"value="ACity=ACITYCLASS"/>

<extension key="valid-class"value="BCity=BCITYCLASS"/>

</field>

<field name="iStreet">

<extension key="valid-class"value="ACity=ACITYCLASS"/>

</field>

</class>

</namespace>

This makes sure that always the right class id is used and a filled database is usable, even if the class is moved or renamed. If you use the extension valid-class without the type code the automatic class id calculation at start time can fail if the target class has been renamed or moved to another namespace. Now the type discrimination is done with two varchar columns, and not int columns.

Additional information

When using persistent interfaces with application identity you must make sure that the type of the primary keys across the implementing classes is the same for every interface.

Progress, Telerik, and certain product names used herein are trademarks or registered trademarks of Progress Software Corporation and/or one of its subsidiaries or affiliates in the U.S. and/or other countries. See Trademarks or appropriate markings.