New Posts New Posts RSS Feed: Are multiple base types supported?
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Are multiple base types supported?

 Post Reply Post Reply
Author
pk55 View Drop Down
Senior Member
Senior Member


Joined: 22-Jul-2009
Location: CA
Posts: 105
Post Options Post Options   Quote pk55 Quote  Post ReplyReply Direct Link To This Post Topic: Are multiple base types supported?
    Posted: 18-Apr-2010 at 1:24pm
In DevForce 2009, you could create multiple injected base types and while the Object Mapper didn't display the inheritence correctly if you had more than one level between an entity and the base type, you could still manually edit the EDMX files to set the right parentage.  The CSDL had an ib:InjectedTypeMap which could specify all the types  in the form of:
ib:InjectedTypeMap="MyParentBaseType, Entity,t;MyChildBaseType,Entity,"
You'd then change the Entity (in the CSDL) to specify it's parentage:
<EntityType Name="SomeEntity" ib:AdornedBaseTypeName="MyChildBaseType -&gt; MyParentBaseType">
You'd also change the partial class declaration to inherit from MyChildBaseType rather than MyParentBaseType.  When you'd look at the ibedmx designer file for that model, you'd see the declaration matched to inherit from MyChildBaseType.
 
 
In DevForce 2010, the CSDL only has (as far as I can tell) an ib10:InjectedBaseType, which implies (to me) a single base type and there doesn't seem to be any AdornedBaseTypeName available.  Also, if I change the partial class to inherit from the child, the IB.Designer.cs declaration has it inheriting from the parent base type (the one and only injected base type).
 
Is this correct?
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 19-Apr-2010 at 8:02pm
You are correct. DevForce 2010 now offers one, and only one, pure-code, injected base type.
 
Of course you can use Entity Framework's own inheritance mechanism as you like. Thus, if Apples, Oranges and Bananas inherit from the Fruit entity, you can arrange that ... and all four of them will ultimately inherit from "YourBaseType" ... which inherits in turn from our Entity class.
 
As you know, Apples, Oranges, Bananas , and Fruit must all be mapped to something in the database (tables presumably). "YourBaseType" is the only custom, unmapped, pure-code class in the inheritance tree.
 
I'm assuming we're talking about entities rooted in Entity Framework and generated by DevForce. You can do anything you want with POCOs.
 
We deprecated multiple levels of pure-code, "injected base types" because they added complexity to the machinery that we hoped to avoid.
 
So in DevForce 2010 I can't inject a "RoundFruit" pure-code type between Fruit and {Apples, Oranges} ... as I could in DevForce 2009.
 
I'm eager to know what you would have done in "RoundFruit".  Oh ... I can imagine ... and I am not suggesting that there was anything the least bit wrong with having intermediate injected types such as "RoundFruit".
 
But I don't want to imagine. I want your use cases. How many such intermediate types do you have? What kinds of things are you doing in them?
 
Maybe we can come up with a good alternative. Maybe you'll give us a good reason to restore the feature.
 
We need your feedback.
 
--
 
p.s. Worst case ... and this is a hack ... you can do surgery on the generated code.
 
Put the desired inheritance relationship in the partial class (which DevForce will never touch) and erase the inheritance definition in the generated code.
 
For example:
  • Erase in the generated Apple class  ": MyBaseClass"  from the end of "public partial class Apple"
  • Add in your custom partial Apple class  " : RoundFruit"  to the end of "public partial class Apple"
  • Ensure that RoundFruit inherits from YourBaseType (or some other intermediate)

Of course this compiles just fine. It should work too, although I haven't tried it :-) , as long as you don't attempt to query for RoundFruit (which I don't think will work).

When, inevitably, you update your model and regenerate, you'll get a compiler error: "Partial declarations of [Apple] must not specify different base classes".  You'll know what to do.
 
Be nice and leave a comment in your partial class for your unwitting successor and victim of your cleverness.
 
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 19-Apr-2010 at 8:09pm
I happen to think the little "p.s." stratagem above will do the trick.
 
If you really hate it, you might consider using our code generation customization feature. You can intercept our entity class declaration. With your custom code that knows that Apple and Orange inherit from RoundFruit, you replace the default ": MyBaseClass" phrase with ": RoundFruit " ... and you're set.
 
DevForce T4 code generation overrides are extremely powerful. With power comes responsibility. Code wisely.


Edited by WardBell - 19-Apr-2010 at 8:15pm
Back to Top
pk55 View Drop Down
Senior Member
Senior Member


Joined: 22-Jul-2009
Location: CA
Posts: 105
Post Options Post Options   Quote pk55 Quote  Post ReplyReply Direct Link To This Post Posted: 20-Apr-2010 at 11:30am
I'll probably use a multi-step approach for this issue.
 
Just as background, we have one parent base type with six child base types that inherit from it (I don't think we need to go into the business case for this here as I don't want to get into the inheritance-vs-composition discussion again). 
 
There are some entities that did inherit directly from the parent so for those, they'll just pick up the injected base type.  As an aside, I do think the way you've implemented the way you decide if the base type needs to be created is a little kludgy where if you specify an injected base type with a period in it's name (as in "Namespace.BaseType") then you won't attempt to create the type in the generated code (this allows you to have one base type across EDMX models) but if there's no "." in the name, you do create it.  Seems like you should create it only if the POCO class doesn't exist. But that's a minor detail.
 
For those entities that did inherit from a child base type, I'll use your suggestion #1 and edit the generated DevForce code to add the correct ancestory (and obviously change the partial business classes inheritance and mark the base types as serializable by adding the DataContract attributes) as a temporary solution to get the current models back up so others can continue working with them in DevForce 2010.  I've just tried it and it works fine.
 
Then I'm planning on using T4 templates to generate the correct inheritance whenever the model is saved.  I wish the EDM designer allowed for the entry of Structural Annotations directly in the designer rather than having to edit the XML  (http://blogs.msdn.com/efdesign/archive/2008/08/12/structural-annotations-one-pager.aspx) so that I could use those in the T4 code.  I may just try to use the Documentation parts (Summary and Long Description) of the EDMX and document in each Entity the inheritance it should use and then "hopefully" be able to pull those out in the T4 template and use them in to set the class declaration inheritance.
 
Thanks for your replies.
 
Back to Top
pk55 View Drop Down
Senior Member
Senior Member


Joined: 22-Jul-2009
Location: CA
Posts: 105
Post Options Post Options   Quote pk55 Quote  Post ReplyReply Direct Link To This Post Posted: 20-Apr-2010 at 11:44am
Forgot to paste the link about using the Structual Annotations in the T4:
 
 
 
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 20-Apr-2010 at 12:11pm
*** MY RE-WRITE APPROACH WILL NOT WORK ***
 
Mea Culpa for misleading anyone. I thought we didn't support injected types for lack of time. Not so. We tried. The problem lies with the Entity Framework.
 
EF will not let us inject an unmapped class in the middle of a mapped hierarchy.
 
The CSDL inheritance hierarchy is "Apple -> Fruit". The CLR class hierarchy (after my proposed surgery) is "Apple -> RoundFruit -> Fruit".  Entity Framework examines the CLR class hierarchy, doesn't recognize "RoundFruit", screams "mismatch", and refuses to materialize the Apple.
 
Note that EF does not consider super classes of Fruit (e.g., Entity, or YourBaseType).  EF cares only about the mapped portion of the hierarchy. That's why we can support an injected base type ... but not injected intermediate types.
 
How about creating a fictive RoundFruit class in the EDMX? One without any mapped properties.  Sorry but EF refuses to validate the model. We don't think this is just a designer problem.
 
We understand that the EF team is aware of the issue and may support "empty" abstract classes in future. Then you could insert an empty RoundFruit class in your CSDL and extend the generated partial class in code.
 
Until that happy day, you'll have to use ugly workarounds.  I have some in mind ... but will keep them to myself until someone needs them.  
Hint: think "extension methods" , implemented in the same assembly as your custom base class, leveraging an "internal" method of your base class to enable the extension method to access non-public members.
@pk55 may be able to escape the problem because it seems he does not need intermediate base types. It appears that all of his custom, pure-code base types are positioned outside the mapped hierarchy.
 
Continuing my example, it looks like he has entities such as: 
Apple -> Fruit -> PlantBase -> GeneralBase -> Entity
Orange -> Fruit -> PlantBase -> GeneralBase -> Entity
BeefSteak -> Ruminant -> MeatBase -> GeneralBase -> Entity
ChickenSteak -> Poultry -> MeatBase -> GeneralBase -> Entity
Apple, Orange, BeefSteak, ChickenSteak, Fruit, Ruminant, Poultry ... these are all mapped entities. PlantBase and MeatBase are custom, abstract, pure code classes that descend from GeneralBase which derives from our Entity. None of the custom base classes intrude into the mapped hierarchy. Entity Framework will be fine.
 
@pk55 will be still have to either surgically alter the generated classes or customize our code generation ... which approaches I described above.
 
--
 
Regarding the "." kludge ... yes it is. We considered examining the project to see if the named class exists. This seemed vulnerable to timing problems that would lead to tedious support calls. We think the "." convention removes all ambiguity. Reasonable minds will differ.
 
---
 
Yes, it would be nice to be able to annotate through the designer. Oh well.  You will have access to attributes (such as the documentation attributes) during DevForce code generation interception so you could read those to give your logic the inheritance direction it needs; that same generation could also strip those directions from the generated XML documentation if you preferred. It's all up to you.
 
I'd be inclined toward a different approach. I'd probably leverage some class naming convention that indicated the appropriate base class to use and supplement that with a separate configuration file to override the convention as needed.
 
You certainly want to write some tests that confirm you've generated these special entity types as expected.
 
Best of luck!


Edited by WardBell - 20-Apr-2010 at 12:13pm
Back to Top
pk55 View Drop Down
Senior Member
Senior Member


Joined: 22-Jul-2009
Location: CA
Posts: 105
Post Options Post Options   Quote pk55 Quote  Post ReplyReply Direct Link To This Post Posted: 20-Apr-2010 at 12:30pm
You are correct that none of our base types are mapped to an actual data store which is why the method outlined works for us (and worked in the past with DevForce 2009 when you could have multiple Injected Base Types).
Back to Top
pk55 View Drop Down
Senior Member
Senior Member


Joined: 22-Jul-2009
Location: CA
Posts: 105
Post Options Post Options   Quote pk55 Quote  Post ReplyReply Direct Link To This Post Posted: 20-Apr-2010 at 4:52pm
It actually wasn't that hard to modify the T4 template (per the DevForce suggestion of overridding the template methods) to switch the entity base type during each save of the model.  The hardest part was figuring out which method to use.

For others that may try this (and who are as new to T4 as I am), this is what I did:
 
Added a parent base type and a child base type that inherits from the parent base type to my project.  Marked them as serializable.
 
In the EMDX, I set the one singular injected base type to be the parent base type (fully qualified with the Namespace.BaseType so that the code gen doesn't attempt to create the class).
 
For any entity that I wanted to use the child base type, I set the Entity Documentation.Summary property to the child base type name.  Obviously, there are other ways to do this.  I like that it keeps it with the model.  You may not.
 
I opened the Object Browser in VS2010 and added a custom component set (since the DLLs I want aren't in my solution by default) and added the IdeaBlade VS 2010 extension dlls so I could look at what methods were available (I haven't installed Reflector yet for 2010).  For me (running XP SP3), they were in c:\Documents and Settings\<user name>\Local Settings\Application Data\Microsoft\VisualStudio\10.0\Extensions\IdeaBlade, Inc\IdeaBlade OM Designer Extension\6.0.1.0 folder.   Not sure where Vista or Windows 7 puts them.
 
Looking at the IdeaBlade.VisualStudio.OM.CodeGenerator.dll in the DomainModelTemplate, I found a GetEntityClassDef method to override. 
 
Opened the Model1.edmx.tt file in my project and changed it (similar to what the Release notes did to add a custom "Foo" attribute).
 
Here's the code:
 
<#@ template  language="C#" debug="true" hostSpecific="true" #>
<#@ output extension=".ReadMe" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ Assembly Name="Microsoft.VisualStudio.TextTemplating.10.0" #>
<#@ Assembly Name="IdeaBlade.Core" #>
<#@ Assembly Name="IdeaBlade.VisualStudio.DTE.dll" #>
<#@ Assembly Name="IdeaBlade.VisualStudio.OM.CodeGenerator.dll" #>
<#@ Assembly Name="IdeaBlade.EntityModel.Edm.Metadata.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="IdeaBlade.VisualStudio.DTE" #>
<#@ import namespace="IdeaBlade.VisualStudio.OM.CodeGenerator" #>
<#@ import namespace="IdeaBlade.EntityModel.Edm.Metadata" #>
<#
 // Source for this file located at: C:\Documents and Settings\<user name>\Local Settings\Application Data\Microsoft\VisualStudio\10.0\Extensions\IdeaBlade, Inc\IdeaBlade OM Designer Extension\6.0.1.0\DomainModelTemplate.tt
 // Model1.edmx  <--- This is needed so that "Transform Related Text Templates On Save" works correctly.
 var template = new MyTestTemplate(this);
 template.Generate();
#>
<#+
 public class MyTestTemplate : DomainModelTemplate {
  public MyTestTemplate(Microsoft.VisualStudio.TextTemplating.TextTransformation textTransformation)
   :base(textTransformation) {}
    
   protected override ClassDef GetEntityClassDef(EntityOrComplexTypeWrapper wrapper)
   {
    // this method is  called for both an Entity and a Complex Type
    // so we could change the parentage of either
    WriteComment("");
    WriteComment("GetEntityClassDef called for: " + wrapper.Name);
    WriteComment("");
    
    // We're using the model's Documentation.Summary to memorialize the true BaseTypeName to use.
    // Could just as easily use the entity name or complex type name and look up it's parentage in a
    // translation XML file, for example
    var _summary = wrapper.Documentation.Summary as string;
    
    // TODO: validate that Summary is a known non-EF mapped base type but if it's not
    // code generation will just throw errors so it's not gonna be too hard to spot these.
    // for this example, I'm just checking to be sure itsn't null or empty :-)
    if (_summary != null && _summary != String.Empty)
    {
     // generate the current ClassDef from the passed argument
     ClassDef _classDef;
     _classDef = base.GetEntityClassDef(wrapper);
     // create a new class def instance as we're overridding it with a different base type
     // and ClassDef.BaseTypeName has a private Setter so we use the ClassDef public
     // constructor that takes Name, BaseTypeName and AccessType and then set the other properties
     ClassDef _newClassDef = new ClassDef (_classDef.Name, _summary, _classDef.AccessType);
     
     // setup the remaining properties
     _newClassDef.SetAbstract (_classDef.IsAbstract);
     _newClassDef.SetNew (_classDef.IsNew);
     _newClassDef.SetPartial (_classDef.IsPartial);
     _newClassDef.SetStatic (_classDef.IsStatic);
     
     // since we're overriding the base type to be a class, I'm setting this to false
     // as I know it won't be an interface
     _newClassDef.BaseTypeIsInterface = false;
     
     // write a comment that we generated something different (useful for debugging as well)
     WriteComment("");
     WriteComment("Changed BaseTypeName to be: " + _newClassDef.BaseTypeName);
     WriteComment("");
     
     // return the new ClassDef
     return (_newClassDef);
    }
    
    // DEFAULT
    // just generate and return the normal ClassDef so all entities that just inherit directly
    // from the base injected type remain the same
    return (base.GetEntityClassDef(wrapper));
   }
 }
#>
Back to Top
GregD View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 374
Post Options Post Options   Quote GregD Quote  Post ReplyReply Direct Link To This Post Posted: 29-Apr-2010 at 11:59am
Originally posted by WardBell

 
Until that happy day, you'll have to use ugly workarounds.  I have some in mind ... but will keep them to myself until someone needs them.  
Hint: think "extension methods" , implemented in the same assembly as your custom base class, leveraging an "internal" method of your base class to enable the extension method to access non-public members.


It was also suggested to me this morning that one could design an interface, write extension methods for that interface, and then have entities (as desired) implement that interface. This wouldn't provide every capability of an abstract base class, but would certainly give you a way to implement some common behaviors.
Back to Top
orcities View Drop Down
Senior Member
Senior Member
Avatar

Joined: 28-Aug-2007
Location: United States
Posts: 454
Post Options Post Options   Quote orcities Quote  Post ReplyReply Direct Link To This Post Posted: 29-Apr-2010 at 1:57pm
Tried this but got an error and had to start my Model over.
Back to Top
WardBell View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 31-Mar-2009
Location: Emeryville, CA,
Posts: 338
Post Options Post Options   Quote WardBell Quote  Post ReplyReply Direct Link To This Post Posted: 29-Apr-2010 at 5:58pm
@orcities - not sure what you tried (template override? interface?) or what went wrong. Can you be more specific?
 
@pk55 - Thanks for the sample. I'm not sure why you wrote your TestTemplate class directly into the template. You get no IntelliSense or compiler help that way.
 
Is there some reason you didn't write it as a separate class in it's own assembly?
Back to Top
pk55 View Drop Down
Senior Member
Senior Member


Joined: 22-Jul-2009
Location: CA
Posts: 105
Post Options Post Options   Quote pk55 Quote  Post ReplyReply Direct Link To This Post Posted: 03-May-2010 at 8:50am
It's not in a separate assembly because I hadn't gotten around to it :-)   Once I've finished the customizations that we need, I'll do that.  In our real app, I actually have this stuff split into multiple T4 classes and just include them in the main template but for the sample, it was clearer to just do it in-line.
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down