Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
Multiple Field Identity
Programmer's Guide > OpenAccess ORM Classic (Old API) > Programming With OpenAccess > Object Identity > Multiple Field Identity

Glossary Item Box

This documentation article is a legacy resource describing the functionality of the deprecated OpenAccess Classic only. The contemporary documentation of Telerik OpenAccess ORM is available here.

Whenever you need more control or flexibility over the identity generation, or if you need more than one field, i.e., a composite key to generate your primary key, or need a key type that is not supported by single field identity as the primary key, you should use this identity.

Multiple Field identity is a mechanism where the identity of objects is defined by the values of some fields in the persistence capable class. These fields serve as the primary key. In this case, you take control over the specification of the identity of your objects, i.e., you need to specify the object-id class.

You have to define a special implementation of the IObjectId interface for all your persistent classes. With this you can cast the result from GetObjectId to your implementation and add more functionality to the class.

If you have a single field you can use the same key generators as with Single Field Identity. If you have multiple fields you can use the Verify mechanism only (refer to db-key-generator for more information).

Specifying Multiple Field Identity

Multiple Field identity is a mechanism where the identity of objects is defined by the values of some fields in the persistence capable class. These fields serve as the primary key. The main reason you may want to use single or multiple field identity is when working with legacy databases. If you are not working with a legacy database it is recommended that you use internal identity. Internal identity is automatic and requires no additional effort on your part.

Use the Metadata Properties dialog to specify the Multiple Field Identity as shown below:

Multiple Field should be chosen as the identity type from the drop-down list present alongside the Type label. Next, the user needs to specify the class that will hold the identity information by selecting the class from the drop-down list appearing alongside the ID Class label. If the class contains a local id class definition this is selected by default. However, the drop-down list contains all existing id classes.

The user can choose the key generator (from the drop-down list present alongside the Key Generator label) based on the field/s that are used to generate the identity, if there is only a single field the same key generators used with Single Field Identity can be used. However, if you have multiple fields, only the Verify mechanism can be used (refer to db-key-generator for more information).

To use multiple field identity a special object identity class is needed for the persistence capable class. Once you select Multiple Field as the identity type, this special object identity class is specified in the [Persistent] attribute:

Copy Code
[OpenAccess.Persistent( Identity = typeof( NorthwindLibrary.OrderDetails.ID ) )]
public class OrderDetails
{
  private int field1; // pk
  private string field2; // pk

 public class ID : OpenAccess.IObjectId
  {
    public int field1; //pk
    // . . .
  }
}

Any class within a hierarchy can define the multiple field identity type, i.e., it is not necessary for the top most persistent class in a hierarchy, to define the same (of course it can do so), however, if a class defines the multiple field identity type, all its persistent base classes must be abstract and all classes derived from it must not define the identity again. A multiple field identity class may also refer to members that are defined in any persistent base class of the class that defines the multiple field identity.

An application may use a mix of multiple field identity, single field identity and internal identity. However, independent persistence capable class hierarchies must use only one identity mechanism. Therefore, it is possible only to have either multiple field, single field or internal identity within a single inheritance hierarchy. Also, you may not change the identity type within a hierarchy, and each hierarchy must use it own identity type.

In case of the flat inheritance (the default inheritance strategy), the derived classes in a hierarchy will have the Identity Type, Identity ID Class/Field, and key Generator labels and drop-down hidden. In case of other inheritance strategies, the identity type can be set.

Object Identity Class/es

OpenAccess ORM supports composite primary keys. However, single column primary keys are more efficient and composite primary keys are not recommended unless you need to map to a legacy schema.

The string format for multiple field identity classes is composed by concatenating the different parts with "-" by default and this might break for columns with GUID, string and negative number values.

The string representation, can be altered by you, but please remember that the constructor with the string parameter must be able to regenerate the correct values from the string representation obtainable by appId.ToString().

The reverse engineering wizard will automatically create correct identity classes. However, if you are manually writing or modifying these generated classes, then certain restrictions as listed below, need to be remembered:

The ID class must implement OpenAccess.IObjectId

The ID class must provide a public "no-args" constructor

The ID class must provide a public constructor that takes a single string parameter. The string must contain the information to set the values of the ID class fields. The ID class must also override the ToString() method to produce a string that can be used as the constructor parameter to create a new, identical ID class instance.

Every field of the persistence capable class that should be a field of the primary key, must be reflected in the object ID class. This means you need to define the primary key by including certain fields into the object identity class. The fields in the ID class must be public and have the same name and type as the corresponding fields of the persistence capable class (The corresponding fields of the persistence capable class need not be public, and may also be defined in a persistent base class of this persistent capable class).

The ID class may not have fields that are not contained in the persistence capable class

The ID class must override GetHashCode(), ToString() and Equals() methods as follows:

Equals() must return true if and only if the object passed is of same type and all fields compare equal

GetHashCode() must be consistent with Equals(), i.e., two equal objects must produce the same hashcode

ToString() must produce a string that can be used with the ID class string parameter constructor to construct a new, identical ID class instance (see above)

Here is the OrderDetails class again with a complete implementation of the OrderDetails.ID class:

Copy Code
/**
    * Example multifield identity objectid-class.
    * Important: ctor(string) must be able to parse value from ToString()
    * Required: Field names and types of ID class must match those of
                 the primary key fields in the persistent class.
 */
  public class ID : OpenAccess.IObjectId
  {
    public int field1; // there must be a primary key field field1 in persistent class
    public string field2; // there must be a primary key field field2 in persistent class


    public ID() {}    // no-args constructor

    public ID( string s )
    {
      int p = 0;
      int i= s.IndexOf('-', p);  // using '-' do delimit the multifield parts
      field1 = System.Int32.Parse(s.Substring(p, i));
      p = i + 1;
      field2 = s.Substring(p);
    }

    public override bool Equals( object o )
    {
      if (this == o)
            return true;
      if (!(o is ID))
            return false;

      ID id = (ID) o;

      if (this.field1 != id.field1)
             return false;
      if (System.Object.Equals(this.field2,id.field2) == false)
             return false;

      return true;
    }

    public override int GetHashCode()
    {
      int result = field1 * 29;
      if (field2 != null)
             result += field2.GetHashCode();
      return result;
    }

    public override string ToString()
    {
      System.Text.StringBuilder buffer = new System.Text.StringBuilder();
      buffer.Append(field1).Append('-');
      buffer.Append(field2);
     return buffer.ToString();
    }
  }
}

It is not necessary for the identity class to be a nested type.

Supported Field Types

The following lists the currently supported field types for use in the application-specific object identity:

System.String

System.Int16

System.Int32

System.Int64

System.Single

System.Decimal

System.Guid

If you want to use GUID as an identity, it is necessary to have a unique GUID created each time. For this, you will need to initialize the GUID explicitly, by adding ID = Guid.NewGuid(); in the constructor of the ID class.

The identity fields of the persistence capable class instances can be set by the application or a key generator may be used. With a key generator, classes that use multiple field identity will behave, in effect, like classes with internal identity but with visible primary key fields. OpenAccess ORM provides two simple key generators, AUTOINC and HIGHLOW, which you can specify to automatically populate the identity fields. The HIGHLOW mechanism is the same as that used by OpenAccess ORM for internal identity. AUTOINC is the mechanism used by the Identity column in MSSQL (refer to db-key-generator for more information).

You can specify the appropriate key generator in the Metadata Properties dialog. For e.g., if you have chosen the AUTOINC key generator for the Person class, the following lines will be added to your app.config file:

Copy Code
<class name="Person">
  <extension key="db-key-generator" value="AUTOINC" />
  . . .
</class>

If a key generator is being used, the identity fields must not be set manually.

Future releases will allow you to specify a custom key generator to be used to generate identity values automatically.

Instances of the object identity classes can be used directly for fetching objects from the database via GetObjectById():

Copy Code
Person p = (Person) scope.GetObjectById( new Person.ID( "Miller" ) );

Also, the return value of GetObjectId() for these classes can be cast to the specific type of the object identity class:

Copy Code
Person.ID myId = (Person.ID) scope.GetObjectId( p );