Dealing with Interfaces

Article Info

Rating: Not rated

Article information

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.




Comments

There are no comments yet.
If you'd like to comment on this KB article, please, send us a Support Ticket.
Thank you!

Please Sign In to rate this article.