|
Article relates to
|
OpenAccess Classic
|
|
Created by
|
Thomas, Telerik
|
|
Last modified
|
November 4, 2008
|
|
Last modified by
|
Peter, 2008
|
HOW TO
Deal with interfaces.
DESCRIPTION
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] |
| public interface ICity { |
| string CityName { |
| get; |
| set; |
| } |
| } |
| [Persistent] |
| public class ACity : ICity { |
| private string acity; |
| public string CityName { |
| get { return acity; } |
| set { acity = value; } |
| } |
| } |
| [Persistent] |
| public class Address { |
| private ICity iCity; |
| public ICity ICityProperty { |
| get { return iCity; } |
| set { iCity = value; } |
| } |
| } |
| |
VB.NET
| <Persistent()> _ |
| Public Interface ICity |
| Property CityName() As String |
| End Interface |
| <Persistent()> _ |
| Public Class ACity |
| Implements ICity |
| Private acity As String |
| Public Property CityName() As String |
| Get |
| Return acity |
| End Get |
| Set |
| acity = value |
| End Set |
| End Property |
| End Class |
| <Persistent()> _ |
| Public Class Address |
| Private iCity As ICity |
| Public Property ICityProperty() As ICity |
| Get |
| Return iCity |
| End Get |
| Set |
| iCity = value |
| End Set |
| End Property |
| End Class |
| |
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] |
| public interface ICity { |
| string CityName { |
| get; |
| set; |
| } |
| } |
| [Persistent] |
| public interface IStreet { |
| string StreetName { |
| get; |
| set; |
| } |
| } |
| [Persistent] |
| public class ACity : ICity, IStreet{ |
| private string acity; |
| private string astreet; |
| public string CityName { |
| get { return acity; } |
| set { acity = value; } |
| } |
| |
| public string StreetName { |
| get { return astreet;} |
| set { astreet = value;} |
| } |
| } |
| [Persistent] |
| public class 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()> _ |
| Public Interface ICity |
| Property CityName() As String |
| End Interface |
| <Persistent()> _ |
| Public Interface IStreet |
| Property StreetName() As String |
| End Interface |
| <Persistent()> _ |
| Public Class ACity |
| Implements ICity |
| Implements IStreet |
| Private acity As String |
| Private astreet As String |
| Public Property CityName() As String |
| Get |
| Return acity |
| End Get |
| Set |
| acity = value |
| End Set |
| End Property |
| |
| Public Property StreetName() As String |
| Get |
| Return astreet |
| End Get |
| Set |
| astreet = value |
| End Set |
| End Property |
| End Class |
| <Persistent()> _ |
| Public Class Address |
| Private iCity As ICity |
| Private iStreet As IStreet |
| Public Property IStreetProperty() As IStreet |
| Get |
| Return iStreet |
| End Get |
| Set |
| iStreet = value |
| End Set |
| End Property |
| Public Property ICityProperty() As ICity |
| Get |
| Return iCity |
| End Get |
| Set |
| iCity = value |
| End Set |
| End Property |
| End Class |
| |
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.
C#
| [Persistent] |
| public interface ICity { |
| string CityName { |
| get; |
| set; |
| } |
| } |
| [Persistent] |
| public interface IStreet { |
| string StreetName { |
| get; |
| set; |
| } |
| } |
| [Persistent(IdentityField = “acityid”)] |
| public class ACity : ICity, IStreet { |
| static private int pk = 0; |
| private int acityid; // pk |
| private string acity; |
| private string astreet; |
| public string CityName { |
| get { return acity; } |
| set { acity = value; } |
| } |
| public string StreetName { |
| get { return astreet; } |
| set { astreet = value; } |
| } |
| |
| … |
| |
| [Persistent(IdentityField = “addressId”)] |
| public class Address { |
| static private int pk = 0; |
| private int 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 = HelperLib.OAHelper.NextNumber(); |
| addressId = pk++; |
| } |
| |
| public int AddressId { |
| get { return addressId; } |
| set { addressId = value; } |
| } |
| } |
| |
VB.NET
| <Persistent()> _ |
| Public Interface ICity |
| Property CityName() As String |
| End Interface |
| <Persistent()> _ |
| Public Interface IStreet |
| Property StreetName() As String |
| End Interface |
| <Persistent(IdentityField := acityid)> _ |
| Public Class ACity |
| Implements ICity |
| Implements IStreet |
| Private Shared pk As Integer = 0 |
| Private acityid As Integer |
| ' pk |
| Private acity As String |
| Private astreet As String |
| Public Property CityName() As String |
| Get |
| Return acity |
| End Get |
| Set |
| acity = value |
| End Set |
| End Property |
| Public Property StreetName() As String |
| Get |
| Return astreet |
| End Get |
| Set |
| astreet = value |
| End Set |
| End Property |
| |
| End Class |
| |
| <Persistent(IdentityField := addressId)> _ |
| Public Class Address |
| Private Shared pk As Integer = 0 |
| Private addressId As Integer |
| ' pk |
| Private iCity As ICity |
| Private iStreet As IStreet |
| |
| Public Property IStreetProperty() As IStreet |
| Get |
| Return iStreet |
| End Get |
| Set |
| iStreet = value |
| End Set |
| End Property |
| |
| Public Property ICityProperty() As ICity |
| Get |
| Return iCity |
| End Get |
| Set |
| iCity = value |
| End Set |
| End Property |
| |
| Public Sub New() |
| ' addressId = HelperLib.OAHelper.NextNumber(); |
| addressId = System.Math.Max(System.Threading.Interlocked.Increment(pk),pk - 1) |
| End Sub |
| |
| Public Property AddressId() As Integer |
| Get |
| Return addressId |
| End Get |
| Set |
| addressId = value |
| End Set |
| End Property |
| End Class |
| |
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] |
| public interface ICity { |
| string CityName { |
| get; |
| set; |
| } |
| } |
| [Persistent] |
| public interface IStreet { |
| string StreetName { |
| get; |
| set; |
| } |
| } |
| [Persistent(IdentityField = “acityid”)] |
| public class ACity : ICity, IStreet { |
| |
| private Guid acityid; // pk |
| |
| private string acity; |
| private string astreet; |
| public string CityName { |
| get { return acity; } |
| set { acity = value; } |
| } |
| |
| public string StreetName { |
| get { return astreet; } |
| set { astreet = value; } |
| } |
| |
| public ACity() { |
| acityid = Guid.NewGuid(); |
| } |
| } |
| |
| [Persistent(IdentityField = “addressId”)] |
| public class 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()> _ |
| Public Interface ICity |
| Property CityName() As String |
| End Interface |
| <Persistent()> _ |
| Public Interface IStreet |
| Property StreetName() As String |
| End Interface |
| <Persistent(IdentityField := acityid)> _ |
| Public Class ACity |
| Implements ICity |
| Implements IStreet |
| |
| Private acityid As Guid |
| ' pk |
| Private acity As String |
| Private astreet As String |
| Public Property CityName() As String |
| Get |
| Return acity |
| End Get |
| Set |
| acity = value |
| End Set |
| End Property |
| |
| Public Property StreetName() As String |
| Get |
| Return astreet |
| End Get |
| Set |
| astreet = value |
| End Set |
| End Property |
| |
| Public Sub New() |
| acityid = Guid.NewGuid() |
| End Sub |
| End Class |
| |
| <Persistent(IdentityField := addressId)> _ |
| Public Class Address |
| Private addressId As Guid |
| ' pk |
| Private iCity As ICity |
| Private iStreet As IStreet |
| |
| Public Property IStreetProperty() As IStreet |
| Get |
| Return iStreet |
| End Get |
| Set |
| iStreet = value |
| End Set |
| End Property |
| |
| Public Property ICityProperty() As ICity |
| Get |
| Return iCity |
| End Get |
| Set |
| iCity = value |
| End Set |
| End Property |
| |
| Public Sub New() |
| addressId = Guid.NewGuid() |
| End Sub |
| |
| Public Property AddressId() As Guid |
| Get |
| Return addressId |
| End Get |
| Set |
| addressId = value |
| End Set |
| End Property |
| End Class |
| |
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.
Sample 5: Application identity, GUIDs as primary key, explicit type code
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.
Please
Sign In
to rate this article.