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
|
I have been struggling all day to get a 1:1 relationship between 2 'sibling' tables to be automatically handled by Lightspeed. I am evaluating it for purchase but I have been stuck on the very first thing I tried to do. Here is the scenario: 1) There are two tables - A and B. Each has a string SOMEKEY as its primary key (this is Oracle) 2) The primary key SOMEKEY is defined identically for both tables 3) There is a foreign key from B to A. Hence, A must exist. B does not have to exist except a business rule mandates that the sibling B for A always be created. It holds 'extra' customisable attributes to extend the default attributes of A. 4) Dropping A on the design surface generates a model. 5) I need a key I generate so I have changed the entity to accept a new primary key as a string in the constructor. 6) Dropping B on the design surface creates a many-to-one relationship between B and A. I change this to one-to-one with the properties dialog. As near as I can tell all this does is change the name of the association thingmy from Bs to B (i.e. singular) 7) An 'association' is generated in B for A as well as in A for B. On one end the name is As, on the other the name is SOMEKEY ( I am simplifying. There are _ fields and what not). This is the bewildering part. 8) Since I am declaring the new primary key string myself and passing it to the new entity, I have deleted what appears to be auto-generated SomeKeyID from A AND from B. I initialise the key on both at the same time BEFORE attaching them to the context. The values look correct in debug mode. The key is there as the Id in the context for both. 9) The words 'Foreign Key' never appear anywhere in the model. i think that B should have SOMEKEY explicitly defined as a foreign key. Unfortunately the express evaluation version does not seem to permit the use of a tool to define the links between A and B as a FK relationship. Maybe I need to buy LS first.... 10) Call to SaveChanges.. A appears to be correct in the SQL that is created but B has extra field SOMEKEY and Oracle blows large chunks. 11) Can you please post a short working example that works for a one-to-one like this? |
|
|
One other note: A separate thread provided a solution for user-defined keys called an 'ExtendedEntity' which subsumes a bunch of suggestions about natural key handling into an abstract base class. It seems to work fine both for single string natural keys as well as user-defined composite keys. All hail to that contributor. The class is used in step (5) of the above. It looks like this:
} This is nice, but EVERY time the design is saved from the Lightspeed surface, it obliterates the use of this class and sets all entities back to the default. It would be very helpful if it did NOT do this. With 3 or 4 entities it is a trivial matter to fix them up, but if I implement this software I will have dozens of entities many of which are legacy and CANNOT BE Id-ified. |
|
|
Are you able to post some more details about the error that is occurring when you call SaveChanges? In terms of persisting the base class, you can do this by adding a new External Class Reference via the LightSpeed Model tool pane which specifies the details of the ExtendedEntity class, and then you can specify the ExtendedEntity as the base class for your entities by setting the Base Class property on the entity properties.
|
|
|
Thank you for the reply and thank you for your assistance in this matter. I am sure it is something wondrously bone-headed that I am doing. I welcome your constructive criticism. ENTITY MODELSThe first bits are the entity models. Table Bridge is 'A' and table Userbrdg is 'B' (the sibling/child) in the above discussion. These instantiate properly as far as I can tell by inspecting the properties during the debug session and what not. Note the reference to ExtendedEntity which permits supplying the string natural key on instantiation. There are partials providing constructors in support of the ExtendedEntity base class for each entity as shown. Please be careful to note that the field BridgeId on the entity Bridge is an alternate key column on table BRIDGE named BRIDGE_ID. It is not coming from LS nor should LS do anything with it. It is simply an attribute that serves as an alternate key for the table in the database. It is my problem to ensure it is quantified and to check for uniqueness in the target. The primary key field name for both tables in BRKEY in the database as noted below. This field is defined as VARCHAR2(15) in Oracle. So the basic sequence is that the entities new() up fine, I can quantify the fields, and add them to the unit of work, but when I execute SaveChanges I get either of two exceptions:
I have attached the entire ClassLibrary project all zipped up as of right now for your inspection. I can't see any point to sending you the consuming code beyond the fragments included below but I can do so if you need the consuming class in its entirety. I have also attached the CREATE TABLE statements from Oracle from the database which will make dummy tables for you if you want them. You will have to set the right Oracle tablespace in the create script.
The problem association-related field is created by LS hereThis is what gets generated by LS for the association. If I leave this uncommented, I get an extra unknown field generated in my SQL. When I uncomment it, I get the circular association exception shown at the end. I have tried setting it to [Transient] to no avail
Relevant consuming code
Adding the Bridge entity SUCCEEDS... ... ...
... ... Creating the Userbrdg sibling record succeeds... ... ... // sibling (1) one-to-one relationship var newusrbr = new LBISLsDataAccess.Userbrdg(theNewBrkey); ... ... ...
... ... ... Adding the Userbrdg entity SUCCEEDS
SaveChanges then fails
Exception is Raised
|
|
|
Actually my summary is not quite right. The above code actually fails when I comment out the bridgeID LS field related to the association. It tries to Add(...) the Bridge entity to the context and fails with the circular association exception. I only get to the SaveChanges failure if I do not try to comment out the association field bridgeId that is generating the extra column. In that case it gets through all the stuff but there is no love at the end. |
|
|
BTW: The External Class References works as indicated. Thank you. |
|
|
This is the duplicate column name version of the problem:Here is the output when I drop the association and recreate it from the Model Designer. I have attached the model and generated cs for this case. It is so close to working but generates this bogus extra column.
2015-10-28 09:57:34,229 [15] DEBUG LOGGER [(null)] - New BRIDGE.BRKEY = 123456789012345 2015-10-28 09:57:38,760 [15] DEBUG LOGGER [(null)] - New USERBRDG.BRKEY = 123456789012345 2015-10-28 09:57:38,761 [15] DEBUG LOGGER [(null)] - New USERBRDG.ID = 123456789012345 2015-10-28 09:57:38,761 [15] DEBUG LOGGER [(null)] - Attempting to save new bridge 123456789012345... 2015-10-28 09:57:54,933 [15] DEBUG LOGGER [(null)] - BEGIN INSERT INTO Bridge ( BRKEY, Adminarea, Altirload, Altirmeth, Altorload, Altormeth, Appspans, BBBRDGEID, BBPCT, BRIDGEID, BRIDGELIFECYCLEPHASE, BRIDGESTATUS, Bridgegroup, Btrigger, Bridgemed, County, Createdatetime, Createuserkey, Crewhrs, Custodian, DECKAREA, Deckwidth, DEFOPRAT, Designappr, Designload, Designmain, District, Dkmembtype, Dkprotect, Dkstructyp, Dksurftype, Docrefkey, Facility, FCDETAIL, Featint, FHWAREGN, FIPSSTATE, Flaggerhrs, Hclrult, Hclrurt, Helperhrs, Histsign, Impact, Implen, Irfactor, Irload, Irtype, Latitude, Length, Lftbrnavcl, Lftcurbsw, Location, Longitude, Mainspans, Materialappr, Materialmain, Maxspan, Modtime, NFHWAREG, Navcntrol, Navhc, Navvc, Nbiimpcost, Nbirwcost, Nbislen, Nbitotcost, Nbiyrcost, Nextinspid, Notes, Nstatecode, ONOFFSYS, Orfactor, Orload, Ortype, Otherload, Owner, Paralstruc, Placecode, Posting, PRECISELAT, PRECISELON, Propwork, RATERINI, Ratingdate, Refhuc, Refvuc, REQOPRAT, Rtcurbsw, Servtypon, Servtypund, Skew, Snooperhrs, Spcrewhrs, Spequiphrs, Srstatus, Strflared, Strucname, STRUCTNUM, Sumlanes, Tempstruc, TOT_LENGTH, Traceflag, Truck1Ir, Truck1Or, Truck2Ir, Truck2Or, Truck3Ir, Truck3Or, Userkey, Userkey1, Userkey10, Userkey11, Userkey12, Userkey13, Userkey14, Userkey15, Userkey2, Userkey3, Userkey4, Userkey5, Userkey6, Userkey7, Userkey8, Userkey9, Vclrover, Vclrunder, Workby, Yearbuilt, Yearrecon ) VALUES ( '123456789012345', '', NULL, NULL, NULL, NULL, 0, NULL, NULL, '123456789012345', NULL, NULL, '', NULL, NULL, '', '10/28/2015 09:57:13', '5568', NULL, '', NULL, NULL, NULL, '', NULL, '', '', NULL, NULL, NULL, NULL, '4d89fe65-17db-419b-86f2-a338acb9be90', '', NULL, '', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, '', '', NULL, '10/28/2015 09:57:13', NULL, NULL, NULL, NULL, NULL, NULL, 'Y', NULL, NULL, NULL, 'Starter BRIDGE record created on 10/28/2015 - ', NULL, 'Y', NULL, NULL, NULL, NULL, '', NULL, '', NULL, 39.050934, -95.678382, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '123456789012345', '123456789012345', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '5568', '', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); INSERT INTO Userbrdg ( BRKEY, ATTACHDESC1, ATTACHDESC2, ATTACHDESC3, ATTACHTYPE1, ATTACHTYPE2, ATTACHTYPE3, AVGHI, BOXHEIGHT_CULV, BRKEY,BRIDGEMEDKDOT, Createdatetime, Createuserkey, CULVFILLDEPTH, CULVWINGTYPE, CUSTODIANKDOT, DESIGNREFPOST, DESIGNLOADKDOT, DESIGNLOADTYPE, DRAINAGEAREA, ENVNOTATION1, ENVNOTATION2, FUNCTIONTYPE, GRAILAPRLT, GRAILAPRRT, GRAILENDTREAT, GRAILEXITLT, GRAILEXITRT, GRAILTYPE, HORIZUNDRSIGN, IRLOADADJ3, IRLOADADJ33, IRLOADADJ3S2, IRLOADADJH, IRLOADADJT130, IRLOADADJT170, IRLOADLFD3, IRLOADLFD33, IRLOADLFD3S2, IRLOADLFDH, IRLOADLFDHS, IRLOADLFDT130, IRLOADLFDT170, IRLOADWSD3, IRLOADWSD33, IRLOADWSD3S2, IRLOADWSDH, IRLOADWSDHS, IRLOADWSDT130, IRLOADWSDT170, KDOTLATITUDE, KDOTLONGITUDE, LASTPAINTSUPE, MAINTAREA, MEDIANWIDTH, Modtime, MULTIDRAINAGE, Notes, Orientation, ORLOADADJ3, ORLOADADJ33, ORLOADADJ3S2, ORLOADADJH, ORLOADADJT130, ORLOADADJT170, ORLOADLFD3, ORLOADLFD33, ORLOADLFD3S2, ORLOADLFDH, ORLOADLFDHS, ORLOADLFDT130, ORLOADLFDT170, ORLOADWSD3, ORLOADWSD33, ORLOADWSD3S2, ORLOADWSDH, ORLOADWSDHS, ORLOADWSDT130, ORLOADWSDT170, OWNERKDOT, PAINTSURFAREA, POSTEDLOADA, POSTEDLOADB, POSTEDLOADC, POSTEDSIGNTYPE, RATINGADJ, RATINGCOMMENT, RESERVOIRADJ, RESTRICTLOAD, ROADTYPESIGN, ROTANGLEDEG, ROTANGLEMIN, ROTDIRECTION, SIGNTYPEQ1, SIGNTYPEQ2, SIGNTYPEQ3, SIGNTYPEQ4, SKEWDIRECTION, SKEWMINUTES, STREAMSIGN, SUPERPAINTSYS, SUPRSTRUCTTOS, Userkey, VERTCLRSIGN, VERTUNDRSIGN, Wateropen ) VALUES ( '123456789012345', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, '10/28/2015 09:57:13', '5568', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 39.050934, -95.678382, NULL, NULL, NULL, '10/28/2015 09:57:13', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, '5568', NULL, NULL, NULL ); END; --> Parameter Direction DbType Value
--> --------- --------- ---------- -----
--> :p0 Input AnsiString 123456789012345
--> :p1 Input AnsiString --> Time: 0 ms 2015-10-28 09:57:55,147 [15] DEBUG LOGGER [(null)] - at System.Data.OracleClient.OracleConnection.CheckError(OciErrorHandle errorHandle, Int32 rc)
at System.Data.OracleClient.OracleCommand.Execute(OciStatementHandle statementHandle, CommandBehavior behavior, Boolean needRowid, OciRowidDescriptor& rowidDescriptor, ArrayList& resultParameterOrdinals)
at System.Data.OracleClient.OracleCommand.ExecuteNonQueryInternal(Boolean needRowid, OciRowidDescriptor& rowidDescriptor)
at System.Data.OracleClient.OracleCommand.ExecuteNonQuery()
at ...?()
at Mindscape.LightSpeed.Profiling.Interceptor.ExecuteCommand(Func 2015-10-28 09:57:55,158 [15] DEBUG LOGGER [(null)] - System.Data.OracleClient.OracleException (0x80131938): ORA-06550: line 270, column 3: PL/SQL: ORA-00957: duplicate column name ORA-06550: line 268, column 1: PL/SQL: SQL Statement ignored at System.Data.OracleClient.OracleConnection.CheckError(OciErrorHandle errorHandle, Int32 rc)
at System.Data.OracleClient.OracleCommand.Execute(OciStatementHandle statementHandle, CommandBehavior behavior, Boolean needRowid, OciRowidDescriptor& rowidDescriptor, ArrayList& resultParameterOrdinals)
at System.Data.OracleClient.OracleCommand.ExecuteNonQueryInternal(Boolean needRowid, OciRowidDescriptor& rowidDescriptor)
at System.Data.OracleClient.OracleCommand.ExecuteNonQuery()
at ...?()
at Mindscape.LightSpeed.Profiling.Interceptor.ExecuteCommand(Func |
|
|
I am sure I have gone over-the-top. You did want some more details though.... :D |
|
|
I may have to start a new thread because a Many-to-one related table with natural keys in a composite Id is also getting a bogus column generated that makes the insert fail. All looks good but the relationship forces. the insert to include the relationship identifier... Fail. So for the time being and in order to make some progress I have just removed all associations and done all the inserts as separate transactions, with a fallback to delete the entire hierarchy if any part fails to save properly (i.e. hand enforcing referential integrity which is making the value of Lightspeed questionable) |
|
|
Thanks for the extra detail. The circular reference exception will be being thrown because we expect to find a backing field for the associated FK on one side of a one-one relationship, without this column here it sees this as two separate relationships which in turn causes the circular reference exception to be raised. So the field has to be there and should be mapped against the relevant database column which holds this value. I am expecting the duplicate column issue is also coming from this being present as well because I am guessing you may already have that column surfaced against an existing entity property - if that is the case you will want to remove that property. Which column would hold this mapping?
|
|
|
The field is named BRKEY in the parent and the child. I am getting an extra BRKEY in the child. I do not think I have an entity property BRKEY yet because the first thing I did was remove any attempt to make this an attribute on the entity when I ran into this problem. So what I would like to do is: 1) Have the SQL NOT include this extra BRKEY. Similarly if I had a 3 or 4 or 5 part composite key, not generate all those (when I get there....) 2) Be able to refer to the BRKEY on the entity in a simple way like BRIDGE.BRKEY instead of Bridge.Id.Brkey although I understand that is often necessary in Linq etc. I can live with that. Apparently this problem also is affecting many-to-one relationships as I have just found out. |
|
|
Your model has BRKEY defined on Userbrdg as both the Id column name and the FK column name for the associated Bridge, so this is why the column is being included twice in the generated SQL. Having the column mapped in this way is not a valid configuration. What is the column that is storing the FK in the database?
|
|
|
Bridge is the parent. Userbrdg is the parallel table that has no meaning if a parent record does not exist. Both have BRKEY as the PK. Userbrdg has a FK to bridge through BRKEY even though it is the PK for Userbrdg. That is the dilemma. Same problem obtains for N:1 tables. |
|
|
Right that make sense, unfortunately this type of mapping is not supported by LightSpeed. An alternative approach you could look at is to not have an strict association defined between the two entities and manually implement a property to fetch the associated Userbrdg record when required based on the Id of the parent Bridge record. e.g. Have a property defined on Bridge called Userbrdg:
|
|
|
Why are you still awake? It is late here and way too early there. So this embedded query gives me the related Userbrdg and I just nuke it if I need to in order to maintain Ref Int... ditto for other tables that are in N:1 relationships. This is of course a legacy database. Maybe I can STEAL A COLUMN to use for this but it is probably easier to just keep it tidy by hand..... So, nutshell, parallel tables with natural keys in a 1:1 relationship are not supported. Ditto for 1:N tables related through a natural key. A wee bit disappointing. In both cases an extra duplicate field is dropped into the SQL. This is only for INSERTs. Who knows what happens with UPDATEs.... Not really good news I am afraid. Means that I have to hand tend all the RI. Given that, is there a way to make sure that all transactions fire in the desired order and that they are all within the scope of a single transaction. Why does this matter? Because the unit of work does what it does in whatever order it sees fit, apparently, which has led to the dependent row(s) being inserted before the parent, which is not possible. Here is the real hierarchy of which I only shared the first association.
and each of the 1:N tables has a sibling which is the customisable table users can change to add columns and so on, like this:
All 8 of these have the field BRKEY in the primary key. Not to put too fine a point on it, but I have N United States states that might be interested in what I am working on and they are all using this same database design, since the mid-nineties. Granted, time for an update... Maybe STEAL A COLUMN is the way to go. :( |
|
|
This property can then be used to perform updates and deletes on the child etc.? How about inserting if it cannot find it? Would you be able to offer a short example of usages? Thanks |
|
|
The example I gave only caters for selection of the child, you would need to handle inserts and updates separate to this.
|
|
|
Yes I gathered that. Thank you. I do not need to do this very often so hand-weeding is acceptable. Thank you |
|