How to: Control Reference Resolution
The AggregateMetadataSource class uses a special algorithm internally to resolve references when merging objects. Suppose you have a class A and a class B, which classes you want to merge. Class A has a reference to class C. In this case, when you merge class A and class B, the AggregateMetadataSource class cannot resolve the reference from A to C out of the box. Reference resolution can be controlled by specifying AggregationOptions in the AggregateMetadataSource. The AggregationOptions enumeration exposes the following values:
- Immediate - the loading of the references will be performed immediately.
- Late - the loading of the references will be performed at a later stage.
The following example demonstrates how to specify the AggregationOptions. Practical examples and more explanations about the reference resolution will be presented in the Examples section.
AggregateMetadataSource aggregateMetadataSource = new AggregateMetadataSource(
new PersonMetadataSource(), new AddressMetadataSource(), AggregationOptions.Late);
MetadataContainer metadataContainer = aggregateMetadataSource.GetModel();
Dim _aggregateMetadataSource As New AggregateMetadataSource(New PersonMetadataSource(), _
New AddressMetadataSource(), AggregationOptions.Late)
Dim _metadataContainer As MetadataContainer = _aggregateMetadataSource.GetModel()
If you don't specify AggregationOptions, the default value is AggregationOptions.Immediate.
Examples
The following section illustrates practical examples demonstrating how to use the AggregateOptions enumeration.
The complete code-snippets for all classes used in this section are listed at the end of the topic.
Suppose you have a solution with three projects (the project structure is displayed on the image below):
- AssemblyA - contains Person and PersonMetadataSource classes. The project has a reference to the AssemblyB project. The PersonMetadataSource derives from the FluentMetadataSource class and uses Default Mapping to map the Person class.
- AssemblyB - contains Address and AddressMetadataSource classes. The AddressMetadataSource derives from the FluentMetadataSource class and uses Default Mapping to map the Address class.
- AssemblyC - contains Client and ClientMetadataSource classes. The project has references to the AssemblyA and AssemblyC projects. The ClientMetadataSource derives from the FluentMetadataSource class and uses Default Mapping to map the Client class.
The Person class has a reference to the Address class. The Client class has references to the Person and Address classes. The relationship between the classes is displayed on the image below.
Merging Person and Address
Merging the Person and Address classes is easy. In this case all references are known in advance, and the AggregateMetadataSource will resolve the references without your assistance. Specifying AggregateOptions in this case will not affect the reference resolution by no means.
AggregateMetadataSource aggregateMetadataSource = new AggregateMetadataSource(
new PersonMetadataSource(), new AddressMetadataSource());
Dim _aggregateMetadataSource As New AggregateMetadataSource(New PersonMetadataSource(), New AddressMetadataSource())
In the previous example, the Person class has a reference to the Address class. During the merge you are passing a mapping configuration about the Person and Address classes at the same time. That's why, the AggregateMetadataSource will resolve the references automatically, and no AggregateOptions need to be specified. Later, you can merge the Client class with the result AggregateMetadataSource (the result from the merging of the Person and the Address classes).
AggregateMetadataSource aggregateMetadataSource = new AggregateMetadataSource(
new PersonMetadataSource(), new AddressMetadataSource());
AggregateMetadataSource result = new AggregateMetadataSource(aggregateMetadataSource,
new ClientMetadataSource());
MetadataContainer metadataContainer = result.GetModel();
Dim _aggregateMetadataSource As New AggregateMetadataSource(New PersonMetadataSource(), _
New AddressMetadataSource())
Dim result As New AggregateMetadataSource(_aggregateMetadataSource, _
New ClientMetadataSource())
Dim _metadataContainer As MetadataContainer = result.GetModel()
A more interesting case is when you want to merge Person with Client or Address with Client first. In both of the cases, the AggregateMetadataSource will not have enough information to resolve the references. For example, suppose you want to merge Client and Person. The Client class has a reference to Person. However, it has a reference to the Address class as well. In this case during the merge, the information about the reference resolution is not sufficient. Namely, this is the scenario where the AggregateOptions enumeration comes to the scene.
AggregateMetadataSource aggregateMetadataSource = new AggregateMetadataSource(
new PersonMetadataSource(AggregationOptions.Late),
new ClientMetadataSource(AggregationOptions.Late), AggregationOptions.Late);
Dim _aggregateMetadataSource As New AggregateMetadataSource(_
New PersonMetadataSource(AggregationOptions.Late), _
New ClientMetadataSource(AggregationOptions.Late), AggregationOptions.Late)
When you use AggregateOptions.Late, you are instructing the AggregateMetadataSource, PersonMetadataSource and ClientMetadataSource that the needed information about the Address reference will be provided later. In this case, the AggregateMetadataSource will create a reference to a type that is not defined in this metadata source. Later, when you merge the Address class with the AggregateMetadataSource (the result from the Person and Client merge), the reference from the Client to the Address object will be resolved.
AggregateMetadataSource aggregateMetadataSource = new AggregateMetadataSource(
new PersonMetadataSource(AggregationOptions.Late),
new ClientMetadataSource(AggregationOptions.Late), AggregationOptions.Late);
AggregateMetadataSource result = new AggregateMetadataSource(aggregateMetadataSource,
new AddressMetadataSource());
MetadataContainer metadataContainer = result.GetModel();
Dim _aggregateMetadataSource As New AggregateMetadataSource( _
New PersonMetadataSource(AggregationOptions.Late), _
New ClientMetadataSource(AggregationOptions.Late), AggregationOptions.Late)
Dim result As New AggregateMetadataSource(_aggregateMetadataSource, New AddressMetadataSource())
Dim _metadataContainer As MetadataContainer = result.GetModel()
Note that in the previous example you have to pass explicitly AggreationOptions.Late to the PersonMetadataSource and ClientMetadataSource. When creating a new FluentMetadataSource, its constructor also accept AggreationOptions parameter. By default its value is AggreationOptions.Immediate.
If you use AggregateOptions.Immediate, you are instructing the AggregateMetadataSource to resolve the references immediately. In this case when you merge the Client and Person classes, the AggregateMetadataSource will not create a reference to a type that is not defined in this metadata source. And later when you merge Address with the result AggregateMetadataSource (the result from the Client and Person merge), the reference from the Client to the Address object will not be resolved.
AggregateMetadataSource clientPersonMetadataSource = new AggregateMetadataSource(
new ClientMetadataSource(), new PersonMetadataSource(), AggregationOptions.Immediate);
Dim clientPersonMetadataSource As New AggregateMetadataSource(New ClientMetadataSource(), _
New PersonMetadataSource(), AggregationOptions.Immediate)
Create a Custom Context Class
Once you have your metadata configured, the next step is to implement a context that is specific to your model and your entities. You simply create a new class that derived from OpenAccessContext and provides properties of type IQueryable<T>. For more information, please refer to the How to: Define OpenAccessContext Class topic. For a complete walkthrough, take a look at Overview.
Code Reference
-
Person
using AssemblyB; namespace AssemblyA { public class Person { public int Id { get; set; } public string Name { get; set; } public Address PersonAddress { get; set; } } }
Imports AssemblyB Namespace AssemblyA Public Class Person Public Property Id() As Integer Public Property Name() As String Public Property PersonAddress() As Address End Class End Namespace
-
PersonMetadataSource
using System.Collections.Generic; using Telerik.OpenAccess.Metadata.Fluent; using Telerik.OpenAccess.Metadata; namespace AssemblyA { public class PersonMetadataSource : FluentMetadataSource { public PersonMetadataSource() { } public PersonMetadataSource(AggregationOptions aggregateOptions) : base(aggregateOptions) { } protected override IList<MappingConfiguration> PrepareMapping() { IList<MappingConfiguration> preparedConfiguration = new List<MappingConfiguration>(); MappingConfiguration<Person> personConfiguration = new MappingConfiguration<Person>(); personConfiguration.MapType().ToTable("People"); preparedConfiguration.Add(personConfiguration); return preparedConfiguration; } } }
Imports System.Collections.Generic Imports Telerik.OpenAccess.Metadata.Fluent Imports Telerik.OpenAccess.Metadata Namespace AssemblyA Public Class PersonMetadataSource Inherits FluentMetadataSource Public Sub New() End Sub Public Sub New(ByVal aggregateOptions As AggregationOptions) MyBase.New(aggregateOptions) End Sub Protected Overrides Function PrepareMapping() As IList(Of MappingConfiguration) Dim preparedConfiguration As IList(Of MappingConfiguration) = New List(Of MappingConfiguration)() Dim personConfiguration As New MappingConfiguration(Of Person)() personConfiguration.MapType().ToTable("People") preparedConfiguration.Add(personConfiguration) Return preparedConfiguration End Function End Class End Namespace
-
Address
namespace AssemblyB { public class Address { public int Id { get; set; } public string Country { get; set; } public string Street { get; set; } } }
Namespace AssemblyB Public Class Address Public Property Id() As Integer Public Property Country() As String Public Property Street() As String End Class End Namespace
-
AddressMetadataSource
using System.Collections.Generic; using Telerik.OpenAccess.Metadata.Fluent; using Telerik.OpenAccess.Metadata; namespace AssemblyB { public class AddressMetadataSource : FluentMetadataSource { public AddressMetadataSource() { } public AddressMetadataSource(AggregationOptions aggregateOptions) : base(aggregateOptions) { } protected override IList<MappingConfiguration> PrepareMapping() { IList<MappingConfiguration> preparedConfiguration = new List<MappingConfiguration>(); MappingConfiguration<Address> addressConfiguration = new MappingConfiguration<Address>(); addressConfiguration.MapType().ToTable("Addresses"); preparedConfiguration.Add(addressConfiguration); return preparedConfiguration; } } }
Imports System.Collections.Generic Imports Telerik.OpenAccess.Metadata.Fluent Imports Telerik.OpenAccess.Metadata Namespace AssemblyB Public Class AddressMetadataSource Inherits FluentMetadataSource Public Sub New() End Sub Public Sub New(ByVal aggregateOptions As AggregationOptions) MyBase.New(aggregateOptions) End Sub Protected Overrides Function PrepareMapping() As IList(Of MappingConfiguration) Dim preparedConfiguration As IList(Of MappingConfiguration) = New List(Of MappingConfiguration)() Dim addressConfiguration As New MappingConfiguration(Of Address)() addressConfiguration.MapType().ToTable("Addresses") preparedConfiguration.Add(addressConfiguration) Return preparedConfiguration End Function End Class End Namespace
-
Client
using AssemblyA; using AssemblyB; namespace AssemblyC { public class Client { public int Id { get; set; } public Person Person { get; set; } public Address Address { get; set; } } }
Imports AssemblyA Imports AssemblyB Namespace AssemblyC Public Class Client Public Property Id() As Integer Public Property Person() As Person Public Property Address() As Address End Class End Namespace
-
ClientMetadataSource
using System.Collections.Generic; using Telerik.OpenAccess.Metadata.Fluent; using Telerik.OpenAccess.Metadata; namespace AssemblyC { public class ClientMetadataSource : FluentMetadataSource { public ClientMetadataSource() { } public ClientMetadataSource(AggregationOptions aggregateOptions) : base(aggregateOptions) { } protected override IList<MappingConfiguration> PrepareMapping() { IList<MappingConfiguration> preparedConfigurations = new List<MappingConfiguration>(); MappingConfiguration<Client> clientConfiguration = new MappingConfiguration<Client>(); clientConfiguration.MapType().ToTable("ClientInfo"); preparedConfigurations.Add(clientConfiguration); return preparedConfigurations; } } }
Imports System.Collections.Generic Imports Telerik.OpenAccess.Metadata.Fluent Imports Telerik.OpenAccess.Metadata Namespace AssemblyC Public Class ClientMetadataSource Inherits FluentMetadataSource Public Sub New() End Sub Public Sub New(ByVal aggregateOptions As AggregationOptions) MyBase.New(aggregateOptions) End Sub Protected Overrides Function PrepareMapping() As IList(Of MappingConfiguration) Dim preparedConfigurations As IList(Of MappingConfiguration) = New List(Of MappingConfiguration)() Dim clientConfiguration As New MappingConfiguration(Of Client)() clientConfiguration.MapType().ToTable("ClientInfo") preparedConfigurations.Add(clientConfiguration) Return preparedConfigurations End Function End Class End Namespace