Object is reinserted into database immediately after delete (DbLinq) - c#

I have a MySql database, whose general structure looks like this:
Manufacturer <== ProbeDefinition <== ImagingSettings
ElementSettings ====^ ^==== ProbeInstance
I'm using InnoDB to allow foreign keys, and all foreign keys pointing to a ProbeDefinition have set ON DELETE CASCADE.
The issue I'm having is when I delete a ProbeDefinition in my code, it gets immediately reinserted. The cascading delete happens properly so other tables are cleared, but it seems that the LINQ to SQL may be sending an insert for no reason. Checking the ChangeSet property on the database shows 1 delete, and no inserts.
I'm using the following small bit of code to perform the delete:
database.ProbeDefinition.DeleteOnSubmit(probe);
database.SubmitChanges();
Logs in MySql show the following commands being executed when this is run:
BEGIN
use `wetscoprobes`; DELETE FROM wetscoprobes.probedefinition WHERE ID = 10
use `wetscoprobes`; INSERT INTO wetscoprobes.probedefinition (CenterFrequency, Elements, ID, IsPhased, ManufacturerID, Name, Pitch, Radius, ReverseElements) VALUES (9500000, 128, 10, 1, 6, 'Super Probe 2', 300, 0, 1)
COMMIT /* xid=2424 */
What could cause this unnecessary INSERT? Note that deleting a Manufacturer in the exact same way deletes correctly, with the following log:
BEGIN
use `wetscoprobes`; DELETE FROM wetscoprobes.manufacturer WHERE ID = 9
COMMIT /* xid=2668 */
Edit: Upon further testing, it seems that this only happens after I've populated a ListBox with a list of ProbeDefinitions.
I tried running the above delete code before and after the following snippet had run:
var manufacturer = (Manufacturer)cbxManufacturer.SelectedItem;
var probes = manufacturer.ProbeDefinition;
foreach (var probe in probes)
{
cbxProbeModel.Items.Add(probe);
}
The object gets deleted properly before said code has run, but anytime after this point, it performs an insert after the delete. Does it not like the fact that the object is referenced somewhere?
Here's the code I'm running to test deleting a definition from the intermediate window:
database.ProbeDefinition.DeleteOnSubmit(database.ProbeDefinition.Last())
database.SubmitChanges()

Turns out there are issues when there are multiple references to your object. Stepping through the DbLinq source, I learned that after a DELETE is completed, it steps through all other "watched" objects, looking for references.
In this case, I have multiple references through the table database.ProbeDefinition as well as through the manufacturer reference, manufacturer.ProbeDefinition. This isn't an issue till I have accessed objects through both methods. Using Remove can delete the reference from manufacturer, using DeleteOnSubmit will delete the the object from the table. If I do one or the other, the other reference still exists, and thus the object is marked to be reinserted. I'm not sure if this is a bug in DbLinq that it doesn't delete other references, or expected behavior.
Either way, in my case, the solution is to either access the table using only a single method, and delete using that method, or to delete using both methods. For the sake of getting it working, I used the second method:
// Delete probe
this.manufacturer.ProbeDefinition.Remove(probe);
database.ProbeDefinition.DeleteOnSubmit(probe);
database.SubmitChanges();
EDIT: Upon further work on the project and similar issues, I have found the true underlying issue of my implementation. I have a long-lived DataContext, and with how caching works (to make SubmitChanges work), you can't do this. The real solution is to have a short-lived DataContext, and reconnect to the database in each method.

Related

Linq To SQL Delete + Insert best practice

As stated in the title i need to perform delete + insert, i do :
context.DeleteAllOnSubmit ( deleteQuery ) ;
foreach ( var entry in entries )
contex.InsertOnSubmit ( entry ) ;
context.SubmitChanges();
As wrote in that post :
Linq to SQL: execution order when calling SubmitChanges()
I read that the delete operation is the last one applied, but at the moment i see my logic work (i am sure that delete+insert happen dozen of times per day).
What i need is understand if the post is wrong or my logic is and for some reason (update check flag in linq to sql datamodel?) only lucky and avoid the trouble.
After that i would like to know what is the better pattern to make "update" when record cardinality changes.
I mean in my table there is a primary key that identify an entity (an entity has many records) and a subkey that identify each record in the same entity (sub entity).
I need to regenerate (because some sub entity may be inserted, edited or delete) so i use delete + insert (in the messagge form which i write to DB contains only entity and sub enetity that exist, not the deleted ones).
EG:
ID SubID Data
1 1_0 Father
2 2_0 Father
2 2_1 Child 1
3 3_0 Father
3 3_1 Child 1
3 3_2 Child 2
I have no control nor over the table (and data format inside them) nor over the message (that i use to write or delete the table displaied above).
I read that the delete operation is the last one applied, but at the moment i see my logic work (i am sure that delete+insert happen dozen of times per day). What i need is understand if the post is wrong or my logic is and for some reason (update check flag in linq to sql datamodel?) only lucky and avoid the trouble.
Post is correct, delete actually deleted at last.
Your code is working as per design, this is not by chance.
It actually loads all records to be deleted and then deleted all one by one. This happens at last.
This will never fail or will not deleted wrong records, however it has performance issue, you can refer very good msdn article on this
Regardless of how many changes you make to your objects, changes are made only to in-memory replicas. You have made no changes to the actual data in the database. Your changes are not transmitted to the server until you explicitly call SubmitChanges on the DataContext.
When you make this call, the DataContext tries to translate your changes into equivalent SQL commands. You can use your own custom logic to override these actions, but the order of submission is orchestrated by a service of the DataContext known as the change processor.
The sequence of events is as follows: refer msdn
When you call SubmitChanges, LINQ to SQL examines the set of known objects to determine whether new instances have been attached to them. If they have, these new instances are added to the set of tracked objects. This is why we are saying insertion at first
All objects that have pending changes are ordered into a sequence of objects based on the dependencies between them. Objects whose changes depend on other objects are sequenced after their dependencies. then the update
After Update deletion is done
Immediately before any actual changes are transmitted, LINQ to SQL starts a transaction to encapsulate the series of individual commands.
The changes to the objects are translated one by one to SQL commands and sent to the server.
At this point, any errors detected by the database cause the submission process to stop, and an exception is raised.
All changes to the database are rolled back as if no submissions ever occurred. The DataContext still has a full recording of all changes

SMO - PropertyNotSetException when accessing table properties

I'm not sure if this is intended behaviour since I can't find it documented anywhere on MSDN, but this still seems a little bit odd.
I'm writing a program that makes use of SMO. For the sake of testing. I've basically got a test database, with a table in it (the structure is unimportant) that has the following trigger.
CREATE TRIGGER [dbo].[MyTableInsert] ON [dbo].[MyTable] AFTER INSERT
AS
BEGIN
PRINT 'after insert';
END
Now in my code. I expected to use this code to see (and prove) that the table 'MyTable' has an INSERT trigger. However running that code throws a PropertyNotSetException.
var table = new Table(database, tableName.ToString());
Console.WriteLine(table.HasInsertTrigger); // Throws PropertyNotSetException
But if I call the Refresh() method after initialising the table. The call to HasInsertTrigger returns true as excepted.
var table = new Table(database, tableName.ToString());
table.Refresh();
Console.WriteLine(table.HasInsertTrigger); // Returns true.
Making the call to Refresh seems unnecessary, but is it required as I can't find any documentation that says it needs to be called before accessing any properties.
I should have seen this a mile off... I was creating new instances of the tables rather than accessing existing ones.

How should custom code be run during an Entity Framework database migration?

I have a very simple Entity Framework 5 DbMigration that is adding a new boolean column to an existing table with thousands of records in it. I want the initial value of that column for each existing row to be set based on the value in two other columns in the same table. This should only affect existing records, therefore should only be run when the migration is performed and never run again.
This is basically the logic that needs to be executed:
var users = (from u in context.Users select u).ToList();
users.ForEach(u =>
{
u.MyNewBoolColumn = (u.Column1 != null && u.Column2 == "some-value");
context.Users.AddOrUpdate(u);
});
There are two options I can think of, but I don't like either of them:
Create two separate migrations, since the column won't exist until after the first one is completed. This seems sloppy and I'm also not sure how to get the database context in the second migration to actually perform the update.
Run code in a DbMigrationsConfiguration implementation. However, this code would run every single time and I won't be able to tell if it has already run and shouldn't update records.
Is there another, better option?
A migration represents a change in the schema, therefor you can't use AddOrUpdate() stuff inside a migration. But you can, and this is what I would suggest you, run plain SQL code using the Sql() method.

Entity Framework concurrency issue using stored procedures

I am using ASP.NET to build a application for a retail company. I am using the Entity Framework (model-first) as my data access layer. I am using stored procedures to do my CRUD operations and all columns are mapped and seems to be correct as all CRUD functionality are working as expected.
But I am having concurrency issues with the DELETE operation.
I've added a TimeStamp column to the table I am doing the CRUD operation on. The UPDATE operation works fine as it is updating by primary key and the TimeStamp value. Thus if no rows are affected with the UPDATE operation, because of a change in the TimeStamp value, the Entity Framework throws a OptimisticConcurrencyException.
The DELETE operation works on the same principle as it is deleting by primary key and the TimeStamp value. But no exception is thrown when the TimeStamp value does not match between the entity and the database.
In the C# delete method I do retrieve the latest record first and then update the TimeStamp property to another TimeStamp value (It might be different to the retrieved value). After some investigation by using SQL Profiler I can see that the DELETE stored procedure is executed but the TimeStamp parameter that is passed to the stored procedure is the latest TimeStamp value and not the value that I have set the TimeStamp property to. Thus the record is deleted and the Entity Framework does not throw an exception.
Why would the Entity Framework still pass the retrieved TimeStamp value to the Stored Procedure and not the value that I have assigned the property? Is this be design or am I missing something?
Any help will be appreciated! (where is Julie Lerman when you need her! :-))
Optimistic concurrency in EF works fine. Even with stored procedures.
ObjectContext.DeleteObjects passes original values of entity to delete function. This makes sense. Original values are used to identify the row to delete. When you delete object, you don't (usually) have meaningful edits to your entity. What do you expect EF to do with then? Write? To what records?
One legitimate use for passing modified data to delete function is when you want to track deletes in some other table and you need to throw in some information not accessible at database layer, only at business layer. Examples include application level user name or reason to delete. In this situation you need to construct entity with this values as original values. One way to do it:
var x = db.MyTable.Single(k => k.Id == id_to_delete);
x.UserName = logged_in_user;
x.ReasonForChange = some_reason;
// [...]
db.ObjectStateManager.ChangeObjectState(x, EntityState.Unchanged);
db.MyTable.DeleteObject(x);
db.SaveChanges();
Of course, better strategy might be to do it openly in business layer.
I don't understand your use case with rowversion/timestamp.
To avoid concurrency issues you pass original timestamp to modifying code.
That way it can be compared to current value in database to detect if record changed since you last read it.
Comparing it with new value makes little sense.
You usually use change markers that are automatically updated by database like rowversion/timestamp in SQL Server, rowversion in Oracle or xmin in PostgreSQL.
You don't change its value in your code.
Still, if you maintain row version manually, you need to provide:
a) new version to insert and update to be written, and
b) old version (read from database) to update and delete to check for concurrent changes.
You don't send new value to delete. You don't need to.
Also, when using stored procedures for modification, it's better to compute new version in the procedure and return it to application, not the other way around.
Hard to tell without seeing any code, but maybe when the postback occurs the page is being re-bound before your delete method is firing? On whatever method databinds the form controls (I assume it's OnLoad or OnInit), have you wrapped any databinding calls with if ( !this.IsPostBack ) { ... }?
Also I'm not sure if there's a reason why you're explicitly storing the concurrency flag in viewstate/session variables, but a better way to do it (imo) is to add the timestamp to the DataKeyNames property of the FormView/GridView (ie: <asp:FormView ID='blah' runat='server' DataKeyNames='Id, Timestamp'>.
This way you don't have to worry about manually storing or updating the timestamp. ;)

Linq to SQL - Delete Child Property

I'm trying to delete a child property of a domain entity. In the UI, the user selects Delete to remove a CustomVariableGroup from an Application entity.
I thought deleting the property from the LINQ-to-SQL entity & then submitting changes, would cause LINQ-to-SQL to take care of the work on the Database side. But the row never gets deleted from the table. When I refresh the page in my application, the property is still there because it's still there in the Database.
public void Save(Application application)
{
ApplicationRecord appRecord = application.Map(); // Maps domain entity to L2S entity
// Before this line executes, appRecord has 0 CustomVariableGroups, which is correct.
this.Database.ApplicationRecords.Attach(appRecord, true);
// After the attach, appRecord now has 1 CustomVariableGroup again. This is wrong.
appRecord = application.Map(); // Hack to remove the CustomVariableGroup again.
// This doesn't delete the CustomVariableGroup from appRecord. Do I need
// to delete it explicitly? Or should removing it from appRecord, and
// calling SubmitChanges() do it?
this.Database.SubmitChanges();
}
What is the right way for me to get rid of this child property on the entity? I guess I could loop through the list and delete each item individually, but I don't think LINQ-to-SQL is supposed to work that way.
Any ideas are appreciated.
Note: The property ApplicationCustomVariableGroupRecords represents a table that resolves a many-to-many association in the Database. An Application can have one or more CustomVariableGroups, and a CustomVariableGroup can belong to one or more Applications.
Normally you have to specifically delete the object - removing it from a parent collection just means you don't want it to be associated with that particular parent anymore. It can't tell that you don't want to then associate it with a different parent. If you want it to get deleted, you need to make the call to have it deleted (DeleteOnSubmit for L2S, IIRC)
if im not wrong the tables which have n to n relations between them are works like nested..So try to first delete from the 3rd table (which contains IDs of 2 table) and then remove from main table..
[Sorry, i can't see add comment button on the page.. so i wrote this idea as answer ]

Categories

Resources