Best way to delete all rows in a table using NHibernate? - nhibernate

To keep my integration tests independent I remove all old data and insert new test data before each test. Is there a better way of doing this than simply querying for all entities and deleting them one by one?
I have considered writing a stored proc that runs "delete from tablename;" for each table that is to be cleared. That ought to quite a bit faster, but it would be nice to do it without doing SQL queries or calling SPs via NH.
I'm using vanilla NHibernate and Linq to NHibernate. I beleive Castle Active Record has something like Foo.DeleteAll(), but I don't want to use Active Record for this project.
Any ideas?
Thanks /Erik
UPDATE:
Since this question was asked and answered, progress has been made by the NHibernate team. As Ayende explains in this blog post, you can now execute DML queries directly, without NHibernate having to fetch any entities.
To delete all Foo objects you could do like this:
using (ISession session = ...)
using (ITransaction transaction = session.BeginTransaction())
{
session.CreateQuery("delete Foo f").ExecuteUpdate();
transaction.Commit();
}
This query would generate the following SQL:
delete from Foo
which aught to be significantly faster than fetching the entities first and then deleting them. Be careful though, since queries like these do not affect the level 1 cache.

In the TearDown of my UnitTests, I mostly do this:
using( ISession s = ... )
{
s.Delete ("from Object o");
s.Flush();
}
This should delete all entities.
If you want to delete all instances of one specific entity, you can do this:
using( ISession s = .... )
{
s.Delete ("from MyEntityName e");
s.Flush();
}
Offcourse, there's a drawback with this method, and that is that NHibernate will first fetch the entities before deleting them.

I use Fluent Nhibernate attributes so I modify code a little in order not to hardcore table names
private static void CleanUpTable<T>(ISessionFactory sessionFactory)
{
var metadata = sessionFactory.GetClassMetadata(typeof(T)) as NHibernate.Persister.Entity.AbstractEntityPersister;
string table = metadata.TableName;
using (ISession session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
string deleteAll = string.Format("DELETE FROM \"{0}\"", table);
session.CreateSQLQuery(deleteAll).ExecuteUpdate();
transaction.Commit();
}
}
}
usage
CleanUpTable<Person>(sessionFactory);

With NHibernate 5.0 you can now simply do:
session.Query<Foo>().Delete();
Documentation:
//
// Summary:
// Delete all entities selected by the specified query. The delete operation is
// performed in the database without reading the entities out of it.
//
// Parameters:
// source:
// The query matching the entities to delete.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// Returns:
// The number of deleted entities.
public static int Delete<TSource>(this IQueryable<TSource> source);

Related

Update command in Fluent NHibernate

I am trying to update an object after retrieving it from a database.
This fires 2 queries , one for the select and the other for the update, is there any way of update an object using Fluent NHiberNate firing only one query ?
My code is as below:
var userProfile = userProfileRepository
.Find(x => x.ClientId == clientId)
.FirstOrDefault();
/* update UserProfile object here */
userProfileRepository.SaveOrUpdate(userProfile);
the SaveOrUpdate Method looks as such :
public bool SaveOrUpdate(T instance)
{
using (var session = SessionManager.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
session.SaveOrUpdate(instance);
transaction.Commit();
}
return true;
}
}
In case that your issue is:
regardless of what I do, SaveOrUpdate() always sends SELECT then UPDATE
You should check the doc:
5.1.4.7. Assigned Identifiers
If you want the application to assign identifiers (as opposed to having NHibernate generate them), you may use the assigned generator. This special generator will use the identifier value already assigned to the object's identifier property. Be very careful when using this feature to assign keys with business meaning (almost always a terrible design decision).
Due to its inherent nature, entities that use this generator cannot be saved via the ISession's SaveOrUpdate() method. Instead you have to explicitly specify to NHibernate if the object should be saved or updated by calling either the Save() or Update() method of the ISession.
So, if your Fluent configuration sets the ID to be assigned - NHibernate has no other way then check if it
exists
or is new
because used method was SaveOrUpdate()
Solution(s)
1) Change the ID to be generated by DB or NHiberante 2) use explicit Update()
Are you trying to create an
UPDATE ... WHERE ...
statement?
AFAIK the NHibernate way to do this, is to select the appropriate objects (using the WHERE clause), update the fields, and persist them again.
var tempObjects = _session.Query<myObject>.Where(o => o.Id > 500);
// update proxy objects
foreach (var o in tempObjects)
{
o.MyValue = updatedValue;
}
// commit updated objects
_session.Update(tempObjects);
To be honest, we've used ISession.CreateSQLQuery ourselves. I hate using SQL in code because it breaks in refactoring, but if you must - here's how:
_session.CreateSQLQuery(
#"UPDATE [MyTable] SET [MyValue]=:updatedvalue WHERE Id > 500")
.SetParameter("updatedvalue", updatedValue)
.ExecuteUpdate();

"Convert" Entity Framework program to raw SQL

I used Entity Framework to create a prototype for a project and now that it's working I want to make the program ready for production.
I face many challenges with EF, the biggest one being the concurrency management (it's a financial software).
Given that it seems to have no way to handle pessimistic concurrency with EF, I have to switch to stored procs in SQL.
To be honest I'm a bit afraid of the workload that may represent.
I would like to know if anybody have been in the same situation before and what is the best strategy to convert a .net code using EF to raw SQL.
Edit:
I'm investigating CLR but it's not clear if pessimistic concurency can be manage with it. is it an option more interesting than TSQl in this case ? It would allow me to reuse part of my C# code and structure of function calling another functions, if I understand well.
I was there and the good news is you don't have to give up Entity Framework if you don't want to. The bad news is you have to update the database yourself. Which isn't as hard as it seems. I'm currently using EF 5 but plan to go to EF 6. I don't see why this still wouldn't work for EF 6.
First thing is in the constructor of the DbContext cast it to IObjectContextAdapter and get access to the ObjectContext. I make a property for this
public virtual ObjectContext ObjContext
{
get
{
return ((IObjectContextAdapter)this).ObjectContext;
}
}
Once you have that subscribe to the SavingChanges event - this isn't our exact code some things are copied out of other methods and redone. This just gives you an idea of what you need to do.
ObjContext.SavingChanges += SaveData;
private void SaveData(object sender, EventArgs e)
{
var context = sender as ObjectContext;
if (context != null)
{
context.DetectChanges();
var tsql = new StringBuilder();
var dbParams = new List<KeyValuePair<string, object>>();
var deletedEntites = context.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted);
foreach (var delete in deletedEntites)
{
// Set state to unchanged - so entity framework will ignore
delete.ChangeState(EntityState.Unchanged);
// Method to generate tsql for deleting entities
DeleteData(delete, tsql, dbParams);
}
var addedEntites = context.ObjectStateManager.GetObjectStateEntries(EntityState.Added);
foreach (var add in addedEntites)
{
// Set state to unchanged - so entity framework will ignore
add.ChangeState(EntityState.Unchanged);
// Method to generate tsql for added entities
AddData(add, tsql, dbParams);
}
var editedEntites = context.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var edit in editedEntites)
{
// Method to generate tsql for updating entities
UpdateEditData(edit, tsql, dbParams);
// Set state to unchanged - so entity framework will ignore
edit.ChangeState(EntityState.Unchanged);
}
if (!tsql.ToString().IsEmpty())
{
var dbcommand = Database.Connection.CreateCommand();
dbcommand.CommandText = tsql.ToString();
foreach (var dbParameter in dbParams)
{
var dbparam = dbcommand.CreateParameter();
dbparam.ParameterName = dbParameter.Key;
dbparam.Value = dbParameter.Value;
dbcommand.Parameters.Add(dbparam);
}
var results = dbcommand.ExecuteNonQuery();
}
}
}
Why we set the entity to unmodified after the update because you can do
var changed properties = edit.GetModifiedProperties();
to get a list of all the changed properties. Since all the entities are now marked as unchanged EF will not send any updates to SQL.
You will also need to mess with the metadata to go from entity to table and property to fields. This isn't that hard to do but messing the metadata does take some time to learn. Something I still struggle with sometimes. I refactored all that out into an IMetaDataHelper interface where I pass it in the entity type and property name to get the table and field back - along with caching the result so I don't have to query metadata all the time.
At the end the tsql is a batch that has all the T-SQL how we want it with the locking hints and containing the transaction level. We also change numeric fields from just being set to nfield = 10 but to be nfield = nfield + 2 in the TSQL if the user updated them by 2 to avoid the concurrency issue as well.
What you wont get to is having SQL locked once someone starts to edit your entity but I don't see how you would get that with stored procedures as well.
All in all it took me about 2 solid days to get this all up and running for us.

Why does NHibernate need to know the ID of an auto ID based entity before flush is called?

With my only ORM knowledge being L2S/EF, I was surprised when the following code inserted a row into the database before I called repo.Save:
var repo = new UserRepository();
var user = new User { Name = "test" }
repo.Add(user);
//repo.Save();
Repo looks like this:
public void Add(T entity)
{
session.Save(entity);
}
public void Save()
{
session.Flush();
}
After some digging, it seems NHibernate needs to make the insert happen right away in order to get the ID of the new entity (since it's using an auto increment ID). But L2S/EF doesn't work like this; I can add many entities and save them all at the end.
Question is: is there a way to achieve the same thing with NHibernate, while still using auto increment IDs, and out of interest does anyone know why it works like this?
Fabio Maulo already blogged about the usage of identity generator a few times. The answer is: use hilo, guid.comb or something like this.
NHibernate needs the identity because every entity in the session (they are called "persistent entities") needs to be identified. The identity is also normally used to determine if the record already exists in the database (unsaved value).
session.Save actually only makes a transient entity persistent. When the database is generating the id, it needs to be stored to get the id. If NH can create the id itself (eg using hilo), it could be stored next time when the session gets flushed.

How to delete data in DB efficiently using LinQ to NHibernate (one-shot-delete)

Producing software for customers, mostly using MS SQL but some Oracle, a decision was made to plunge into Nhibernate (and C#).
The task is to delete efficiently e.g. 10 000 rows from 100 000 and still stay sticked to ORM.
I've tried named queries - link already,
IQuery sql = s.GetNamedQuery("native-delete-car").SetString(0, "Kirsten");
sql.ExecuteUpdate();
but the best I have ever found seems to be:
using (ITransaction tx = _session.BeginTransaction())
{
try
{
string cmd = "delete from Customer where Id < GetSomeId()";
var count = _session.CreateSQLQuery(cmd).ExecuteUpdate();
...
Since it may not get into dB to get all complete rows before deleting them.
My questions are:
If there is a better way for this kind of delete.
If there is a possibility to get the Where condition for Delete like this:
Having a select statement (using LinQ to NHibernate) => which will generate appropriate SQL for DB => we get that Where condition and use it for Delete.
If there is a better way for this kind of delete.
Yes, you could use HQL instead of SQL.
If there is a possibility to get the Where condition for Delete [using Expressions]:
No, AFAIK that's not implemented. Since NHibernate is an open source project, I encourage you to find out if anyone has proposed this, and/or discuss it on the mailing list.
Thanks for your quick reply. Now I've probably got the difference.
session.CreateSQLQuery(cmd).ExecuteUpdate();
must have cmd with Delete From DbTable. On the contrary the HQL way
session.CreateQuery(cmd).ExecuteUpdate();
needs cmd with Delete From MappedCollectionOfObjects.
In that case it possibly solves my other question as well.
There now is a better way with NHibernate 5.0:
var biggestId = GetSomeId();
session.Query<Customer>()
.Where(c => c.Id < biggestId)
.Delete();
Documentation:
//
// Summary:
// Delete all entities selected by the specified query. The delete operation is
// performed in the database without reading the entities out of it.
//
// Parameters:
// source:
// The query matching the entities to delete.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// Returns:
// The number of deleted entities.
public static int Delete<TSource>(this IQueryable<TSource> source);

LINQ to SQL: Concurrency resolution

Given this LINQ to SQL:
using (var db = Database.Context)
{
var root = (from post in db.Post
where post.Id == rootPostId
select post).Single();
root.LastActivityUtc = DateTime.UtcNow;
db.SubmitChanges();
}
What will happen if the same record is concurrently being changed by another call to the same method (where this code lives) with the same rootPostId? Will an exception be thrown?
In such an event--concurrency conflict--I'd like to handle it by simple discarding the change so that just one update to LastActivityUtc is submitted instead of both, which will probably have the same value anyway.
You can detect and resolve your concurrency issues, by catching a ChangeConflictException:
using (var db = new MyDataContext())
{
var root = (from post in db.Post
where post.Id == rootPostId
select post).Single();
root.LastActivityUtc = DateTime.UtcNow;
try
{
db.SubmitChanges();
}
catch (ChangeConflictException)
{
db.ChangeConflicts.ResolveAll(RefreshMode.KeepChanges);
db.SubmitChanges();
}
}
With RefreshMode.KeepChanges you will keep all changes of your client objects, and the changes from other users on other fields will be merged.
Recommended articles:
Optimistic Concurrency Overview
LINQ To SQL Samples - Optimistic Concurrency
Resolve Concurrency Conflicts by Merging with Database Values (LINQ to SQL)
This seems like a good candidate for a stored procedure. Rather than select/update, create a simple stored procedure that executes the following:
UPDATE Post SET LastActivityUtc = GETUTCDATE() WHERE Id=#id
Pass the id to the stored procedure and call it whenever you want to update the last activity of the post.