Stores an embedded type (struct or non-persistent class) as a property of a persistent class in a set of database fields.
property: The name of an abstract property of the persistent class, that returns the embedded type. The property can have a getter (read only), or setter (write only), or both (read and write).
Auto: Genome tries to match the constructor for initialising the embedded type automatically. This is the default value, when the attribute is omitted.
Explicit: Genome matches for a constructor based on the provided ctor-signature.
ctor-signature: List of types to match for the constructor signature, when Explicit is specified. When omitting this attribute, and specifying Explicit, an empty signature list is assumed (which selects the default constructor).
struct-member: A member of the embedded type, which is to be mapped to a database field. Currently, only members that can be mapped with <PersistentField/> are supported.
param-name: Optional specification for the name of a constructor parameter of the embedded type, to which Genome should map the database field value when constructing an instance from the database. This is necessary when Genome is not using the default constructor for initialising the embedded type, and the constructor parameter names cannot be automatically matched to the embedded type members.
persistent-field-element: Mapping of the member to a persistent field.
· Embedded types can be structs or non-persistent classes. Non-persistent class refers to any CLR type which does not map its extent to a specific database table (i.e. does not maintain an identity in the database).
· The embedded class cannot be abstract.
· Currently, only scalar members of the struct or CLR type can be mapped to database fields.
· Only public members of the embedded type can be mapped to database fields.
· Genome needs to determine, how to initialise the embedded struct, when reading it from the database: initialisation is performed either with the default constructor and explicitly setting the individual properties of the embedded struct, or using a constructor which initialises the fields.
o If at least one mapped member is read only, or there is no default constructor on the class (structs always provide a default constructor), the embedded type has to provide a public constructor for initialising all the members when constructing an instance from the database.
o You can explicitly specify a contructor to be used with constructor="Explicit" and providing a ctor-signature (constructorSignature). Omitting constructorSignature, or specifying an empty ctor-signature, selects the default constructor.
o Currently, there is no support for mixed initialisation of members using constructor and direct member assignment. This means that either all mapped members must be public or all members must be initialised by a constructor. You cannot use a specific constructor, which does not initialise all mapped fields of the embedded struct.
o When not using the default constructor, Genome needs to match the constructor parameters to the mapped members of the embedded struct. By default, the mapped member name with a lower case first letter is assumed as the constructor parameter name. The parameter name can be explicitly defined using the ConstructorParameter element or attribute on the member mapping.
· Currently, Genome cannot detect changes to members in an embedded class automatically. Therefore, we recommend that you use structs or treat the embedded instance as immutable (reset the reference with a new instance instead of modifying values of the assigned instance).
· Mapped members of the embedded type can also be used in queries (e.g. for filtering).
· Members of embedded types can also be mapped to server-side OQL expressions. Map the embedded type with <Type/> and use the corresponding member mappings.
In the Northwind database, the Customer has the following scalar fields:
The Address, City, Region, PostalCode and Country properties can be extracted to an embedded struct Address:
Address can be a class as well. In this case, you have to make sure that the class provides a default constructor (a constructor that can be called with no arguments).
The embedded class can now be exposed on Customer, instead of the individual scalar fields:
In the mapping of Customer, the individual fields of Address have to be mapped to the database fields of the customers table, which were previously mapped to scalar properties:
Note that Address.Street is mapped to the Address field of the customers table.
Having mapped an embedded struct as described above, you can also use the struct members for filtering.
The following query retrieves all customers in the USA:
The same in LINQ:
Similarly, you can use the struct members in projections.
This query retrieves the addresses of all customers:
The same in LINQ:
You can also project to members of embedded structs.
This query retrieves the list of countries of all customers:
The same in LINQ:
As a general rule, you can use embedded struct members for the same operations as direct persistent fields.
When Genome loads an object with an embedded struct member from the database, it initialises the struct by assigning the mapped properties of the embedded struct with the mapped database field values.
However, when the struct members are read only, this initialisation does not work. In this case, you can map to a constructor of the struct, which initialises its members. By default, Genome tries to match the constructor with the following rules:
· The constructor has to have a parameter for each mapped property.
· The parameter name has to match the mapped property name with a lower case start character.
The following code shows a modified Address struct that will be initialised by the defined constructor, as the Street property is read only:
In case the constructor parameter names cannot be matched with the public member names, the ConstructorParameter attribute can be used to provide explicit mapping:
Note that the constructor has to contain all mapped properties of the embedded struct as parameter. You do not need to specify the ConstructorParameter attribute for members, as long as they follow Genome’s default naming expectation.
When mapping an embedded struct to a class, remember to ensure that a default constructor is available if you do not provide a special initialisation constructor as described above.
Like methods of any other CLR type, methods of embedded structs can be mapped to be used in OQL.
On the previously discussed Address class, imagine the following method to look up the country code of an Address:
Note that Address is expressed as a class, since you cannot use this (access instance members) in lambda expressions in C#.
You can map this expression as well, in order to use it in other queries:
Note that this mapping of Address just provides server-side behaviour for Address.GetCountryCode() and has nothing to do with the mapping of Customer.Address.
After that, you can use the mapped member in server-side expressions, such as filtering and projection. The following query returns all customers with country code "AUT":
The same in LINQ:
Note that both queries are fully evaluated in the database, joining the CountryCodes table in the WHERE clause.
When working with embedded structs, keep in mind that Genome can only detect changes applied to the property that returns the struct, not to changes of values within the struct.
This means that an update such as
can be tracked, while the following update is not detected by Genome:
It is therefore recommended to implement embedded structs as immutable, as described below.
When updating struct members, keep in mind that structs are value types: this means that you cannot update the property of a struct as shown below (assuming the previously described domain model that has Address implemented as struct):
The reason for this is that customer.Address is a property or field that returns the address as a new value (and not as a reference). Any changes you apply to this new value will be lost and not assigned to customer.Address.
Instead, assign the value to a variable, on which you can performan changes, and assign the new struct value back to customer.Address:
To avoid this confusion, it is a recommended pattern in .NET to implement structs as immutable. This means that the struct can only be initialised with the constructor, and exposes all properties read only:
Note that the public constructor for initialising the struct needs to match Genome’s default naming expectations or has to be mapped explicitly with ConstructorParameter.
Assigning a new Country is then straightforward:
When Address is a class, the members are directly modifiable. However, Genome cannot detect changes performed directly on members of an embedded class. It is therefore recommended again to make the class immutable by exposing the members read only, allowing only for initialisation by the constructor, as described previously.
Editions:Professional, Evaluation, Express
Database Platforms:Microsoft SQL Server 2000, Microsoft SQL Server 2005, Oracle 9i Release 2, Oracle 10g Release 2, IBM DB2