Is there anyway to delete an object based on some conditions? For example, if cascade:delete-orphan is indicated in a relationship, it will delete the child when the parent is deleted. What I am looking for is to delete an object when some condition is satisfied.
perhaps you're looking for query.delete() ?
session.query(MyClass).filter_by(some_col='fred').delete(synchronize_session=False)
Related
I know this is an odd question because I've always been taught to use a foreign key constraint. However, I've come across a case where a foreign key reference value must be kept for historic purpose when the reference is deleted.
It is a task management system whereby a task occurrence references a parent task containing the recurrence rule. This parent task can be deleted, but the occurrence itself must remain in tact with the non-existing parent id. If the parent task cannot be found, the system simply returns an error - eg "parent task no longer exist." The reason why the parent id cannot be set to null on cascade is because it is being used elsewhere in the occurrence as an identifying key.
Another example: What about a YouTube video that was removed from a playlist. Similar situation right? It is being referenced in the playlist, but the video doesn't exist, so it returns an error in the playlist instead.
Do I simply not define a foreign key at all and just simply create the parent_id reference column as a normal column? I just want to be sure how this is normally handled when one encounters a case where one table references another, but the former is not constraint by the existence of the latter.
Having a constraint is just a technical helper to enforce the semantics defined for the database, i.e. "this column contains a number that is not only an INTEGER(32) but also an identifier for a record in some other table". As such they're not strictly necessary, but it:
makes the intention of the field clear (self documentation)
keeps your data "clean" by preventing incorrect data from being inserted
gives the database engine a hint concerning the content of the table which may allow the db to perform more efficiently.
That said, the "proper" way to accomplish what you've described would be not to physically delete the parent record in the first place. Instead, mark the parent as deleted. Since you're keeping the record for historical purposes, surely you'll want to be able to know what the parent used to be, even if it's no longer active or valid.
Second option would be to create a dummy "parent record deleted" reference. Whenever you delete a parent, you update remaining references to point to the dummy record instead. At least you wouldn't rely on errors to implement expected and valid behaviour.
Finally, I see no reason you shouldn't be able to set the foreign key to NULL. It sounds like you're using the foreign key as part of the primary key of the record in question ("is being used .. as an identifying key"). This you almost certainly should not be doing, if that's the root cause of the problem, start by changing that.
Do I simply not define a foreign key at all and just simply create the
parent_id reference column as a normal column?
Yes. At least this is the way I got to know and how we handle this stuff at work.
You might then want to set an index on the reference column.
I have 2 tables with a master-detail relationship. We use a DataGridView to handle the detail records. The detail records have fields with lookup values (foreign keys). Instead of setting the ID value of the lookup tables we set the object value. This all works well, instead of one situation. If we add a detail row and set at least one of the lookup fields and remove this row before saving (submitchanges), we receive this error:
An attempt was made to remove a relationship between a TableX and a TableY. However, one of the relationship's foreign keys (TableY.TableXID) cannot be set to null.
This problem was discussed in this question (scroll down to answer of Neil Barnwell), but it doesn't have an answer for this particular problem. The solution "Call Datacontext.GetChanges" doesn't help as Datacontext doesnt expose a method GetChanges and GetChangedset fails with same error.
I assume there is some kind of mistake when removing the row. When you add the detail row to the master row, it is automatically attached to the context. So if you add with:
myMasterRow.DetailsRows.Add(myDetails);
You have to use DeleteOnSubmit to reverse the adding:
context.DetailsRows.DeleteOnSubmit(myDetails);
The data context is smart enough to treat the deletion as "undo add", so the entry will never hit the database.
It seems that if you set an object of the entity and remove the new record before any submit Linq still thinks you want to add the new record. In this case you have to set all objects that have FK references to null.
See this post which describes the problem and solution in detail: http://social.msdn.microsoft.com/Forums/en/linqtosql/thread/4170c7bb-f727-4e6a-9190-fc268ae4ce4b
I am trying to delete an object that sometimes has a many-to-many relation. The code I use now is:
db.DeleteObject(registeredDevice);
db.SaveChanges();
This ofcourse just removes the registeredDevice. But usually this device has a many-to-many relation to a project in the database. When trying to delete the device in that scenario it will give an error.
The DELETE statement conflicted with the REFERENCE constraint
What I need to do is remove the device and its relation (the entry in the many-to-many table, not the project it is related to). How do In do this with LINQ ?
To remove everything, you need to make sure all the data is loaded. Basically, linq to sql has to know about the data it is deleting before hand to make the best judgement on how to delete it.
Hoep that helps
I have a many-to-many relation in 3 tables: ProgramUserGroup and Feature are the two main tables, and the link between them is LinkFeatureWithProgramUserGroup, where I have Foreign key relations to the two parent tables.
I have a dataset with inserts: I want to add a new row to ProgramUserGroup, and a related (existing) Feature to the LinkFeatureWithProgramUserGroup table.
When Inserting new rows, I'm setting the default id to -1:
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"> <DataSetUserGroup xmlns="http://tempuri.org/DataSetUserGroup.xsd">
<ProgramUserGroup diffgr:id="ProgramUserGroup1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<id>-1</id>
<Name>99999999999</Name>
<Active>false</Active>
</ProgramUserGroup>
<LinkFeatureWithProgramUserGroup diffgr:id="LinkFeatureWithProgramUserGroup1" msdata:rowOrder="0" diffgr:hasChanges="inserted">
<id>-1</id>
<Feature_id>7</Feature_id>
<ProgramUserGroup_id>-1</ProgramUserGroup_id>
</LinkFeatureWithProgramUserGroup> </DataSetUserGroup> </diffgr:diffgram>
while I'm updating the tables, I get an error:
"The INSERT statement conflicted with the FOREIGN KEY constraint "FK-LinkFeatu-Progr-7DCDAAA2". The conflict occurred in database "x", table "dbo.ProgramUserGroup", column 'id'."
The code for the update is the following:
DataSetUserGroupTableAdapters.LinkFeatureWithProgramUserGroupTableAdapter lfa = new LinkFeatureWithProgramUserGroupTableAdapter();
DataSetUserGroupTableAdapters.ProgramUserGroupTableAdapter pug = new ProgramUserGroupTableAdapter();
pug.Update(dsUserGroup.ProgramUserGroup);
lfa.Update(dsUserGroup.LinkFeatureWithProgramUserGroup);
if I check the ProgramUserGroup table's new row's ID, it has been updated from -1 to ##identity (like 1099), so it's okay - it inserts the new row.
But In the LinkFeatureWithProgramUserGroup table, the related ProgramUserGroup.ID value is still -1, it was not updated anyhow.
How could I force the update of the link table's keys as well?
I've tried
pug.Update(dsUserGroup.ProgramUserGroup);
dsUserGroup.Merge(dsUserGroup.ProgramUserGroup);
lfa.Update(dsUserGroup.LinkFeatureWithProgramUserGroup);
But didn't solve the problem :(
Thanks,
b.
Yes, there's a work-around for this.
You need to tell your parent table's table-adapter to refresh the
data-table after update operation.
This is how you can do that.
Open the properties of ProgramUserGroupTableAdapter -> Default Select Query -> Advnaced options. and Check the option of Refresh the data table. Save the adapter now. Now when you call update on table-adapter, the data-table will be updated [refreshed] after the update operation and will reflect the latest values from database table. if the primary-key or any coloumn is set to auto-increment, the data-table will have those latest value post recent update.
Now you can Call the update as pug.Update(dsUserGroup.ProgramUserGroup);
Read latest values from the ProgramUserGroup coloumns and assign respective values into the child table before update. This will work exactly the way you want.
alt text http://ruchitsurati.net/files/tds1.png
I have a simple parent-child situation where the parent can have multiple children. The user can update the list of children at will by selecting or deselecting them from a listbox. I try updating the child list using code like below but I get a SqlException:
Violation of PRIMARY KEY constraint
'PK_Child_1'. Cannot insert duplicate
key in object 'dbo.Child'.
It appears that LINQ is inserting the new children before deleting the existing ones. I'm sure there is a simple pattern for handling this but I'm stumped.
context.DeleteAllOnSubmit(parent.Children);
foreach (string childname in listBox1.SelectedItems) {
parent.Children.Add(new Child(parentkey, childname));
}
context.SubmitChanges();
Each parent has a unique key (a GUID) and all parent and child columns are non-nullable. The child table is a simple two-column table with the parent key and a varchar value, with a compound primary key consisting of both columns.
Thanks
The problem is that you're adding the new items before the old ones have been deleted.
Linq-to-SQL has a clearly defined order of operations on a SubmitChanges() call (you should definitely also use SubmitChanges() - not Save() !):
* Inserts
* Updates
* Deletes
(for reference, see here and here)
The trouble here is - you seem to add back child nodes that have the same keys as the ones you delete away in the DeleteAllOnSubmit() call.
But because the INSERT of the new items comes before the deletion, you end up with a conflict.
What you need to do is one of two things:
either you delete the child nodes and then call SubmitChanges() before adding the new items again
OR:
you change your logic so that child nodes that still exist aren't first deleted and then re-added; change your logic to only delete those items that are really deleted (and not added back), and insert only those that are truly new
With either of these two steps, you should get your system working.
Marc