Instantiating an existing entity instance
To load the entity's data from the persistent storage, we use the generated class related to this entity's definition, create an instance of that class and load the data (the actual entity instance) of the particular entity using a DataAccessAdapter object. As an example we're loading the entity identified with the customerID "CHOPS" into an object.
Using the primary key value
One way to instantiate the entity in an object is by passing all primary key values to the constructor of the entity class to use:
CustomerEntity customer = new CustomerEntity("CHOPS");
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
adapter.FetchEntity(customer);
}
Dim customer As New CustomerEntity("CHOPS")
Using adapter As New DataAccessAdapter()
adapter.FetchEntity(customer)
End Using
This will load the entity with the primary key value of "CHOPS" into the object named customer.
FetchEntity has several overloads which offer different additional features. See the DataAccessAdapterBase class in the LLBLGen Pro runtime framework reference manual for details.
Using Linq / QuerySpec
There are other ways to fetch an entity using the PK value. LLBLGen Pro supports Linq and has its own high-level query system QuerySpec. The example below looks like the following in these two query systems.
CustomerEntity customer = null;
using(var adapter = new DataAccessAdapter())
{
var metaData = new LinqMetaData(adapter);
customer = metaData.Customer.FirstOrDefault(c=>c.CustomerId=="CHOPS);
}
var qf = new QueryFactory();
var q = qf.Customer.Where(CustomerFields.CustomerId=="CHOPS");
CustomerEntity customer = null;
using(var adapter = new DataAccessAdapter())
{
customer = adapter.FetchFirst(q);
}
Using a related entity
Another way to instantiate this same entity is via a related entity. Adapter however doesn't support automatic data loading when you traverse a relationship (lazy loading), all data has to be fetched up-front. A related entity offers a way to formulate the exact filters to fetch a specific entity very easily.
Let's load the order with ID 10254
, which
is an order of customer CHOPS
. We can now use the loaded order to load
an instance of the entity CHOPS
. The example uses the
KeepConnectionOpen feature by passing true to the constructor of the
DataAccessAdapter object. The example explicitly closes the connection
after the DataAccessAdapter usage is finished as we've ordered it to
keep it open.
OrderEntity order = new OrderEntity(10254);
using(DataAccessAdapter adapter = new DataAccessAdapter(true))
{
adapter.FetchEntity(order);
order.Customer = adapter.FetchNewEntity<CustomerEntity>(order.GetRelationInfoCustomer());
adapter.CloseConnection();
}
Dim order As New OrderEntity(10254)
Using adapter As New DataAccessAdapter(True)
adapter.FetchEntity(order)
order.Customer = adapter.FetchNewEntity(Of CustomerEntity)(order.GetRelationInfoCustomer())
adapter.CloseConnection()
End Using
By setting order.Customer to an instance of CustomerEntity, the logic automatically sets the CustomerID field of order to the CustomerID of the specified CustomerEntity instance. Also the order object will be added to the CustomerEntity instance's Orders collection. This means that the following is true after the above code snippet:
- order.CustomerID is equal to order.Customer.CustomerID
- order.Customer.Orders.Contains(order) is true
The framework keeps the two in sync as well. Consider the following
situation: a new EmployeeEntity instance employee, which has an
autonumber primary key field, and a new OrderEntity instance order. When
the following is done: order.Employee = employee;
, and the order is
saved (or the employee), the field order.EmployeeID is automatically set
to the new key field of the employee object after employee is saved.
If CustomerEntity is in an inheritance hierarchy, the fetch is polymorphic.
This means that if the Order entity, in this case the entity with PK value 10254
, has a
reference to a derived type of CustomerEntity, for example GoldCustomer,
the entity returned will be of type GoldCustomer. See also Polymorphic
fetches below.
Using a unique constraint's value
Entities can have other unique identifying attributes which are defined
in the database and the LLBLGen Pro designer as unique constraints. In
addition to the primary key these unique values can be used to load an
entity. In our example, the CustomerEntity entity has a unique constraint
defined on its field CompanyName, so we can use that field to load the
same entity that the CHOPS
example loaded above.
Fetching the entity using a unique constraint is done via these steps: first create an empty entity class instance, set the fields which form the unique constraint to the lookup value, then fetch the entity data using a special method call of the DataAccessAdapter.
Because an entity can have more than one unique constraint, you have to specify which unique constraint to use, which means: specify a filter for the unique constraint columns. Entities with unique constraints have methods to construct these filters automatically as shown in the following example.
CustomerEntity customer = new CustomerEntity();
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
customer.CompanyName = "Chop-suey Chinese";
adapter.FetchEntityUsingUniqueConstraint(customer, customer.ConstructFilterForUCCompanyName());
}
Dim customer As New CustomerEntity()
Using adapter As New DataAccessAdapter()
customer.CompanyName = "Chop-suey Chinese"
adapter.FetchEntityUsingUniqueConstraint(customer, customer.ConstructFilterForUCCompanyName())
End Using
Using a prefetch path
An easy way to instantiate an entity can be by using a Prefetch Path, to read related entities together with the entity or entities to fetch. See for more information about Prefetch Paths and how to use them: Prefetch Paths.
Using a collection class
Another way to instantiate an entity is by instantiating a collection class and fetch one or more entity definitions into that collection. This method is described in detail in the section about collection classes. You can also see Tutorials and Examples: How Do I? - Read all entities into a collection.
Using a Context object
If you want to get a reference to an entity object already in memory,
you can use a Context object, if
that object was added to that particular Context object. The example
below retrieves a reference to the customer object with PK CHOPS
, if
that entity was previously loaded into an entity object which was added
to that Context object. If the entity object isn't in the Context
object, a new entity object is returned. An example usage is shown
below.
CustomerEntity customer = (CustomerEntity)myContext.Get(new CustomerEntityFactory(), "CHOPS");
if(customer.IsNew)
{
// not found in context, fetch from database (assumes 'adapter' is a
// DataAccessAdapter instance)
adapter.FetchEntity(customer);
}
Dim customer As CustomerEntity = CType(myContext.Get(New CustomerEntityFactory(), "CHOPS"), CustomerEntity)
If customer.IsNew Then
' not found in context, fetch from database (assumes 'adapter' is a DataAccessAdapter instance)
adapter.FetchEntity(customer)
End If
Polymorphic fetches
Already mentioned early in this section is the phenomenon called 'Polymorphic fetches'. Imagine the following entity setup: BoardMember entity has a relationship (m:1) with CompanyCar. CompanyCar is the root of a TargetPerEntityHierarchy inheritance hierarchy and has two subtypes: FamilyCar and SportsCar. Because BoardMember has the relationship with CompanyCar, a field called CompanyCar is created in the BoardMember entity which is mapped onto the m:1 relationship BoardMember - CompanyCar.
In the database, several BoardMember instances have been stored, as well as several different CompanyCar instances, of type FamilyCar or SportsCar. Using DataAccessAdapter.FetchNewEntity, you can load the related CompanyCar instance of a given BoardMember's instance by using the following code:
CompanyCarEntity car =
adapter.FetchNewEntity<CompanyCarEntity>(myBoardMember.GetRelationInfoCompanyCar());
Dim car As CompanyCarEntity = _
adapter.FetchNewEntity(Of CompanyCarEntity)(myBoardMember.GetRelationInfoCompanyCar())
However, car
in the example above, can be of a different type. If for
example the BoardMember instance in myBoardMember
has a FamilyCar as
company car set, car
is of type FamilyCar. Because the fetch action
can result in multiple types, the fetch is called polymorphic. So, in
our example, if car
is of type FamilyCar, the following code would
also be correct:
FamilyCarEntity car = adapter.FetchNewEntity<FamilyCarEntity>(myBoardMember.GetRelationInfoCompanyCar());
Dim car As FamilyCarEntity = adapter.FetchNewEntity(Of FamilyCarEntity)(myBoardMember.GetRelationInfoCompanyCar()))
Would this BoardMember instance have a SportsCar set as company car, this code would fail at runtime with a specified cast not valid exception.
DataAccessAdapter.FetchEntity and inheritance
DataAccessAdapter.FetchEntity() is not polymorphic. This is by design as it fetches the entity data into the passed in entity object. As it's already an instance, it would be impossible to change that instance' type to a derived type if the PK values identify an entity which is of a subtype of the type of the passed in entity instance.
In our previous example about BoardMember and CompanyCar, BoardMember is a derived type of Manager which is a derived type of Employee. If FetchEntity is called by passing in an Employee instance, and the PK identifies a BoardMember, only the Employee's fields are loaded, however if the entity is in a hierarchy of type TargetPerEntity, LLBLGen Pro will perform joins with all subtypes from the supertype, to make sure a type is stored OK.
Be aware of the fact that polymorphic fetches of entities in a TargetPerEntity hierarchy use JOINs between the root entity's target and all subtype targets when the root type is specified for the fetch. This can have an inpact on performance.