Removing children entries from parent causes error on SaveChanges - asp.net-core

I am getting the following error:
The association between entity types 'Docket' and 'DocketLine' has been severed but the foreign key for this relationship cannot be set to null. If the dependent entity should be deleted, then setup the relationship to use cascade deletes.
The issue comes about because I have a Docket (header) than has multiple children (DocketLines), and I am doing an update where I am adding new Lines to the docket header, and I am just adding those new DocketLines to the Docket.DocketLines collection (which works fine). But when I attempt to remove a DocketLine from the same collection using Docket.DocketLines.Remove(deletedLine), then this generates the error message above. Any idea why?
I had to change my code to remove the Lines directly from the _context.DocketLines.Remove/RemoveRange(...) collection in the end, and this works, but it seems odd that I would add new items to a child collection to insert new DocketLines, but couldn't remove items from that same collection to remove DocketLines.

Inside the DbContext/OnModelCreating method, comment the OnDelete behaviour of the entity that is giving you that problem:
That will allow you to avoid that problem.

Related

Property [participants_count] does not exist but exists and can be dumped

I have 2 tables, evenements and participants, represented by 2 models Evenement and Participant.
Those entities are belongsToMany related, so I have a third table evenement_participant following Laravel's naming conventions, and inside are foreign evenement_id and participant_id columns.
I'm able to retrieve the relationship and I can
dd($evenement->participants)
which gives me a collection of participants.
In my controller, I have this db call:
$evenements = Evenement::withCount(['participants' => function($query) {
$query->where('is_active', 1);
}])
This withCount generates a participants_count attribute for each evenement.
In my blade view, there is a for-each loop on the evenements collection, and somewhere I do this:
$evenement->participants_count
and I face this error:
Property [participants_count] does not exist on this collection
instance.
However, if instead I do the following in the same blade view
#dd($evenement->participants_count)
it dumps me the count.
I dropped all the evenements to keep just one for testing, and I still have the same error.
Sorry, made a typo in a condition inside my blade loop

Intershop EDL modelling - How to add dependency with on cascade delete

We have some custom objects modelled through EDL which have foreign keys to system Intershop objects (ISPRODUCT and ISORDER). We need our objects to get deleted when referenced order or product is deleted.
This is the extract from the EDL file:
/**
* Relation to product PO (tariff item)
*/
dependency tariff: ProductPO
{
foreign key(tariffID);
}
/*
* Order relation
*/
dependency order: OrderPO
{
foreign key(orderID);
}
As I can see, it is possible to add delete actions on EDL relations but it is not possible to add delete actions on dependencies.
What we are doing at the moment is modifying the statements in the generated dbconstraints.oracle.ddl files like this:
EXEC staging_ddl.add_constraint('A1APPLICATIONFORM', 'A1APPLICATIONFORM_CO_003', 'FOREIGN KEY (TARIFFID) REFERENCES PRODUCT (UUID) ON DELETE SET NULL INITIALLY DEFERRED DEFERRABLE DISABLE NOVALIDATE');
EXEC staging_ddl.add_constraint('A1APPLICATIONFORM', 'A1APPLICATIONFORM_CO_004', 'FOREIGN KEY (ORDERID) REFERENCES ISORDER (UUID) ON DELETE CASCADE INITIALLY DEFERRED DEFERRABLE DISABLE NOVALIDATE');
But it is only the temporary workaround because these files will get overwritten each time we restart the code generator on the EDL.
On relationship it is possible to define the on delete action like this:
relation promotionBenefitPOs : A1PromotionBenefitPO[0..n] inverse promotionPO implements promotionBenefits delete default;
Is it possible to achieve the same thing on the dependency with the system objects?
I didn't know that was possible with EDL, good to know. My problem with this approach is that the orm cache does not know that these objects are being removed by oracle, so it might have phantom object floating around in the orm cache.
I would use this register listener solution to remove these objects so that everything is updated and flushed out of the cache.
I do wonder how the code generator deals with this delete property on the relation.
I'm afraid you need to do that by hand. Meaning once an instance of the types involved is removed, you need to query for your custom glue object and remove that one a subsequent action by your own. As dependency is merely a weak (unidirectional) relation that orm cannot automatically remove.
See here for documentation about EDL-dependency: https://support.intershop.com/kb/index.php/Display/247P28
For example, I checked ProcessPagelet-Delete pipline. In there we first unassign (i.e. remove the assignment) Label objects from the Pagelet to be deleted. The PageletLabelAssingmentPO contains a dependency to Pagelet as you can see here:
orm class PageletLabelAssignmentPO extends LabelAssignmentPO
{
attribute pageletUUID : uuid;
dependency pagelet : PageletPO
{
foreign key(pageletUUID);
}
}

ATK4: Handling many to many relationships in CRUD - Adding subCRUD without breaking Add New

I'm trying to gracefully handle extra many to many relationship data in an ATK4 CRUD object. I can manually add the current entry's linked tables as sub CRUD objects while isEditing(), but in doing so i break the 'Add New' button. Code as follows:
function page_projects($p) {
$crud = $this->add('CRUD');
$crud->setModel('Project', null, array('Title', 'School'));
if($crud->grid){
$crud->grid->addPaginator(10);
$crud->grid->addQuicksearch(array('Title','School'));
}
if($crud->isEditing()){
$vForm = $crud->form;
$keywords = $vForm->add('CRUD');
$keywords->entity_name = 'Keyword';
$keywords->setModel(
$crud->model->load($crud->id)->
ref("mProjectKeywords"));
//Other snipped m:m rels
}
}
The issue is simply that $crud->id is not populated (and shouldn't be) when generating a new entry, which means i can't spawn new sub cruds. A workaround is to use:
if($crud->isEditing() && !is_null($crud->id)) {
when checking isEditing(), but this simply stops the sub CRUDS from being instantiated to avoid having load() throw an exception. I've tried looking at loadAny() and tryLoad() and neither do what i want in a many to many context: load a record if one exists, generate a new one otherwise.
Does anyone know a better way of handling this, and if there's not already one in the framework then what's the best angle for attacking this problem?
In this case that's normal behavior because you simply can't create mProjectKeywords record in database table before you INSERT referenced records in parent tables (Project and Keywords).
I do something like if($crud->isEditing() && !is_null($crud->id)) in my projects too.
That means you first have to save (insert) new record in Project and only then sub-CRUD shows up and you can add referenced records in Project edit form (which reloads automatically after you save new project).

Can I delete an entity that is not in cache?

I want to delete a record from the DB that hasn't been retrieved from a breeze query. The entity hasn't been retrieved so it's not in the cache, but I know the KEY of the record from another operation. Here's what I've tried:
create a new entity from the manager:
manager.createEntity(entityNames.book);
setting the ID :
bookToDelete().bookID(1); // bookToDelete is a ko observable from step 1
updating the state:
bookToDelete().entityAspect.setDeleted();
When I save changes, this transaction is not included in the JSON
You almost have it. Calling entityAspect.setDeleted on an 'Added' entity moves it directly to a 'Detached' state, which in effect removes it from the EntityManager, and hence it cannot be saved. This is deliberate. It handles the case where you create an entity and later delete it. In this case, there is no entity to save.
So, in your case, you have to make the entity either 'Modified' or 'Unchanged' before you call entityAspect.setDeleted. You can do this by either calling entityAspect.setUnchanged or entityAspect.setModified before calling entityAspect.setDeleted or you can call entityAspect.acceptChanges.
Note that you will also have to insure that the 'clone' entity passes validation and if you have a concurrency field on the entity, you will need to set this appropriately as well.
UPDATE Dec 7th
You can create the book entity in the marked-for-delete state in a single step as shown:
var book = manager.createEntity(entityNames.book,
{ BookID: 1 }, // use initializer to set the key
breeze.EntityState.Deleted); // creates the entity in the Deleted state
Be sure to initialize it with all other properties that are necessary for the entity to pass validation and optimistic concurrency checks on the server.
No problem if you don't have these checks. Not sure how you'd get those values without querying the server if you did have such checks.
got it. cant delete entity while still in added state. I first setModified. then setdeleted. didnt see any side affects.

JPA & Ebean ORM: Empty collection is not empty

I've started switching over a project from hand-written JDBC ORM code to Ebeans. So far it's been great; Ebeans is light and easy to use.
However, I have run into a crippling issue: when retrieving a one-to-many list which should be empty there is actually one element in it. This element looks to be some kind of proxy object which has all null fields, so it breaks code which loops through the collection.
I've included abbreviated definitions here:
#Entity
class Store {
...
#OneToMany(mappedBy="store",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
List<StoreAlbum> storeAlbums = new LinkedList<StoreAlbum>();
}
#Entity
class StoreAlbum {
...
#ManyToOne(optional=false,fetch=FetchType.EAGER)
#JoinColumn(name="store_id",nullable=false)
Store store;
}
The ... are where all the standard getters and setters are. The retrieval code looks like this:
Store s = server.find(Store.class)
.where()
.eq("store_id",4)
.findUnique();
Assert.assertEquals("Sprint",s.getStoreName());
Assert.assertEquals(0, s.getStoreAlbums().size());
The database is known to contain a 'store' row for "Sprint", and the 'store_album' table does not contain any rows for that store.
The JUnit test fails on the second assertion. It finds a list with 1 element in it, which is some kind of broken StoreAlbum object. The debugger shows the object as being of the type "com.lwm.catalogfeed.domain.StoreAlbum$$EntityBean$test#1a5e68a" with null values for all the fields which are declared as nullable=false (and optional=false).
Am I missing something here?
Thought I'd post an update on this... I ended up giving up on EBeans and instead switched the implementation over to use MyBatis. MyBatis is fantastic; the manual is easy to read and thorough. MyBatis does what you expect it to do. I got it up and running in no time.
EBeans didn't appear to detect that the join for the associated collection resulted in a bunch of null ids, but MyBatis handled this scenario cleanly.
I ran into the same issue and was able to solve it by adding an identity column to the secondary table (StoreAlbum). I did not investigate the cause but I suppose Ebean needs a primary key on the table in these kind of situations.