|
|
|
Dynamic Entity Types
|
Level 300
DevForce Enterprise
|
Sep 28, 06
|
|
|
DevForce developers define “static” business object types at design
time by mapping class definitions to objects in data source schemas
such as tables, views, and stored procedures in relational databases.
Sometimes we don’t know the structure of the class we’ll need until
runtime. For example:
- A particular search can fetch data from many rows of a very large table depending
upon how the user structures the query. Poor performance is a huge risk. We want
to retrieve the minimum number of columns necessary to present the result in a grid.
We don’t know which columns until the user specifies the query.
- We want to generate some aggregate statistics from a table (counts, averages, etc.).
We compose a query to retrieve them although we don’t know what we’re
after (the columns returned by the query) until runtime, perhaps because the user
picks the statistics from a menu.
- We may need to build a class around a table we discover in the runtime database
– a table that didn’t exist in the design-time database. This can be
a “user-defined table” that our customer has added to the database in
order to extend and customize user information managed by our application.
In all cases we’d like to represent the data as entities in our business object
model. But we can’t use any of the static entity types output by the Object
Mapper because we don’t know the “schema” of those entities until
runtime.
Enter the “Dynamic Entity Type.” As of release 3.2.1.1,
DevForce developers can create a new “dynamic” type – a new class
– at runtime.
Dynamic entity type instances can be queried, created, modified, and deleted. They
reside in the entity cache of a
PersistenceManager’s and inherit from the DevForce Entity class, just like instances
of the static entity types. A dynamic entity type may even be shared with other
clients if the clients are n-tier deployed and use the same name for the type.
Keyed and Unkeyed Dynamic Entity Types
Dynamic entity types come in two basic flavors: those defined with, and those defined
without a primary key.
Dynamic entity types with a primary key operate almost exactly like a statically
defined DevForce entity type. Query results are merged into a PersistenceManager according to the
query’s MergeStrategy.
The primary key helps the merge discover if the arriving entity is new to the cache
or is already present; if present, the strategy determines whether the arriving
entity updates the cached entity, replaces it, or is ignored.
Merge logic depends upon schema consistency across queries. It follows that the
structure of the result of all queries for a keyed dynamic entity type must match
the schema for that type. The type’s schema is only “open” for
the first query.
On the other hand, when we query for dynamic entity types without a primary
key, the PersistenceManager does not merge the results. Absent a primary key, it
cannot determine if the arriving entity is already in the cache. Therefore, for
keyless dynamic entities, the PersistenceManager first discards all prior instances
of the type and then fills the cache with the query results. And in this circumstance,
schema consistency is not an issue, so the PersistenceManager, upon detecting a
difference, simply drops the old schema and adopts the new schema implied in the
query result.
Creating Dynamic Entity Types
Dynamic entity types can be created, with or without a primary key, using the CreateType()
method of the new DynamicEntityTypeBuilder class. Here, for example, is the code
to create a dynamic entity type with a primary key, defined by name.
|
|
C#:
Type type1 =
DynamicEntityTypeBuilder.CreateType("foo1", "IdeaBladeTest1", “LastName”);
VB.NET:
Dim type1 As Type = DynamicEntityTypeBuilder.CreateType( _
"foo1", "IdeaBladeTest1", “LastName”)
|
In the above example, the first argument is the name for the type; the second argument
is a DevForce DataSourceKeyName; the final argument is the name of the
primary key column.
You can create a multi-part key simply by specifying the additional column names
in subsequent parameters (no limit):
|
|
C#:
Type type1 =
DynamicEntityTypeBuilder.CreateType("foo1", "IdeaBladeTest1", “CompanyName”, “EmployeeNumber”);
VB.NET:
Dim type1 As Type = DynamicEntityTypeBuilder.CreateType( _
"foo1", "IdeaBladeTest1", “CompanyName”, “EmployeeNumber”)
|
|
Key columns can also be referenced by their column index. The equivalent of the
multi-part key specification just illustrated would be the following, assuming that
the column indexes for CompanyName and EmployeeNumber are 0 and 1, respectively.
That data should come from the default datasource.
|
|
C#:
Type aDynamicType =
DynamicEntityTypeBuilder.CreateType("foo1", "Default", 0,1);
VB.NET:
Dim aDynamicType As Type = DynamicEntityTypeBuilder.CreateType( _
"foo1", "Default", 0,1)
|
|
To create a dynamic entity type without a primary key, simply omit the
primary key column:
|
|
C#:
Type type1 = DynamicEntityTypeBuilder.CreateType("foo1", "IdeaBladeTest1");
VB.NET:
Dim type1 As Type = DynamicEntityTypeBuilder.CreateType( _
"foo1", "IdeaBladeTest1")
|
|
An additional option for creating dynamic entity types will be available in the
next release of DevForce, scheduled for early November: declaring the schema programmatically
at runtime. That will permit the creation of an entity that has no database table
behind it at all. Instances of such an entity can then be created and populated
programmatically. And note that any instance of a dynamic entity, whether
backed by a database table or not, can be persisted to a local file using the PersistenceManager’s
familiar SaveEntitySet() method.
Querying for Instances of Dynamic Entity Types
Dynamic entity types can be queried either from the
PersistenceServer via a
PassthruRdbQuery or from the local PersistenceManager via a normal
EntityQuery. (A forthcoming
version of this feature will support querying for dynamic type entities from the
PersistenceServer
via either the StoredProcRdbQuery
or the WsQuery.)
Querying from the PersistenceServer
(Backend Database)
|
|
C#:
PersistenceManager pm = PersistenceManager.DefaultManager;
Type type1 =
DynamicEntityTypeBuilder.CreateType("foo1", " Default",”LastName”);
IEntityQuery query1 = new PassthruRdbQuery(
type1, "select FirstName, LastName from Employee");
IList
entities1 = pm.GetEntities(query);
Type type2 = DynamicEntityTypeBuilder.CreateType("foo2", "Default");
IEntityQuery query1 =
new PassthruRdbQuery(type2, "select count(*) from orders");
IList entities2 = pm.GetEntities(query);
VB.NET:
Dim pm As PersistenceManager = PersistenceManager.DefaultManager
Dim type1 As Type = DynamicEntityTypeBuilder.CreateType( _
"foo1", " Default",”LastName”)
Dim query1 As IEntityQuery = New PassthruRdbQuery( _
type1, "select FirstName, LastName from Employee")
Dim entities1 As IList(Of DynamicEntity) = _
pm.GetEntities(Of DynamicEntity)(query)
Dim type2 As Type = DynamicEntityTypeBuilder.CreateType("foo2", "Default")
Dim query1 As IEntityQuery = _
New PassthruRdbQuery(type2, "select count(*) from orders")
Dim entities2 As IList(Of DynamicEntity) = _
pm.GetEntities(Of DynamicEntity)(query)
|
|
Querying from the PersistenceManager Cache
|
|
C#:
PersistenceManager pm = PersistenceManager.DefaultManager;
Type type1 =
DynamicEntityTypeBuilder.CreateType("foo1", "Default", ”LastName”);
// EntityQueries against dynamic entities automatically have //
EnableUDFQueries=true
EntityQuery query = new EntityQuery(type1, "LastName", EntityQueryOp.LT, "G");
IList entities =
pm.GetEntities(query, QueryStrategy.CacheOnly);
VB.NET:
Dim pm As PersistenceManager = PersistenceManager.DefaultManager
Dim type1 As Type = DynamicEntityTypeBuilder.CreateType( _
"foo1", " Default",”LastName”)
' EntityQueries against dynamic entities automatically have '
EnableUDFQueries=true
Dim query As EntityQuery = _
New EntityQuery(type1, "LastName", EntityQueryOp.LT, "G")
Dim entities As IList(Of DynamicEntity) = _
pm.GetEntities(Of Entity)(query, QueryStrategy.CacheOnly)
|
|
Accessing Data in a Dynamic Entity Type Object
You can access the data in a dynamic entity type object by name, or by index, with
the Entity indexing method.
|
|
C#:
String lastName;
lastName = (String) aDynamicEntity[“LastName”]; // or
lastName = (String) aDynamicEntity[1];
VB.NET:
Dim lastName As String
lastName = CType(aDynamicEntity(“LastName”), String) ' or
lastName = CType(aDynamicEntity(1), String)
|
|
This is the same approach we use to access data in a User-Defined Field (UDF).
Saving Changes
Currently new, modified, or deleted dynamic entities in the PersistenceManager cannot
be saved back to the backend datastore. This will be supported in the next release
of DevForce. Meanwhile, enjoy the local-persistence-only variety!
|
|
All Tech Tips
|