This thread looks to be a little on the old side and therefore may no longer be relevant. Please see if there is a newer thread on the subject and ensure you're using the most recent build of any software if your question regards a particular product.
This thread has been locked and is no longer accepting new posts, if you have a question regarding this topic please email us at support@mindscape.co.nz
|
Hi, I have some problems setting up LS using inheritance and please let me explain. Here is the setup. 1. My class structures requires deep inheritance with many classes drived from its parent(base) class. 2. I use ConcreteTableInheritance for inheritance and all classes except the "leaf class" are abstract class. (And it seems to work and I'm very glad it did) 3. Now I need to set association to the base class (not between the abstract classes but to an external class to that are outside of inheritance tree. 4. Initially, I thought I could create association to the base class thinking that all of its derived classes will inherit the association. But association to base class is not allowed. I think I understand the reason why. It's because the association name on the target object needs to be unique but having many derived classes means the uniqueness cannot be guaranteed. 5. So I'm forced to create associations to all of the "leaf" classes. Problems. 1. I have many "leaf" classes drived from a common base. It's quite difficult to create association to all of the leaf classes and there are wires all over the places making the editing also difficult. Technically, it's possible not to use inheritance at all but I believe there is reason why one wants to use inheritance. The main reason are to make design clean and code reuse. Like wise, I wish that LS has better support for inheritance. Creating association to base class is pretty fundamental; instead of creating association to all leaf classes, treat associaition to base class as short-cut to create association to all of its leaf classes, implitcitly. (association name on the target classes are autogenerated followed by each leaf class name or something) I know it's possible because all I'm doing is to create association to all of its leaf classes, each by hand and I'm not doing anything special. 2. Until problem #1 is solved, I have to create association to the leaf classes. But there is one more problem. The association property needs to be defined in the base class. (the base class uses the association) However, creating association in the leaf classes means that the association in the leaf class needs to override the association in the base class. I found an association option, "Inheritance", and I can set it to "override" The problem is that setting it to "override" it will generate code with "override" both side of association. I need to override on the derived class and not on the other side of association. On the otherside, there is nothing to override (there is no inheritance on the otherside) , causing compilation error. I think the best solution is to support association to the base class. I tried few other ORM tools and they all work fine except I when I tried inheritance setup. Inheritance is the main reason why I like LS. LS gave me the best support for inheritance and I wish it pushes it further. Thanks a lot for the help. -chris
Initially, I thought I could create association to the base class (and all derived classes inherits the association). But association to base class is not allowed. I think I understand the reason why. It's because the association name on the target object needs to be unique but having many derived classes means the uniqueness is not guaranteed. 2. So, instead of creating |
|
|
You need to use single table inheritance or class table inheritance. These allow polymorphic loading and are therefore suitable for use in associations. (You can't do a polymorphic load when using concrete table inheritance because there's no way of knowing which table the target entity belongs to -- I guess we could issue a separate query for every table in the hierarchy and combine the results, but that is very laborious and not the behaviour most people expect. Plus for to-one associations there would be no single table for the foreign key referred to.) If you need to stick with concrete table inheritance but want to access the leaf class associations through the base class, you can implement a polymorphic accessor via the partial classes e.g. partial class MyBaseEntity { partial class MyDerivedEntity { This avoids the polymorphic load problem because each derived entity has a specific association and a specific foreign key, while providing a uniform way of accessing the associations. Again, if you can say more about the problem you are trying to solve, it may be possible for us to suggest an alternative approach or to implement a suitable enhancement. |
|
|
Hi, Ivan, I tried to explain the problem the best I can. Maybe I'm not good at explaining. ^^ You advice, adding a method in the base class and overriding in the derived class works, but I will take your advice as a temporary workaround. Accessing property directly is possible as I stated in #2 above. It's more like a bug in that when I set "override" inheritance association property in the Property Editor, it creates "override" on both side of associations. If we can choose which side to "override", it will fix the problem. Also, I would like to ask you for some consideration to the problem #1. I have many derived classes and wiring association to derived classes instead of base classes causing some major problems and maintenance(when the common property changes, I have to change them all instead of just one) very difficult. If I were to work this way, I'll probably just not use inheritance at all as it doesn't give me much advantages of using inheritance. Thanks for your help. Cheers! -chris
|
|
|
Regrading my choice of using ConcreteInheritance, I considered both single table inheritance or class table inheritance but I decided I cannot use them in my set up. I have deep hierarchy and many derived classes. Single table inheritance will give me a humongous table and class table inheritance will fragment DB to too many tables. I also need to edit the db by hand using a db editor and ConcreteInhertance seems like the only choice. Thanks. -chris
|
|
|
Thanks for the extra info. It sounds like you're running into the classic object-relational mismatch: relational databases just don't do well at representing deep hierarchies. As you note, STI is a nonstarter and class table inheritance gets fragmentary, so you are left only with concrete table inheritance which at the DB level is no inheritance at all! We are able to map concrete table 'inheritance' for value fields, but because of the foreign key and heterogeneous select problems we can't do it for associations. We're not currently able to split the association 'override' behaviour in the designer because of the risk of breaking existing code, but we agree this would be something we should provide in the future. We will try to incorporate it in 5.0 (we restrict breaking changes to major versions) but obviously that is too far away to help you. Fortunately you may be able to work around this by modifying the templates. If you take a look at RelationshipProperties.vm, line 12, you will see the template for EntityCollection properties: $Translator.TranslateMethodAttributes($relationship.PropertyAttributes) EntityCollection<$Translator.QualifiedTypeName($relationship.To)> $relationship.Name The underlined bit is where the 'override' comes from. You could replace this with 'public' and this would stop us generating the 'override' keyword on all EntityCollections. So now you could set Override on the association in the designer, and it would affect only the to-one end of the association in the generated code. This is quite a crude approach because it globally suppresses 'override' on EntityCollections. If you needed specific control over which ends of which associations were 'override' you could do that by creating an extension property and consulting that in your templates. See http://www.mindscapehq.com/documentation/lightspeed/Working-with-Models-in-the-Visual-Designer/Customising-the-Generated-Code for information about this. Yes, we realise it would be better to have this built into the designer but we don't want to break existing code. Something that occurred to me about your scenario is that given that you don't seem concerned about the heterogeneous table problem it might be that the entities you're concerned about are reference data and you are therefore not interested in the collection properties. If so you can suppress the collection properties altogether by leaving the name blank. You still need separate associations from each derived class but this might help avoid having to muck around with templates to solve the 'override' problem. See http://www.mindscapehq.com/documentation/lightspeed/Domain-Modelling-Techniques/Reference-Data-and-Lookups if this would help with your scenario. |
|
|
Hi, Ivan, Thanks for your detailed answer. I think we are on the same page what I'm trying to do. ^^ I've had many problems and spend quite a bit of time trying to setup inheritance with other ORM tools and they are not even close getting it to work. I was surprised why users didn't raised issues with inheritance. I believe it's quite common problems and one of the important purpose ORM is to resolve the mismatch. LS, on the other hands, just works! So I know they have some problems. Although I have some issues, I can work around it but this is quite fundamental to my needs. To me, being able to create association to base class is an important factor whether to use inheritance or not. If I can't, I might just not use inheritance at all, because, as I said earlier, there is very little benefits and I've had too much headaches already.
So, please let me ask you a question. Is creating association to base class is impossible by design? I understand about the heterogeneious problem. But what if I don't care about the association on the other side, i.e., association name can be autogenerated or not generated at all. If heterogeneous problem can be resolved this way, do you think it's possible to create association to the base class? If so, I think I rather wait until it get supported rather than working around the problem. If it's not possible, or require huge undertaking, I'll rethink about inheritance. I really wish you would positively consider about the problem
Thank you very much for your suppprt. Cheers! -chris
|
|
|
If you are talking about one-way associations then it might well be possible to have an association in the base class. In the database, each table would contain a FK to the reference data, which is a single table, so there's no integrity issue. And because you can't traverse back over the collection, the heterogeneous collection issue doesn't arise. So conceptually this seems like it should be okay. Would that meet your requirement, i.e. a base class could have a to-one association to a reference data entity, but with no reverse association? If so, let me know and I'll see what the implementation issues are with this. I can't make you any promises, though. |
|
|
Sure, it will be jsut fine. The association in the base class is the important one. If I need an reference the other way, I can find them though a query. I have many associtions to the base classes and it getting out of hands quickly. I wish it won't be long before it get's supported.
Thanks a lot for your help. Cheers! -chris |
|
|
Hi Chris, This will be in the next nightly build. Note that it will only be permitted for one-way associations, i.e. one-to-many associations where the CollectionName is empty. In addition, one-way associations currently require the target entity (the reference data) to be cached, so be sure to set the Cached option on the target entity class. In fact, you can have this feature today! Although the designer will give you an error on the association, the generated code will actually work all the same. Again, remember to delete the CollectionName and mark the reference data as Cached. One caveat: we haven't implemented designer-database sync for this yet, so if you're using that, you'll need to exclude some spurious updates each time you sync. We'll try to tackle this at some point, but it will take a bit longer. |
|
|
Hi, Ivan, It's really GREAT! I'll test the first thing tomorrow. Thanks a lot for your help.
-chris |
|
|
Hi, Ivan, I just got the nightly build, Oct 11, and tested. I removed "Collection Name" and made the target model, "Cached" but I have few problems. 1. Generating source code says... Error 1 Base classes with associations must use discriminated inheritance. Entity Actor has associations but has derived types using concrete table inheritance. C:\UMUDClient\CommonLib\DB\UMud.lsmodel 0 1 CommonLib 2. And "Update Database" didn't succeed. The error message box reads, " The following updates could not be carried out: Conversation: Add column AutospeechID and associated foriegn key to table Acto - Table "umud4.actor" doesn't exist"
Actor, is the abstract base class here, and of course, it doesn't exist in DB.
3. Despited of the errors above, it compiled fine, however, I have runtime error... > Mindscape.LightSpeed.dll!Mindscape.LightSpeed.Data.CommandRunner.ExecuteReader.AnonymousMethod__3() Line 22 + 0x33 bytes C# Npc is derived class from Actor class. I appreciate for your help.
Cheers! -chris |
|
|
Hi Chris, 1. New nightly builds go up at around 1200 UTC. You were a little too keen! You need the 12 Oct nightly build, which is available now. 2. Yes, I noted this as a known issue in my previous post and said that we would be addressing this at some point, but I'm not sure when. 3. Could you provide us with the exception *message* as well as the stack trace? The stack trace tells us that something went wrong when we tried to execute the database command, but not what. Also in case we need to investigate further could you provide us with a minimal project (console application or NUnit test, just the minimal set of entities required to exhibit the error) so we can figure out what you and we are doing differently? (You can attach a zip file via the Options tab.) Thanks! |
|
|
Hi, Ivan, I just got Oct 12 build and problem #1 away. Sorry about not giving you the exception error itself. Here it is. {"Unable to materialize field [AutospeechId] on type [DB.UMUD.Npc]: field is type 'System.Int32' but database returned type 'System.DBNull'. Check your table has an Id column and that your mappings are correct. See inner exception for details."} My class structure is like the following. Actor <- Npc (Actor is abstract base class and Npc is leaf class) Conversation has one-to-many association to Actor, i.e., Actor has AutospeechId foriegn key to Conversation. The error occured when trying to load Npc. I hope it helps to figure out.
Thanks -chris |
|
|
(Clarification) "Actor has AutospeechId foriegn key to Conversation" Since there is no real Actor table, and "Update Database" cannot update DB correctly, I created AutospeechId Foriegn key in Npc table.
|
|
|
When you manually created AutospeechId in the database, you made it nullable, and some entries have null values. But in your LightSpeed model the association is non-nullable. So when LightSpeed tries to materalise the association, it goes "wait, I have a DBNull coming from the database, but I have to materialise it into a non-nullable field -- error!" Make AutospeechId non-nullable in the database and you should be good to go. |
|
|
Awesome!!! it's works!!! Thanks a lot for your help.
Right now, I understand that it supports one-way association only as we talked about. Thinking little more since Yesterday, I find that two-way association is also important. Sorry about saying I just need one-way association. I already found a case that I need two-way association. So please let me ask you this. How hard is to support two-way association? Sorry about pressuring you but could auto-generating name to resolve heterogeneous problem could help solve the problem?
Again, I appreciate for your help.
Cheers! -chris |
|
|
Prohibitively hard. Autogenerating a collection name is nothing to do with the problem. All the collection name does is give you a way to refer to the collection in your code: it has no impact whatsoever on the way LightSpeed performs the query. The problem comes from the SQL we would need to generate to traverse the to-many association. Consider a base class Person, with derived classes Employee and Customer. We are using concrete table inheritance so Employee and Customer are separate tables. Now suppose Person has an association to City. This is represented by CityId fields in the Employee and Customer tables. So far so good, and with the latest build, we can handle this. But now consider if the association is two way, i.e. City has a People collection. How do we populate the People collection? We can't query the Person table, because there is no Person table. Instead, we need to query both the Employee table and the Customer table -- and all the other tables for Person-derived types. What is currently one query becomes multiple queries to different tables. It's not impossible. But it's a major piece of work. And right now we don't plan to do it. The solution is to work around it in your City class with a custom method: partial class City { |
|
|
I see.. what I meant is little different and please let me explain. Without the association to the base class, I have to create associations, City <-> Employee and City <-> Customer. City will have Employee and Customer collection. This is how I'll set up without the capability to create association to base class. Suppose we can create association to base class, City <-> Person. Now, as you said, City cannot have Person collection. And here is what I had in mind. Having association between City and base class, Person means that City will find all of the derived class of Person and *implicitly* create collections for the leaf classes. In otherwords, City will have Customer and Employee collections automatically. (or choose not to create collections at all ) If there is collision in collection names, it will autogenerate to resolve. I hope the intention is clear.
Cheers! -chris
|
|
|
Thanks, that's an interesting idea and we'll definitely take it on board. However it's not something we can do as a quick fix I'm afraid. It's not just a matter of having the designer generate multiple collection properties instead of just one: we'd need to do some work in the runtime on the resolution of reverse associations (since the reverse association would no longer be in the target class but instead in an undiscriminated base class). So for two-way associations you will still need to create associations to each derived class, and use either override or an interface to gain polymorphic access to the association property. |
|
|
Hi, Ivan, Thanks for the consideration though. I'm happy that it works even though it's only for the one-way association, and for the completeness, I hope it doesn't just stop there.
Cheers! -chris |
|