- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Custom Entities
Joined: 26-Oct-2003
Here's the scenario:
I have 2 types of organizations, "Vendors", and "Manufacturers". Any organization can be either a Vendor, Manufacturer, or both. Currently the set of data (the columns) is identical for each. This could change in the future.
Organization Table
OrganizationID
Name
IsVendor
IsManufacturer
I rely heavily on the EntityType enum provided by the generated code for use in instancing detail forms, creating display collections, etc.
I would like to be able to create a "VendorEntity", and a "ManufacturerEntity" by inheriting from the "OrganizationEntity" generated by the framework, or perhaps creating brand-new entities.
Since I would like to be able to treat my custom entities the same as the generated entities as well as because I depend so heavily on the EntityType enum in the framework, I want to incorporate these custom entities into the generated code and not know that they weren't generated.
I've read through the section entitled "Extending the Framework Through Inheritance" in the documentation, as well as looked through the runtime library source code. Here's my problems:
- If I want to fetch a collection containing all of the Vendors (but not Manufacturers), the SQL query should look like this:
Select * FROM Organization WHERE IsVendor = 1
How do I do this? Before you say, "Use a predicate", remember that I'm trying to accomplish this:
Dim vendors as New EntityCollection(New VendorEntityFactory)
adapter.FetchEntityCollection(vendors, Nothing)
I do not want to have to remember to provide the requisite predicate because I'm trying to treat this the same as any other entity. However, there doesn't seem to be any way to specify in the EntityFactory what criteria to use when fetching entities of the required sort. I believe framework's assumption is that 1 Entity = 1 Table, thus there is no means to specify that 1 Entity = a subset of 1 table.
- When fetching individual custom entities by Primary Key,
Dim vendor as New VendorEntity(vendorID)
adapter.FetchEntity(vendor)
how do I ensure that the requested key (vendorID) is not only found in the table, but only found in the subset (the criteria) of that table that matches the custom entity's requirement. The query to fetch a Vendor, for example, should be
SELECT * FROM Organization
WHERE OrganizationID = @organizationID
AND IsVendor = 1
in order to ensure that if the user accidently provides me with a PK value that matches a Manufacturer, that that Manufacturer is not returned in the form of a Vendor. However, there doesn't seem to be any provision to do this. The only way I can see to do it currently is to allow the adapter to fetch the data, then dispose of the data in the custom entity factory if it doesn't match the criteria...after the data has already been fetched.Am I missing something?
- What is the best way to modify the EntityType enum with my new Custom Entity Types in order to avoid having to reupdate the enum everytime I regenerate the whole LLBL? Modify the template? I usually avoid this at all costs, but I'll do it if necessary. Is there a way to programmatically add a value to an enum? Please don't say CodeDOM...
Thanks for any help...
Jeff...
Any organization can be either a Vendor, Manufacturer, or both....
and
I would like to be able to create a "VendorEntity", and a "ManufacturerEntity" by inheriting from the "OrganizationEntity" generated by the framework
don't add up.
If a vendor can be a manufacturer, you can't create classes which derive from the same supertype. This is the same problem with person <- employee and person <- customer. What if the employee is a customer too?
Better is to apply 'roles' to a class. I.e.: add a RoleID to the entity, which illustrates what type the organisation is: vendor, manufacturer or both.
Extending the framework through inheritance means you create a new factory for organisationentity. In there you override the Create methods. Based on the value of the discriminator field you then create an OrganisationEntity instance, a VendorEntity instance or a ManufacturerEntity instance. Vendor and Manufacturer are subclasses of OrganisationEntity with basicly just constructors.
What you want though: filtering on discriminator fields without specifying a filter, is not yet implemented.
I believe framework's assumption is that 1 Entity = 1 Table, thus there is no means to specify that 1 Entity = a subset of 1 table.
Correct in that 1 table == 1 entity, as the table definition is the database equivalent of an entity definition. Inheritance support is not yet added (will be done in april). It turns out to be not that hard, but it will require some changes.
Your non-filter entity fetch, which does requires a filter on the descriminator, requires that FetchEntity (or FetchEntityCollection) feed the fetch logic with additional predicates and relations. This information is not yet in the entity framework and there is at the moment no way to inject these statements as well.
- What is the best way to modify the EntityType enum with my new Custom Entity Types in order to avoid having to reupdate the enum everytime I regenerate the whole LLBL? Modify the template? I usually avoid this at all costs, but I'll do it if necessary. Is there a way to programmatically add a value to an enum?
You don't, as all your types are in the same table, and the enum is used to create entity fields so you can use the Organization type for all.
But as you have an inheritance problem (vendor can be manufacturer and vice versa), inheritance is not the solution I'm afraid (and also a reason why I think it is very overrated in the database world. Inheritance to add behavior: yes. Inheritance to add types: not always a good solution)
Joined: 26-Oct-2003
Otis wrote:
Any organization can be either a Vendor, Manufacturer, or both....
and
I would like to be able to create a "VendorEntity", and a "ManufacturerEntity" by inheriting from the "OrganizationEntity" generated by the framework
don't add up.
If a vendor can be a manufacturer, you can't create classes which derive from the same supertype. This is the same problem with person <- employee and person <- customer. What if the employee is a customer too?
Well, let's look at that: What if? In my case it will happen all the time. There are two scenarios I see related to this: either the data set describing each subtype is the same or it is different. If it's the same, both subtypes live in the same table, with some "discriminator" as you say, and all data for both subtypes are pulled from the same table. If the data set is different, then either you continue to place all of the attributes for both subtypes in the same table, or you create new tables, one for each subtype, place common data in the supertype table, and subtype-specific data in the subtype tables.
Ok, given. Now what's the problem? I understand that if an Organization is both a Vendor and a Manufacturer, and I have to have the different attributes for each in their own subtype table. But in all read and write scenarios that I can think of this isn't a problem, as long as I can specify which type of entity I want, single-entity fetches, collection fetches, all saves, whatever shouldn't have a problem with this...am I missing something?
- What is the best way to modify the EntityType enum with my new Custom Entity Types in order to avoid having to reupdate the enum everytime I regenerate the whole LLBL? Modify the template? I usually avoid this at all costs, but I'll do it if necessary. Is there a way to programmatically add a value to an enum?
You don't, as all your types are in the same table, and the enum is used to create entity fields so you can use the Organization type for all.
But, technically, it's a different entity. I will be treating Manufacturers and Vendors differently, even though they are in the same table. Without a corresponding EntityType, I can't conditionally handle them in my code. For example, I generate (or, rather, instantiate) detail forms based on the entity type, so that for any given entity, bringing up the appropriate detail form looks like this:
Dim frm as Form
frm = DetailFormFactory.Create(VendorEntityType)
frm.Show
Without the entity type, this won't work. Boy, I can't wait for Partial Classes..
But as you have an inheritance problem (vendor can be manufacturer and vice versa), inheritance is not the solution I'm afraid (and also a reason why I think it is very overrated in the database world. Inheritance to add behavior: yes. Inheritance to add types: not always a good solution)
I'll be honest, I don't like the table solution much either, but it appears to be the only way to do it in the relational model. However, with a well designed entity or service layer the issues should be handled well enough.
Given what I know now, here are my enhancement requests:
- Allow entities to be defined as criteria other than "SELECT * FROM TABLE", including multiple-table views (1:1 relationships only, of course)
- Allow that criteria to be used when fetching both entities and collections, such that the user doesn't have to include the necessary filter each time.
- Allow me to create custom entities that are defined in my code, but handled in the same manner as the LLBL-generated code
- Allow me to extend the EntityType enum to include my custom entities without having to worry about re-adding them everytime the code is regenerated. (Perhaps in the designer?)
Jeff...
jeffreygg wrote:
Otis wrote:
Any organization can be either a Vendor, Manufacturer, or both....
and
I would like to be able to create a "VendorEntity", and a "ManufacturerEntity" by inheriting from the "OrganizationEntity" generated by the framework
don't add up.
If a vendor can be a manufacturer, you can't create classes which derive from the same supertype. This is the same problem with person <- employee and person <- customer. What if the employee is a customer too?
Well, let's look at that: What if? In my case it will happen all the time. There are two scenarios I see related to this: either the data set describing each subtype is the same or it is different. If it's the same, both subtypes live in the same table, with some "discriminator" as you say, and all data for both subtypes are pulled from the same table. If the data set is different, then either you continue to place all of the attributes for both subtypes in the same table, or you create new tables, one for each subtype, place common data in the supertype table, and subtype-specific data in the subtype tables.
Ok, given. Now what's the problem? I understand that if an Organization is both a Vendor and a Manufacturer, and I have to have the different attributes for each in their own subtype table. But in all read and write scenarios that I can think of this isn't a problem, as long as I can specify which type of entity I want, single-entity fetches, collection fetches, all saves, whatever shouldn't have a problem with this...am I missing something?
Yes, as inheritance uses is-a relations. So vendor is-a organisation and manufacturer is-a organisation. If you then also introduce vendor is-a manufacturer and manufacturer is-a vendor, you have multiple inheritance, which is not definable in .NET, unless you use interfaces and do the implementation yourself.
So IMHO, what you're confusing is type based inheritance with semantic data-interpretation. If I pull an entity out of the organisations table, I've to check the 'type' field to see what it really is: vendor, manufacturer or both. This is a 'role': it helps interpretation, but it is not fixed. Say, a manufacturer starts selling its own goods too: manufacturer becomes vendor. Does it change type? No, it changes roles.
With simple filters you get these out of the database: give me all the vendors, give me all the manufacturers. These sets can overlap, which makes them not suitable for inheritance.
- What is the best way to modify the EntityType enum with my new Custom Entity Types in order to avoid having to reupdate the enum everytime I regenerate the whole LLBL? Modify the template? I usually avoid this at all costs, but I'll do it if necessary. Is there a way to programmatically add a value to an enum?
You don't, as all your types are in the same table, and the enum is used to create entity fields so you can use the Organization type for all.
But, technically, it's a different entity. I will be treating Manufacturers and Vendors differently, even though they are in the same table. Without a corresponding EntityType, I can't conditionally handle them in my code. For example, I generate (or, rather, instantiate) detail forms based on the entity type, so that for any given entity, bringing up the appropriate detail form looks like this:
Dim frm as Form
frm = DetailFormFactory.Create(VendorEntityType)
frm.Show
Without the entity type, this won't work. Boy, I can't wait for Partial Classes..
You can pull organisations and check its type field and base your selection on that. As I said: because a vender can be a manufacturer as well and vice versa, having types is not going to work because what if an entity is both: what to instantiate: a manufacturer form or a vendor form?
But as you have an inheritance problem (vendor can be manufacturer and vice versa), inheritance is not the solution I'm afraid (and also a reason why I think it is very overrated in the database world. Inheritance to add behavior: yes. Inheritance to add types: not always a good solution)
I'll be honest, I don't like the table solution much either, but it appears to be the only way to do it in the relational model. However, with a well designed entity or service layer the issues should be handled well enough.
You can also do it in at least 2 other ways -> table per type, where you place the unique fields per type in a separate table and table per type where you place all fields of a type in a different table (thus in fact storing data multiple times). Both require joins, which is not that great but have the advantage that you can define FK's on them which only are valid for that type, not for the supertypes.
Given what I know now, here are my enhancement requests: 1. Allow entities to be defined as criteria other than "SELECT * FROM TABLE", including multiple-table views (1:1 relationships only, of course)
Will be added in april/may. This is already scheduled for a long time. 2 types of inheritance mapping will be supported: single table multiple classes and table per type where you store the unique fields per type in a separate table. Other types of inheritance mapping are pretty useless (if the hibernate guys say so, it must be true! )
- Allow that criteria to be used when fetching both entities and collections, such that the user doesn't have to include the necessary filter each time.
That follows out of 1) Queries for a given type should not require you to specify a filter to distinguish that type or require you to specify relations so all data of a type is fetched
- Allow me to create custom entities that are defined in my code, but handled in the same manner as the LLBL-generated code
I don't quite follow this one...
- Allow me to extend the EntityType enum to include my custom entities without having to worry about re-adding them everytime the code is regenerated. (Perhaps in the designer?)
I think if 3) is resolved in the designer 4) is solved as well so if you could explain 3) for me, I think 4) is done too