Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
Value Types
Programmer's Guide > OpenAccess ORM Classic (Old API) > Programming With OpenAccess > The .NET Data Model > Value Types

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.

Instances of value types directly contain their data. Variables that are of value types each have their own copy of the data, and therefore, an operation on one variable does not affect other variables. Value types are implicitly sealed types, since they cannot be derived from.

All supported values types, regardless of whether built-in, enums, nullable or user-defined, are only supported as fields of other user-defined persistent types. It is not possible to store a standalone instance of a value type in the database.

Built-in Value Types

The following table lists the .NET Framework built-in value types supported by OpenAccess ORM:

OpenAccess ORM — Supported Value Types

.NET Framework Type

C# Alias

Visual Basic Alias

Range of Values

System.Boolean

bool

Boolean

true, false

System.Byte

byte

Byte

1 byte, 0 ... 255

System.Char

char

Char

One unicode character (2 bytes)

System.DateTime

 

Date

Date and time representation

System.Decimal

decimal

Decimal

 

System.Double

double

Double

8 bytes, floating point double precision conforming to IEEE 754

System.Int16

short

Short

2 bytes, -215 ... 215-1

System.Int32

int

Integer

4 bytes, -231 ... 231-1

System.Int64

long

Long

8 bytes, -263 ... 263-1

System.SByte

sbyte

 

1 byte, -128 ... +127

System.Single

float

Single

4 bytes, floating point single precision conforming to IEEE 754

System.UInt16

ushort

 

2 bytes, 0 ... 216 -1

System.UInt32

uint

 

4 bytes, 0 ... 232 -1

System.UInt64

ulong

 

8 bytes, 0 ... 264 -1

System.Guid

 

 

globally unique identifier, 128-bit

These value types correspond to the primitive data types used by programming languages. An additional type, System.Enum, is also inherited from System.ValueType. System.Enum provides the base class for enumerations. An enumeration is a named constant whose underlying type can be any integral type except Char. If no underlying type is explicitly declared, Int32 is used. User-defined types can be derived from System.Enum. Derived types are value types and are sealed, i.e., these types cannot serve as base types for other user-defined types.

Enumerations

All enumeration types, whose underlying type is one of the supported built–in value types (see above for supported built-in value types), are supported and treated like their underlying value type.

Arrays of enumerations are not supported.

Nullable Value Types

.NET 2.0 introduces System.Nullable<type> where type is a valuetype. This allows you to express a null-value for fields of this type.

The C# alias is type?

Both the syntaxes are equivalent as shown in the example below:

Copy Code
public class PersClass
{
	int normalInt;	 //can not be null
	int? nullableInt1; //can be null
	System.Nullable<int> nullableInt2; //equivalent to int?
}

Earlier, with the normal int field, especially in conjunction with legacy databases, it was possible that some fields could not have the required mapping (since null was not allowed), especially if the corresponding column in the database allowed NULL-values.

However, with a nullable int field, the range of allowed values for the field can be the same as the range of allowed values for the database column, especially when both permit NULL-values.

OpenAccess ORM supports nullable types for all of the supported built-in value types as listed above. These types are also supported only as field types of user-defined persistent types.

User-defined value types

For user defined enumerations refer to the enumerations section above.

User-defined value types are used in .NET to express simple user defined data types, which can be copied such as telephone numbers. They do not follow the normal semantics with respect to referencability i.e., they are not easily referencable, from the outside and are copied by value in general. This allows them to be embedded into other objects as you can do with built-in data types like int or double.

User-defined value types can be used to express common data aggregation and functionality, for e.g. an Address value type, which is physically contained/used by a Person and a Company class.

All other user-defined value types have to be marked as persistent, so that they can be used as field types of other user-defined persistent types. Instances of user-defined persistent value types can only be stored as a part of a user-defined persistent reference, i.e. they cannot be stored standalone.

However, a user-defined persistent value-type is allowed to have fields of another user-defined value-type.

Such fields are stored embedded in the table of their owning object, i.e. they are "flattened" as additional columns into the parents table (see also Mapping), User-defined value-types don’t have their own table.

You should access the data of the structs only via accessor methods (properties) This is necessary to avoid an unconscious "copy-by-value" behavior, which might not be what you intended. The intended usage pattern for them is to support "atomic" application specific types, which can be embedded in memory and in the relational tables.

Also currently arrays, collections or dictionaries of user-defined value types are not supported

For user-defined values, the same rules, which apply to user-defined persistent reference types apply; the exception being that no standalone entities are allowed (refer to the User-defined persistent reference types section below for the more information).

Change Tracking for Value Type Fields

If an instance of a persistence capable class contains a built-in value type field and this field is passed by reference (as a ref parameter) to a method that modifies the field, OpenAccess ORM cannot track this change. In practice what this means, is that the object instance containing the value type field will not be marked as modified and will not be updated when the enclosing transaction is committed. As, this can lead to unexpected behavior, the OpenAccess ORM enhancer will detect such situations and issue a warning during enhancement.

The behavior described above is only true for built-in value types (Refer to Built-in Value Types for a list of the built-in value types supported in OpenAccess ORM). In the case of user-defined persistent value types, there are no such limitations; change tracking works even when the user-defined value type fields are passed by reference. Therefore, there are no warnings printed by the enhancer for user-defined value type fields.

Consider the following example. Start with a persistence capable class, MyPersistent:

Copy Code
[OpenAccess.Persistent]

class MyPersistent

{

  int intValue;

}

In the MySource.cs source file, define a method with an int parameter passed by reference:

Copy Code
int MethodWithRefParam( ref int val )

{

  val = val + 42;

  return va1;

}

In the following code, the value of the persistent int field is passed by reference:

Copy Code
IObjectScope os = . . .

os.Transaction.Begin();

MyPersistent obj = . . .

MethodWithRefParam( ref obj.intValue );

os.Transaction.Commit();

For this code, the OpenAccess ORM enhancer prints the following warning:

MySource.cs(15,4): Warning: Cannot track changes for the field MyPersistent.intValue in the call of int MethodWithRefParam(int ByRef).

In the above case, even though the intValue field has been modified, the obj instance will not be updated in the database during the Commit() call.

You can avoid the above situation and the enhancer warning, by making a change to your source code. There are two possibilities:

The easiest way is to simply not to rely on the use of the "ref" parameter and instead assign the object field to the return value from the method:

Copy Code
obj.intValue = MethodWithRefParam( obj.intValue );

OpenAccess ORM can track this "direct" modification to the object and can properly mark it as modified.

A second possibility is to use an additional "out" parameter and pass the value type twice. For value type fields that are passed as "out" parameters the OpenAccess ORM change tracking mechanism will always mark the object containing the passed value type as modified (Even if the method does not actually modify the parameter).

The following example shows how this works. First redefine the method to take the "ref" parameter and an additional "out" parameter:

Copy Code
int MethodWithRefParam( ref int inVal, out int outVal )

{

  outVal = inVal + 42;

  return outVal;

};

Now, when you call the method, pass the value type twice.

Copy Code
MethodWithRefParam( obj.intValue, obj.intValue );

OpenAccess ORM will mark obj as modified and the object will be updated in the database during the transaction Commit() call.