Nhibernate - item gets its ParentID updated to null instead of being deleted - c#

The business logic inside a process is:
begin transaction
add an item to a collection
perform a find("somethingA")
delete that item depending on the previous step.
commit transaction
Im using cascade all-delete-orphans, and the inverse=true, both in my parent class. When removing the item from the collection, I set the .parentObj = null and remove the item from the collection.
When using TemplateFlushMode.Auto, I profiled the Database and the following is done:
INSERT item
SELECT related tosomethingA
UPDATE parentID (the FK to the parent) of the Item to NULL
(the insert item is done because a find() is done, to guarantee data consistency in the database). The the select is done, and then I would expect a DELETE to be performed... but an update to parentID = null is done.
When performing a Session.Flush() right before the Find(), the following happens:
INSERT item
SELECT related tosomethingA
DELETE Item
When using TemplateFlushMode.Commit, the changes are done at the end of the transaction and the item is never inserted:
SELECT related tosomethingA
The app I'm working with is using TemplateFlushMode.Auto, so I'm wondering, ,is there an explanation why the cascading is not working if a select is done in between one item is added and then removed in the same transaction?
I know the first answer that comes up is "don't add an item to a collection, if it will be removed afterwards". But I rather don't change the business logic.
Does anyone know why an update is being done instead of a delete when using AUTO? (when using Commit it does not insert the object)

I guess you should use the cascade="delete" option in your mapping file in order to cascade on delete

Are you using a two way one to many binding ? Have you marked not-null="true" on the foreign key in the details table ? Can you please post your mappings XML file for better understanding ?
I know that i haven't answered your question, but I do not have enough reputation to post a comment instead.

Related

EF Core reload all entities for an entity type

In EF Core you can "reload" an Entity from the data store to pickup any changes.
An example of why you might need to do that is if you need to resolve a DB concurrency exception on SaveChanges.
This does work ok for one record...
EntityEntry<T> entityEntry = GetEntity(123);
entityEntry.Reload();
The only problem is, the Reload() executes a SQL statement per entityEntry.
So, if you want to refresh a set of entityEntry, you get a SQL statement per entry.
Whereas, the normal context.Set<T>().Load(); executes one SQL statement that retrieves all of the rows.
With a small number of entityEntry, the performance hit is negligable; but anytime I see a RBAR design approach, it raises a red flag for me.
Question: other than a RBAR loop
foreach (var e in context.ChangeTracker.Entries<T>)
{
e.Reload();
}
is there a way to re-execute the context.Set<T>().Load(); to reload the entries as a set?
Note: I am using the
context.Entry(e).State = EntityState.Detached
approach now; checking to see if there is something better that I have missed.
UPDATE 1
How I am testing
Read data from database
Add record to database
Delete record from database
Change record in database
"reload" data
the result I am looking to get is
entity for changed record is updated
entity for deleted record is removed
"optionally" entity for new record is added
the Add is optional because that is not the designed behaviour of "reload", which is to update existing entity.

How to update-insert or remove a lot of data in database with contact table?

I use with winform in C# and Entity Framework.
In Database I have a contact table
Between "word" and "user", Word table has a lot of data (4000+).
I have a window with datagridview where there is a checkbox in each line that the user marks the words he wants.
And by pressing the save button I want to update all the records that he has changed in the table.
listWord = Program.DB.WordUseUser.Where(lw => lw.IdUser == thisIdUser).ToList();
///Clicking on the checkbox I add or remove from ListWord accordingly...
foreach (var item in listWord)
{
Program.DB.WordUseUser.Remove(item);
}
Program.DB.SaveChanges();
foreach (WordUseUser item in listWord)
{
Program.DB.WordUseUser.Add(item);
}
Program.DB.SaveChanges();
It takes a lot of time (of course ...)
And I'm looking for a more effective solution.
I tried to use a solution here:Fastest Way of Inserting in Entity Framework
But it only talks about updating existing data
And not updating and adding and deleting together
I would love for help !!
Fast reply - you have to do it inside explicit transaction.
Not only this is secure, but also this would be much more faster.
So, begin transaction - do your updates/inserts and commit transaction.
Every query creates it's own implicit transaction. Unless there is already existing transaction. So think of it as:
without creating a transaction database has to do 12000 operations (for every query: create transaction, execute query, commit transaction) and when you create an explicit transaction then it's just 4002 operations.

Best way to update a collection of SQL data

I am writing a .net/entity framework code snippet that's supposed to update/delete a bunch of MS SQL rows with the latest data passed from UI.
Say the table originally has 20 rows and the latest collection contains 15 records. Out of the 15, 9 have changes and 6 remain the same. So 9 rows will be updated, and the 5 rows that are not in the latest collection, will be deleted from the table.
So my question is, what's the best way of doing this: If I iterate over all 20 rows and try to find each of them, it would be O(mn). Deleting all table rows and re-insert them may be faster but I am not sure.
All help appreciated!
So you have a user interface element filled with items of some class. You also have a database with a table filled with items of the same class.
IEnumerable<MyClass> userInterfaceElements = ...
IQueryable<MyClass> databaseElements = ...
Note: the query is not executed yet!
You want to update the database such, that after your update your database contains the items from your user interface elements.
User interface elements that are not in the database yet will be added
Database elements that are not in the user interface need to be removed
User interface elements that are also in the database need to be updated.
You didn't write how you decide whether a user interface element is also in the database.
Let's assume you don't invent primary keys. This means that elements with a default value (zero) for your primary key are elements that are not in the database.
var itemsToAdd = userInterfaceElements.Where(row => row.Id == 0);
var itemsToUpdate = userInterfaceElements.Where(row => row.Id != 0);
var idsItemsToKeep = itemsToUpdate.Select(row => row.Id);
var itemsToRemove = databaseElements.Where(row => !idsItemsToKeep.Contains(row.Id))
The last one: remove all items that have an Id that is not in your user interface elements anymore.
Note: we still have not executed any query!
Adding the items to your database will change databaseElements, so before you make any changes you need to materialize the items
var addList = itemsToAdd.ToList();
var updateList = itemsToUpdate.ToList();
var removeList = itemsToRemove.ToList();
By now you've queried your database exactly once: you fetched all items to remove. You can't order entity framework to remove items without fetching them first.
dbContext.MyClasses.RemoveRange(removeList);
dbContext.MyClasses.AddRange(addList);
To update in entity framework, the proper method would be to fetch the data and then change the properties.
Some people prefer to attach the items to the dbContext's change tracker and tell that it is changed. This might be dangerous however, if someone else has changed some properties of these items, especially if you don't show these values in your user interface elements. So only do this if you really have a long list of items to update.
Proper way:
foreach(var itemToUpdate in updateList)
{
var fetchedItem = dbContext.MyClasses.Find(itemToUpdate.Id);
// TODO: update changed properties of the fetchedItem with values from itemToUpdate
}
Dangerous method:
foreach(var itemToUpdate in updateList)
{
dbContext.Entry(itemToUpdate).State = entityState.Modified;
}
Finally:
dbContext.SaveChanges();
Improved delete method
You've got a problem when you filled your user interface element with database values, and some other process removed one of these values from your database.
When your code looks at the primary key, it will think it is in the database, however it is not there anymore. What to do with this element? Add it again? Act as if the user also wanted it to be deleted?
To solve this kind of problems, quite often people don't delete items from their database, but declare them obsolete instead. They add a Boolean column to the table that indicates whether the item is to be deleted in the near future. This solves the problem that people want to update items while others want them to be removed.
Regularly, every month or so, a process is started to remove all obsolete objects. The chance that you want to update an obsolete object are much lower.
If this needs to be full save: don't remember a Boolean obsolete, but the obsolete date. Periodically remove all items that are obsolete for a longer time.
The nice thing about the obsolete, is that if someone declared an item obsolete by accident, there is still some time to repair this.

Linq to SQL, Update a lot of Data before One Insert

Before insert new value to table, I need change one field in all rows of that table.
What the best way to do this? in c# code, ore use trigger? if C# can you show me the code?
UPD
*NEW VERSION of Question*
Hello. Before insert new value to table, I need change one field in all rows of that table with specific ID( It is FK to another table).
What the best way to do this? in c# code, ore use trigger? if C# can you show me the code?
You should probably consider changing your design this doesn't sound like it will scale well, i would probably do it with a trigger if it is always required, but if not, id use ExecuteCommand.
var ctx = new MyDataContext();
ctx.ExecuteCommand("UPDATE myTable SET foo = 'bar'");
Looking at your comment on Paul's answer, I feel like I should chime in here. We have a few tables where we need to keep a history of each entry in that table. We implement this by creating a separate table for each. For example, we may have a Comment table, and then a CommentArchive table with a foreign key reference to the CommentId in the Comment table.
A trigger on the Comment table ensures that each time certain fields in the Comment table are updated, the "old" version (which is accessible via the deleted table in the trigger) gets pushed to the CommentArchive table. Obviously, this means several CommentArchive entries may exist for each Comment, but if you're only looking for the "active" comments, you just look in the Comment table. And if you need information about the history of a comment, you can easily use LINQ to SQL to jump from the Comment you're interested in to the CommentArchives that reference it.
Because the triggers we use in the above example only insert a single value into the Archive table for each update, they run very quickly and we get good performance. We had issues recently where I tried making the triggers more complex and we started getting dead-locks with as few as 15 concurrent transactions. So the lesson is that you should make these triggers simple, and make them touch as few rows in as few tables as possible.

Check if it is safe to delete a row

I want to be able to check if deleting a row from a table in SQL Server 2008 will fail because of a Foreign Key violation without attempting to delete it.
Basically, I don't want to show the user a delete button if they are not going to be able delete it because the key is used elsewhere.
I am needing this in many places in the application so don't really want to have to write the checks manually to see if it safe to delete the row. Any suggestions on the best way to achieve this?
I am using entity framework for access to the data.
There is no quick and easy way to check this. You could probably build something dynamic using information_schema, but it will undoubtedly be ugly and not very fast.
The absolute best choice is the few lines of custom code that it takes to verify per location.
Another option would be to start a transaction, try the delete. If it fails, then you know. If it succeeds, roll back the transaction, and you know the delete is possible. This is still ugly and is using transactions in a somewhat broken way, but it would work. Make sure cascade deletes aren't on for the table, though.
When you query, do a LEFT JOIN to the child table. Use CanDelete computed value to decide if the button should be shown. The COUNT here removed duplicates if you more than 1 child row per parent row.
SELECT
Col1, Col2, Col3, ...,
CASE C.Existence WHEN 0 THEN 1 ELSE 0 END AS CanDelete
FROM
ParentTable P
LEFT JOIN
(
SELECT COUNT(*) AS Existence, FKColumn
FROM Childtable GROUP BY FKColumn
) C ON P.FKColumn = C.FKColumn
WHERE
P.Col = ...
Another way might be
SIGN(C.Existence) AS HasChildRows
I've done this sort of thing in prior applications. I created a function named something like TryDelete(). Inside the method, I attempted to delete the desired row. If I got a FK exception, I caught it and returned false. In either case, true or false, I encapsulated the delete in a transaction and then rolled it back.
You could add in a partial class of your entity a method that would check if the referenced objects exist.
For example, lets say you have Entity1 which has collections of Entity2. Basically, in each of the entity partial classes you'd write a property IsReferenced that would:
For Entity1 return true if Entity1 has any item in Entity2
For Entity2 return ture if there's a reference to Entity1
As you're guessing, you'll need to make sure that you include referenced values always in your fetch, or, if you're working attached to context, you could use .Load() in IsReferenced to fetch entities before checking. It is an overhead, it just depends if you're willing to 'pay' for it.
Then, you can show/hide the 'delete' button based on that element property wherever needed thus avoiding having to repeat the checks each time.
I think you have 2 possible choices here.Since you cannot garantee that all relations will be mapped in your OM, you would have to check it on the database.
You can either try an actual delting inside a transaction that is rolled back afterwards, but this would also work if you have to contraint configured with cascading deletes...
Another way would be extracting all contraints from the sysobjects table, and verify that each table has no records. But that would require some dynamic SQL, which can also get quite messy.
if you're at the database level I would join all the tables where a conflict could exist.
any records that return can not be deleted which means the remaining set can be.
Assuming that the database is used by multiple users (which the vast majority are) - there's going to be a window of opportunity between "checking" the delete is possible, and the user possibly deciding to delete the row, during which someone else might perform some activity that negates the result of the test.
This means that you might display the Delete button, but by the time you attempt the delete, it's no longer possible. Also, you might not display a Delete button, but by the time the user has decided they want to delete the row (but can't find the button), they should be allowed to.
There's no way to avoid these kind of races. I'd just let people attempt the delete if they want to, but be prepared to deal with failures due to Foreign Keys.

Categories