Artificial Data API
This article is relevant to entity models that utilize the deprecated Visual Studio integration of Telerik Data Access. The current documentation of the Data Access framework is available here.
Artificial types/fields are types/fields that are not present in the model itself but are defined during runtime. Telerik Data Access provides a single API point that enables you to do operations with artificial types and fields. The API significantly improves the usability of the artificial types and it is easily accessible through the OpenAccessContext instance.
This topic will make a quick overview of the methods for working with Artificial Data:
- OpenAccessContext Extensions
- Setting and Getting Artificial Properties/Fields
- SymbolicFieldName Class
For additional information, please refer to the Artificial Types section in the Code-Only documentation.
OpenAccessContext Extensions
The Telerik.OpenAccess namespace provides the following extension methods for the OpenAccessContext type. Generally, the methods allow you to create new instances of artificial types and to query for artificial types from the database.
To use the extension methods for artificial data you need to use/import the Telerik.OpenAccess namespace.
- CreateInstance(MetaPersistentType persistentType) - creates an instance of the specified persistent type and returns it as object. The MetaPersistentType parameter specifies the artificial type.
-
CreateInstance(string persistentTypeFullName) - creates an instance of the specified persistent type and returns it as object. The method accepts a string parameter representing the full name ({Namespace}.{TypeName}) of the artificial type.
using ( FluentModelContext fluentContext = new FluentModelContext() ) { object product = fluentContext.CreateInstance( "FluentModel.Product" ); Telerik.OpenAccess.Metadata.MetaPersistentType categoryType = fluentContext.Metadata.PersistentTypes.FirstOrDefault( t => t.FullName == "FluentModel.Category" ); object category = fluentContext.CreateInstance( categoryType ); }
Using fluentContext As New FluentModelContext() Dim product As Object = fluentContext.CreateInstance("FluentModel.Product") Dim categoryType As Telerik.OpenAccess.Metadata.MetaPersistentType = fluentContext.Metadata.PersistentTypes.FirstOrDefault(Function(t) t.FullName = "FluentModel.Category") Dim category As Object = fluentContext.CreateInstance(categoryType) End Using
GetAll(MetaPersistentType persistentType) - returns an IQueryable endpoint, which allows you to write dynamic LINQ queries against artificial persistent types. The MetaPersistentType parameter specifies the artificial type.
-
GetAll(string typeFullName) - returns an IQueryable endpoint, which allows you to write dynamic LINQ queries against artificial persistent types. The method accepts a string parameter representing the full name ({Namespace}.{TypeName}) of the artificial type.
using ( FluentModelContext fluentContext = new FluentModelContext() ) { IQueryable result = fluentContext.GetAll( "FluentModel.Category" ). Where( string.Format( "Id < {0}", 10 ) ); foreach ( object category in result ) { Console.WriteLine( category.FieldValue<object>( "CategoryName" ) ); } Telerik.OpenAccess.Metadata.MetaPersistentType productType = fluentContext.Metadata.PersistentTypes.FirstOrDefault( t => t.FullName == "FluentModel.Product" ); result = fluentContext.GetAll( productType ). Where( string.Format( "Id < {0}", 10 ) ); foreach (object product in result) { Console.WriteLine( product.FieldValue<string>( "ProductName" ) ); } }
Using fluentContext As New FluentModelContext() Dim result As IQueryable = fluentContext.GetAll("FluentModel.Category").Where(String.Format("Id < {0}", 10)) For Each category As Object In result Dim categoryName As String = Telerik.OpenAccess.ExtensionMethods.FieldValue(Of Object)(category, "CategoryName") Console.WriteLine(categoryName) Next category Dim productType As Telerik.OpenAccess.Metadata.MetaPersistentType = fluentContext.Metadata.PersistentTypes. FirstOrDefault(Function(t) t.FullName = "FluentModel.Product") result = fluentContext.GetAll(productType).Where(String.Format("Id < {0}", 10)) For Each product As Object In result Dim productName As String = Telerik.OpenAccess.ExtensionMethods.FieldValue(Of String)(product, "ProductName") Console.WriteLine(productName) Next product End Using
CreateDynamicInstance(MetaPersistentType persistentType) - creates a new instance of the specified persistence type and returns the instance as dynamic.
-
CreateDynamicInstance(string persistentTypeFullName) - creates a new instance of the specified persistence type and returns the instance as dynamic. The method accepts a string parameter representing the full name ({Namespace}.{TypeName}) of the artificial type.
To use the CreateDynamicInstance method, your project should use .NET Framework version 4.0 or later and you need to add a reference to the Telerik.OpenAccess.40.Extensions.dll assembly.
using ( FluentModelContext fluentContext = new FluentModelContext() ) { dynamic product = fluentContext.CreateDynamicInstance( "FluentModel.Product" ); Telerik.OpenAccess.Metadata.MetaPersistentType categoryType = fluentContext.Metadata.PersistentTypes.FirstOrDefault( t => t.FullName == "FluentModel.Category" ); dynamic category = fluentContext.CreateDynamicInstance( categoryType ); }
Using fluentContext As New FluentModelContext() Dim product As Object = fluentContext.CreateDynamicInstance("FluentModel.Product") Dim categoryType As Telerik.OpenAccess.Metadata.MetaPersistentType = fluentContext.Metadata.PersistentTypes. FirstOrDefault(Function(t) t.FullName = "FluentModel.Category") Dim category As Object = fluentContext.CreateDynamicInstance(categoryType) End Using
Setting and Getting Artificial Properties/Fields
Setting and getting artificial fields is done using the following extension methods:
- SetFieldValue<T>(string nameOfPersistentField, T newValue) - sets the value of the specified artificial field.
- FieldValue<T>(string nameOfPersistentField) - gets the value of the specified artificial field.
The SetFieldValue and FieldValue extension methods are part of the Telerik.OpenAccess namespace.
When you want to use the SetFieldValue extension method you need to ensure that the type is managed by the context. In the example below, immediately after the creation of the artificial type, it is added to the fluent context.
The following example demonstrates how to use the SetFieldValue and FieldValue extension methods. First, a new category is created by using the CreateInstance extension method. Then, we need to ensure that the new category is managed by the context. That's why it is passed to the Add method. The next steps are to initialize the category by using the SetFieldValue method and commit the changes to the database. The final piece of code retrieves all categories from the database with id < 10 and prints their name by using the FieldValue method.
using ( FluentModelContext fluentContext = new FluentModelContext() )
{
object category = fluentContext.CreateInstance( "FluentModel.Category" );
fluentContext.Add( category );
category.SetFieldValue( "CategoryName", "MyCategory" );
category.SetFieldValue( "Description", "New Description" );
fluentContext.SaveChanges();
IQueryable result = fluentContext.GetAll( "FluentModel.Category" ).
Where( string.Format( "Id < {0}", 10 ) );
foreach ( object obj in result )
{
Console.WriteLine( obj.FieldValue<string>( "CategoryName" ) );
}
}
Using fluentContext As New FluentModelContext()
Dim category As Object = fluentContext.CreateInstance("FluentModel.Category")
fluentContext.Add(category)
' Extension methods on objects should be called as static methods
' due to limitations in VB
Telerik.OpenAccess.ExtensionMethods.SetFieldValue(category, "CategoryName", "MyCategory")
Telerik.OpenAccess.ExtensionMethods.SetFieldValue(category, "Description", "New Description")
fluentContext.SaveChanges()
Dim result As IQueryable = fluentContext.GetAll("FluentModel.Category").
Where(String.Format("Id < {0}", 10))
For Each obj As Object In result
Dim categoryName = Telerik.OpenAccess.ExtensionMethods.FieldValue(Of String)(obj, "CategoryName")
Console.WriteLine(categoryName)
Next obj
End Using
The next example does the same as the previous one. However, it is reworked to use the CreateDynamicInstance method. Note that in this case, you don't need to use the SetFieldValue and FieldValue methods.
using ( FluentModelContext fluentContext = new FluentModelContext() )
{
dynamic category = fluentContext.CreateDynamicInstance( "FluentModel.Category" );
fluentContext.Add( category );
category.categoryName = "MyCategory";
category.description = "New Description";
fluentContext.SaveChanges();
IQueryable result = fluentContext.GetAll( "FluentModel.Category" ).
Where( string.Format( "id < {0}", 10 ) );
foreach ( dynamic obj in result )
{
Console.WriteLine( obj.categoryName );
}
}
Using fluentContext As New FluentModelContext()
Dim category As Object = fluentContext.CreateDynamicInstance("FluentModel.Category")
fluentContext.Add(category)
category.categoryName = "MyCategory"
category.description = "New Description"
fluentContext.SaveChanges()
Dim result As IQueryable = fluentContext.GetAll("FluentModel.Category").
Where(String.Format("Id < {0}", 10))
For Each obj As Object In result
Console.WriteLine(obj.categoryName)
Next obj
End Using
MetaPersistentType Method
Finally, there is one additional method introduced on MetaPersistentType level. This is the GetMembers(bool includeBasedTypeMembers) method. It returns an enumeration of all primitive and navigation members of the persistent type.
To get an idea about the GetMembers(bool includeBasedTypeMembers) method take a look at the following example. Suppose you have two classes Animal and Cat. The Cat class derives from Animal.
The following table illustrates the output of the GetMembers(bool includeBasedTypeMembers) method depending on the class it is invoked:
GetMembers(true) | GetMembers(false) | |
Animal (base class) | Returns "AnimalId" and "Name" | Returns "AnimalId" and "Name" |
Cat (derived class) | Returns "AnimalId", "Name", "Breed" and "LivesLeft" | Returns "Breed" and "LivesLeft" |
SymbolicFieldName Class
The static class SymbolicFieldName allows developers to target previously inaccessible internal fields like:
- Discriminator column – the column that describes the type of an entity that is part of an inheritance hierarchy
- Version/Timestamp column – the column that holds concurrency control information for the version or the timestamp of the last update
- Internal Identity – the column that represents the automatic identity for an entity when the client did not supply one during creation of the entity mapping.
Below is a table that shows when and how to use the available Telerik.OpenAccess.SymbolicFieldName:
Symbolic Field Name | Meaning | Return type | When to Use |
ClassId | Discriminator column | String or Int32 | Entity type is part of inheritance hierarchy |
Version | Version/Timestamp column | Short or Long | Entity has concurrency control set to Version or Timestamp |
InternalPrimaryKey | Internal Identity | Int32 | Entity does not define any identity members |