NHibernate query exception, any body help me? - nhibernate

I have meeting a problem, the code look like simple, but exception:
DDS.Model.ATest atest = new DDS.Model.ATest();
atest.AID = Guid.NewGuid();
ISession session = SessionProvider.GetNewSession();
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(atest);
int count = session.CreateQuery("from ATest").List().Count;
//Above row throw a exception:
//Batch update returned unexpected row count from update; actual row count: 0; expected: 1
transaction.Commit();
}

You are trying to load items before saving. Commit the transaction first, and then execute the query.
DDS.Model.ATest atest = new DDS.Model.ATest();
//atest.AID = Guid.NewGuid(); // You should not assign IDs by yourself
ISession session = SessionProvider.GetNewSession();
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(atest);
transaction.Commit();
}
int count = session.CreateQuery("from ATest").List().Count;
But that doesn't seem to be the problem in your case. I believe you have ID mapped as Guid or Guid.comb. You should not assign the value to ID. NHibernate will take care of that.
When you assign the value and call session.SaveOrUpdate(), it will try to do update since ID value is not Guid.Empty. The update method will fail with the exception: Batch update returned unexpected row count from update; actual row count: 0; expected: 1, since UPDATE ... WHERE AID = <some guid> will be executed.

Related

Entity Framework Serializable Transaction Deadlock

I have to insert a row into the database but the problem is that the primary key is generated based on the total counts of rows.
E.g. if the db has 25601 rows, the ID of the newly inserted record would be CT25602.
I want to use transactions for primary key collisions.
Here is the code I wrote.
public void CreateContact(ContactViewModel input)
{
var transactionScopeOptions = new TransactionOptions
{
IsolationLevel = IsolationLevel.Serializable,
Timeout = TimeSpan.MaxValue
};
using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.Required, transactionScopeOptions))
{
var contactNo = GenerateIdentity();
var contact = MapContactFields(new NavContact { No_ = contactNo }, input);
_db.Contacts.InsertOnSubmit(contact);
_db.SubmitChanges();
transaction.Complete();
}
}
This code gives me deadlocks if two persons are trying to insert a contact in a small timespan.
Any suggestions ? Thank you
Yes, the scenario you described is very likely to deadlock. I would recommend using a sequence instead. If not, then one solution is to acquire an exclusive app lock in the transaction, before scannig for the next identity. See sp_getapplock.

Entity Framework Transactions and Deadlock

When SaveChanges() is called on the context, all insert/delete/update operations are executed in a single transaction. It is also possible to use DbContextTransaction for transactions. I am trying to simulate deadlock using both of these approaches. When I use DbContextTransaction, I get the deadlock exception right away but SaveChanges() alone does not throw any deadlock exceptions even after an hour. Am I doing something wrong?
Here is the code with DbContextTransaction. I try to update the first row and then the second row in the main thread. I also start another task which tries to update the second row first and then the first row.
while (true)
{
using (var context = new SchoolDBEntities())
{
using (System.Data.Entity.DbContextTransaction dbTran = context.Database.BeginTransaction())
{
Random r = new Random();
int r1 = r.Next();
int r2 = r.Next();
Student std1 = context.Students.First();
std1.StudentName = "test"+r1;
context.SaveChanges();
Student std2 = context.Students.Find(2);
std2.StudentName = "test"+r2;
context.SaveChanges();
dbTran.Commit();
}
}
}
But when I try it with just SaveChanges() it does not generate deadlock:
while (true)
{
using (var context = new SchoolDBEntities())
{
try
{
Random r = new Random();
int r1 = r.Next();
int r2 = r.Next();
Student std1 = context.Students.First();
std1.StudentName = "test" + r1;
Student std2 = context.Students.Find(2);
std2.StudentName = "test" + r2;
context.SaveChanges();
}
}
}
I am using SQL Profiler to trace the transactions. I even added more updates to the second approach just to make that transaction's duration equal to the DbContextTransaction case thinking it might be the reason but still no luck! When I look at the trace, I see that updates belonging to a particular transaction start only after the previous transaction is committed. What could be the reason?
Upon further investigation, I found out that regadless of the order of changes I have made in the context, the order in which SaveChanges() method always sends update queries to the SQL Server is based on the primary key of the table. In other words, even though I try to reverse the order of update request by first changing row 2 and then row 1, SaveChanges() first executes the update query for row 1 and then for row 2. That's why I don't get a deadlock by using just SaveChanges() method. It does not reverse the order of the queries.

nHibernate update not working for existing entity

i am trying to update record with nHibernate. I tried several solutions, but none of them work (shows no error, bu data is alsno not updated).
First code:
MyRepository rep = new MyRepository(GetCurrentSession());
UserPost post = rep.GetById(id);
post.ValidTo = date;
rep.Update(post);
Second code:
ISession session = GetCurrentSession();
MyRepository rep = new MyRepository(GetCurrentSession());
UserPost post = rep.GetById(id);
post.ValidTo = date;
rep.Update(post);
session.Update(post);
session.Transaction.Commit();
session = null;
Maybe somedy has a suggestion?
1) You need to flush the session if you are not using a transaction`:
var post = _session.Load<Post>(id); //assumes this record exists in the db
post.SomeAttribute=somenewvalue;
_session.SaveOrUpdate(post);
_session.Flush;
2) I don't see a transaction being started? You need to start a transaction to commit.
using(var transaction = _session.BeginTransaction()){
_session.SaveOrUpdate(post);
transaction.commit();
}
using(var transaction = _session.BeginTransaction()){
_session.SaveOrUpdate(post);
transaction.commit();
}
I had this Batch Update returns rowcount = 0 but expected is 1 exception. But this works
Is UserPost mapped correctly? Are you using .hbm.xml files (note the hbm) and the xml file is marked as an embedded resource?
In my experience if an entity is not mapped NHibernate doesn't complain nor throw an error.
Actually looking at your code in more detail you are not calling session.Save
If you want to update some persist entity's fields you shouldn't call session.Update() or session.SaveOrUpdate(), you can use session.Flush() or transactions:
MyRepository rep = new MyRepository(GetCurrentSession());
UserPost post = rep.GetById(id);
post.ValidTo = date;
rep.Flush(); // session.Flush()
OR
using(var transaction = _session.BeginTransaction()){
UserPost post = rep.GetById(id);
post.ValidTo = date;
transaction.commit();
}

NHibernate query count

I am new to NHibernate and I want to have a count of rows from database. Below is my code,
SearchTemplate template = new SearchTemplate();
template.Criteria = DetachedCriteria.For(typeof(hotel));
template.Criteria.Add(Restrictions.Lt("CheckOutDate", SelDate) || Restrictions.Eq("CheckOutDate", SelDate));
template.Criteria.Add(Restrictions.Eq("Canceled", "False"));
int count = template.Criteria.SetProjection(Projections.Count("ID"));
It gives me an error when I try to compile app that says
"Cannot implicitly convert type 'NHibernate.Criterion.DetachedCriteria' to 'int'"
I want to have a count of rows of the table hotel..
You want to use GetExecutableCriteria:
SearchTemplate template = new SearchTemplate();
template.Criteria = DetachedCriteria.For(typeof(hotel));
template.Criteria.Add(Restrictions.Lt("CheckOutDate", SelDate) || Restrictions.Eq("CheckOutDate", SelDate));
template.Criteria.Add(Restrictions.Eq("Canceled", "False"));
var count = DoCount(template.Criteria, session /* your session */);
public long DoCount(DetachedCriteria criteria, ISession session)
{
return Convert.ToInt64(criteria.GetExecutableCriteria(session)
.SetProjection(Projections.RowCountInt64())
.UniqueResult());
}
On a side note, you should take a look at using NHibernate.Linq:
var result = (from h in Session.Linq<Hotel>()
where h.CheckOutDate <= SelDate
where h.Canceled != true
select h).Count();
More information here.

Transactions in NHibernate - UPDATE then INSERT. What am I doing wrong?

In this sample console app I want to update a row in a table, and then insert another row in the same table.
The table is like this
CREATE TABLE [dbo].[Basket2](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NULL
) ON [PRIMARY]
CREATE UNIQUE NONCLUSTERED INDEX [IX_Basket] ON [dbo].[Basket2]
(
[UserId] ASC
)
So basically a user cannot have 2 baskets.
For reasons beyond this post baskets must not be deleted from the table. Therefore when a user needs a new basket the old one is just set to a unique number (id*-1).
The following code is a sample app that simulates the flow - and fails
private static void Main(string[] args)
{
ISessionFactory sessionFactory = CreateSessionFactory();
int userId = new Random().Next();
int basketId;
using (var session = sessionFactory.OpenSession())
{
using (var tx = session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
var newBasket = new Basket {UserId = userId};
basketId = (int) session.Save(newBasket);
tx.Commit();
}
using (var tx = session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
var basket = session.Get<Basket>(basketId);
basket.UserId = basket.Id*-1;
session.Save(basket);
// comment in this line to make it work:
//session.Flush();
var newBasket = new Basket {UserId = userId};
session.Save(newBasket);
tx.Commit();
}
}
}
The error is:
Unhandled Exception: NHibernate.Exceptions.GenericADOException: could not insert: [ConsoleApplication1.Basket][SQL: INSERT INTO [Basket] (UserId) VALUES (?); select SCOPE_IDENTITY()] ---> System.Data.SqlClient.SqlException: Cannot insert duplicate key row in object 'dbo.Basket' with unique index 'IX_Basket'.
If I Flush the session (commented out lines) it works, but why is this necessary?
I would prefer not having to Flush my session and letting Commit() handle it.
You don't need to Save / Update / SaveOrUpdate any entities which are already in the session.
But you are reusing the same id again. So make sure that the session is flushed before:
using (var tx = session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
var basket = session.Get<Basket>(basketId);
basket.UserId = basket.Id*-1;
// no save
//session.Save(basket);
// flush change on unique field
session.Flush();
var newBasket = new Basket {UserId = userId};
// save new item which is not in the session yet
session.Save(newBasket);
tx.Commit();
}
This is because you add the same unique value again. Of course you change the existing value before, but this is not stored to the database before the session is flushed.
The session is flushed when:
you call flush
before queries (except of Get and Load)
on commit (except you use your own ADO connection)
It is a common misunderstanding that NH performs update or insert on the database when you call Save or Update. This is not the case. Insert and update are performed when flushing the session. (There are some exceptions on that, eg. when using native ids.)