NHiberate session.Get() gets the cached entity. How to get the latest entity from the DB? - nhibernate

I'm using the CQRS pattern to get the data from the DB using NHibernate.
Here is the CommittingTransactionCommandHandler which calls the UpdateProductHandler
class CommittingTransactionCommandHandler
{
private readonly ISession session;
private readonly ICommandHandler<TCommand> _inner; //Generic Command Handlers
public CommittingTransactionCommandHandler(ISession session)
{
this.session = session;
}
public async Task Execute(TCommand command)
{
using (var txn = session.BeginTransaction(IsolationLevel.Unspecified))
{
try
{
await _inner.Update(command); // This calls the UpdateProducthandler's Update method
txn.Commit();
}
catch (Exception ex)
{
throw;
}
}
}
}
Here is the Command Handler for the Update.
class UpdateProductHandler : ICommand
{
private readonly ISession session;
public UpdateProductHandler(ISession session)
{
this.session = session;
}
public async Task Update(int id, ProductIdentity productIdentity)
{
var product = session.Get(id);
product.Update(productIdentity);
}
}
Here is the Query Handler for the Get
class GetProductHandler
{
private readonly ISession session;
public GetProductHandler(ISession session)
{
this.session = session;
}
public async Task<Product> Get(int id)
{
var product = session.Get(id);
if (product == null)
throw new Exception("Entity not found");
return Task.FromResult(product);
}
}
Here is the code for the Product entity
class Product
{
public virtual int Id { get; protected set; }
public virtual string Name { get; protected set; }
public virtual string Description { get; protected set; }
public virtual int? Version { get; protected set; }
public virtual void Update(ProductIdentity productIdentity)
{
Name = productIdentity.Name;
Description = productIdentity.Description;
}
}
The flow is
CommittingTransactionCommandHandler is a generic command handler. This is called from the API, which internally invokes the UpdateProductHandler. The transaction is opened in this and committed here.
The scenario is that
I get a Product from the DB using the GetProductHandler. (In this case, the version number of the Product is 10.)
I'm updating the Product using the UpdateProductHandler and commits the session which is under the transaction. (Here version number of the Product is 11)
Immediately after the Update Product, I query the same Product using the GetProductHandler and loads it in the Edit mode in the UI. (But the Product fetched using the GetProductHandler has a version number 10 and not 11.)
Here is the issue, instead of getting the latest update from the DB, the above GetProductHandler, gets the previous state of the object.(Found using the version number)
Now, if I try to update the Product, I get a Stale Object State Exception since the version number is 10 which is not the latest version of the Product.
I've tried with session.Refresh(product) but all in vain as it affects the other transactions.
How can I resolve this?

As Ayende explains, Get will NOT always hit the database. It will only hit the database if entity not found in first level cache.
You have not explained how you are updating the entity; what is inside product.Update method. If you are updating using API that does not update underlying first level cache (SQL/HQL/Linq), then the problem you stated in question is obvious. In that case,
You load entity in session cache using Get (Version 10). This hits the database.
Then, you call Update (database hit) which will not update the first level cache (Version changed to 11 in database; NOT in cache).
Then you call Get again. NHibernate looks if entity is already loaded in session cache. Yes; it is (Version 10). It just returns it. No database hit.
Solution 1:
Not sure why that Update method is needed. You can directly update the entity like below:
public async Task Update(int id, ProductIdentity productIdentity)
{
var product = session.Get(id);
product.Version = 11;
//Update other properties here
...
...
//That's it. You are already calling `Flush` somewhere somehow
}
Solution 2:
As Ayende NOT recommended (and I am not recommending either), don't use Get and write a query that will hit the database. Use Linq/HQL/SQL API instead.
select customer from s.Linq<Customer>()
where customer.Id = customerId
select customer
).FirstOrDefault();
Every time that I see something like that, I wince a little inside. The reason for that is quite simple. This is doing a query by primary key. The key word here is a query.
This means that we have to hit the database in order to get a result for this query. Unless you are using the query cache (which by default you won’t), this force a query on the database, bypassing both the first level identity map and the second level cache.
Get and Load are here for a reason, they provide a way to get an entity by primary key. That is important for several aspects, most importantly, it means that NHibernate can apply quite a few optimizations for this process.

Related

NHibernate, a different object with the same identifier value was already associated with the session

I have been working with NHibernate, using Fluent NHibernate for mapping. I solved a lot of issues, and started to think myself as experienced in nhibernate.
However, this error is quite strange.
This is my model:
public class MessageNew
{
public virtual int Id { get; set; }
public virtual string Content { get; set; }
public virtual string Subject { get; set; }
public virtual User User { get; set; }
public virtual bool IsSent { get; set; }
public virtual string AmazonMessageId { get; set; }
}
And my mapping
public class MessageNewMap : ClassMap<MessageNew>
{
public MessageNewMap()
{
Id(x => x.Id);
Map(x => x.Content).CustomSqlType("text");
Map(x => x.Subject);
Map(x => x.AmazonMessageId);
Map(x => x.IsSent);
References(x => x.User);
}
}
Here where exception occurs:
foreach (var userToSend in usersToSend)
{
string body = MailHelper.BuildSomeBody()
if (userToSend != CurrentUser)
{
MessageNew message = new MessageNew
{
User = userToSend,
IsSent = false,
Content = body,
Subject = subject
};
session.Save(message); // Exception thrown
}
}
The exception details:
NHibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: 1779, of entity: Models.MessageNew
at NHibernate.Engine.StatefulPersistenceContext.CheckUniqueness(EntityKey key, Object obj)
at NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityColumn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.Save(Object obj)
Id generator is database driven auto-increment id generator. (not hilo or any other). NHibernate version is 3.2.0 .
I have tried overloading Equals and GetHashCode, no luck.
The UnitOfWork pattern I am using requires not to commit transaction or flush session inside foreach loop. NHibernate says there is another object with same id, but all i am doing is inserting a new object, which does not have any identifier at all.
I am using the same structure all over my project, and it works well everywhere but this. I am suspicious that it might be because of "Content" property, which is text and set to a large string.
What am i missing here? Or NHibernate is missing something?
Sometimes it happend when we assign the object to the same new object. So first check your model and viewmodel that they are not same.
I had similar problem. I went through a lot of discussions, tutorials and forums, but after writing some unit tests, I realized:
1) session.Contains method works with instances
2)session.Save/SaveorUpdate works with ID
This error shows you have another instances of object with same ID in session.So, contains return false because you are working on different instances and Save/SaveorUpdate throws an exception because there is another object with same ID in session.
I've solved my problem like this(my problem was in Job Entity):
Job lJob = lSession.Load<Job>(this.ID);
if(lJob.ID==this.ID)
lSession.Evict(lJob);
lSession.SaveOrUpdate(this);
I hope it helps you
You can use Evict() to evict an object from a session and then you can do whatever you want.
This error occurs when you have the same object in another session.
messagenew should implement Equals and GetHashCode
public class MessageNew
{
public virtual int Id { get; set; }
public override bool Equals(object obj)
{
var other = obj as MessageNew;
return (other != null) && (IsTransient ? ReferenceEquals(this, other) : Id == other.Id;
}
private int? _cachedHashcode; // because Hashcode should not change
public override int GetHashCode()
{
if (_cachedHashcode == null)
_cachedHashcode = IsTransient ? base.GetHashCode() : Id.GetHashCode();
return _cachedHashcode.Value;
}
public bool IsTransient { get { return Id == 0; } }
}
I read some NH code. It basically inserts the new instance into the database to get its id. Then it checks if the id generated by the database is actually unique. If not, you get this exception.
Your database is not generating unique ids. You most probably forgot to set it to an IDENTITY column.
OR the identity starts counting on 0 instead of 1.
That exception usually indicates that you have 2 separate instances of an object with the same identifier value which you are trying to manage over the same session.
You already have another instance of the entity with that id.
Two possible issues:
1 - Your comparison of the entity does not work. You could override equals as suggested or you could change your test case that you use prior to the save:
if (userToSend.Id != CurrentUser.Id)
2 - You are not generating a unique Id for your entity, you need to either assign an Id yourself, have NHibernate generate one or have your sql server do it for you. In your mapping it is implied that an Identity should be used (Fluents default) but have you set up the column in your database to be and Identity column?
My take: you are not declaring an Id generator. Therefore, as soon as you get two MessageNew instances in the session, they'll both have 0 as the Id.
maybe a bit late but hope this helps.
I had a similar problem when i was trying to save multiple instances of an object over the same session with an auto generated column on them. My solution was giving a diferent value and assign it mannually for each entity, so nhibernates doesn't recognize it as the same primary key for that entity.
[..]
};
session.Clear();
session.Save(message);
Try this, helped me.
Add below two lines before Session.Save or Session.SaveOrUpdate
Session.Clear();
Session.Flush();
This will clear all cached entities with the Session.

RavenDB UseOptimisticConcurrency in Config?

Is there a way to set optimistic concurrency to true in Raven.Server.exe.config? Or, can it somehow be applied at the database level? On RavenDB's site, I see a couple of mentions of setting UseOptimisticConcurrency = true, but it looks like it's at the session level within the code:
public void Save<T>(T objectToSave)
{
using (IDocumentSession session = Database.OpenSession())
{
session.Advanced.UseOptimisticConcurrency = true; // This is the setting
Guid eTag = (Guid)session.Advanced.GetEtagFor(objectToSave);
session.Store(objectToSave, eTag);
session.SaveChanges();
}
}
I would like to know if it that setting exists somewhere server-wide, so it doesn't need to be specified for each session in code.
Edit: The code above produces the following error. Trying to find out why...
Edit 2: Ok, I'm making progress. If I retrieve the object, and call GetEtagFor() all within the same session, then I get a valid eTag. So I guess my main question now is: Is the correct way to use a session, within a client UI, to open the session once when the app starts, and then close it at the end? And... What's the correct way to store the eTag? The way it's coded above, the eTag is retrieved right before storing, which I would imagine is the wrong way to do it. I'm guessing the eTag should be retrieved when the object is first retrieved. But when we originally get a list of objects, should we have to loop through each and call GetEtagFor() on them? Doesn't seem right...
Bob,
No, UseOptimisticConcurrency is something that you need to setup when you open the session.
And NO, a single session per the entire application is the wrong thing to do. See this article for more details on session management:
http://archive.msdn.microsoft.com/mag200912NHibernate
It talks about NHibernate, but the session management parts applies to ravendb as well.
Disclaimer: This is not a recommended approach, and in fact bad practice to open an IDocumentSession that will live as long as the client app lives. For an alternate solution, see the answer posted here: RavenDB Catch 22 - Optimistic Concurrency AND Seeing Changes from Other Clients.
It looks like I got optimistic concurrency working, so I thought I'd post this to help any else.
First, in DataAccessLayerBase, I initialize the DocumentStore and IDocumentSession. These will be open and used as long as the client app is running.
public abstract class DataAccessLayerBase
{
protected static DocumentStore Database { get; private set; }
protected static IDocumentSession Session { get; private set; }
static DataAccessLayerBase()
{
if (Database != null) { return; }
Database = GetDatabase();
Session = GetSession();
}
private static DocumentStore GetDatabase()
{
string databaseUrl = ConfigurationManager.AppSettings["databaseUrl"];
DocumentStore documentStore = new DocumentStore();
try
{
documentStore.Url = databaseUrl;
documentStore.Initialize();
}
catch
{
documentStore.Dispose();
throw;
}
return documentStore;
}
private static IDocumentSession GetSession()
{
IDocumentSession session = Database.OpenSession();
session.Advanced.UseOptimisticConcurrency = true;
return session;
}
}
Next, when retrieving the data, use the existing session:
public class CustomVariableGroupData : DataAccessLayerBase, ICustomVariableGroupData
{
public IEnumerable<CustomVariableGroup> GetAll()
{
return Session.Query<CustomVariableGroup>();
}
}
Finally, when saving, get the eTag and save.
public class GenericData : DataAccessLayerBase, IGenericData
{
public void Save<T>(T objectToSave)
{
Guid eTag = (Guid)Session.Advanced.GetEtagFor(objectToSave);
Session.Store(objectToSave, eTag);
Session.SaveChanges();
}
}
If another instance of the UI is running, and changes the object, a concurrency exception will occur. And that's what we wanted.
I just looked at the title of this post again, and realized this doesn't answer the question of how to set concurrency in the server config file. However, since it can now be set once in the data layer, that's good enough for me.

Creating an Update method in my nHibernate repository

My current repository looks like:
public class Repository<T> : IRepository<T>
{
public ISession Session
{
get
{
return SessionProvider.GetSession();
}
}
public T GetById(int id)
{
return Session.Get<T>(id);
}
public ICollection<T> FindAll()
{
return Session.CreateCriteria(typeof(T)).List<T>();
}
public void Add(T t)
{
Session.Save(t);
}
public void Remove(T t)
{
Session.Delete(t);
}
}
the interface:
public interface IRepository<T>
{
T GetById(int id);
ICollection<T> FindAll();
void Add(T entity);
void Remove(T entity);
}
How would I create an update method?
Do I have to call flush or transaciton commit to write to the database?
I have tried updating an object, and calling saveorupdate(); but it didn't change the value in the db.
You don't.
The repository pattern means: "emulate a normal collection for persistence usage"
With normal collection, I mean something like List<T>
When you do this:
var list = new List<User>();
list.Add(myUser);
myUser.MyProperty = newValue;
MyProperty for the user in both the list and the myUser variable are set to newValue, because it's the same reference. It would be rather strange to do this instead:
var list = new List<User>();
list.Add(myUser);
myUser.MyProperty = newValue;
list.Update(myUser);
Repository should behave the same as other collections, like list. When you need an explicit update, please do not call it a repository. Collections do not work like that.
To answer your question: Yes you have to commit a transaction or flush the session to change persist the changes in the session in the database. It might be better to make a unit of work responsible for this, instead of the repository, and inject the unit of work into the repository, instead of the session.
We don't actually have an Add method on our repositories. Instead we just have a Save method (not in love with the name). The method does a Session.SaveOrUpdate() which works fine since we have unsaved-value specified on our definitions.
In terms of seeing actual changes to the database this doesn't occur until you close your session. This saves NHibernate from doing any work until it absolutely has to.
The only reason you would ever need to flush is if you wanted to get an id back from your database but it's for this reason that identity isn't preferred NHibernate Avoid Identity Generator When Possible

NHibernate: sometimes I want one item, other times I want them all

I have a database schema that stores one "Page" with many "Revisions". Like a simple wiki.
90% of the time when I load a page, I am just interested in the latest revision. However, sometimes I want all revisions.
With NHibernate I can map the Page to the Revisions, and tell it to lazy-load. However, when I access the latest revision, it will load all other revisions - a big waste of I/O.
My page class currently resembles:
public class Page
{
public Page()
{
Revisions = new HashedSet<Revision>();
}
public virtual ISet<Revision> Revisions { get; private set; }
public virtual Revision LatestRevision
{
get { return Revisions.OrderByDescending(x => x.Revised).FirstOrDefault(); }
}
public virtual Revision Revise()
{
var revision = new Revision();
// ...
revision.Entry = this;
revision.Revised = DateTime.UtcNow;
Revisions.Add(revision);
return revision;
}
}
How would I model this such that the LatestRevision is automatically loaded when the Page is loaded, but the other revisions are lazy-loaded if, for instance, I attempted to iterate them?
I would particularly like a solution that works with LINQ to NHibernate, but using ICriteria (or even SQL if I have to) is good enough.
I'm tackling a similar problem as well.
What about mapping exactly as you have it there. The LatestRevision property could be mapped as a one-to-one mapping to the revisions table and the revisions would be as you've already got it. You would have to have a setter (probably make it private) and manage the relationship in the revise method.
One problem would be that the the LatestRevision would still be in the set of revisions.
I've also come across a post by Ayende which uses the formula attribute for the property, I've never used it but looks like it might fit the bill.
You could use a derived property in your mapping file (rather than performing the logic in the property). It might look something like this:
<property name="LatestRevision"
forumla="select top r.f1, r.f2, r.etc from Revisions order by revised desc"
type="Revision" />
For more info on this approach search for 'nhibernate derived properties'.
https://www.hibernate.org/hib_docs/nhibernate/1.2/reference/en/html_single/
Add a LatestRevision column (maintain it) and map to that.
It will save you a lot of headaches.
I ended up going with the solution from here:
Partially Populate Child Collection with NHibernate
My page now has these properties:
public virtual Revision CurrentRevision
{
get
{
return _revision ?? Revisions.OrderByDescending(x => x.Revised).FirstOrDefault();
}
set { _revision = value; }
}
public virtual ISet<Revision> Revisions { get; private set; }
The loading code is:
public Page GetPage(string name)
{
var entryHash = (Hashtable)_session.CreateCriteria<Page>("page")
.Add(Restrictions.Eq("page.Name", name))
.CreateCriteria("Revisions", "rev")
.AddOrder(Order.Desc("rev.Revised"))
.SetMaxResults(1)
.SetResultTransformer(Transformers.AliasToEntityMap)
.UniqueResult();
var page = (Page)entryHash["page"];
page.LatestRevision = (Revision)entryHash["rev"];
return page;
}
NHProf shows this as the only query being executed now, which is perfect:
SELECT top 1 this_.Id as Id3_1_,
this_.Name as Name3_1_,
this_.Title as Title3_1_,
rev1_.Id as Id0_0_,
rev1_.Body as Body0_0_,
rev1_.Revised as Revised0_0_,
....
FROM [Page] this_
inner join [Revision] rev1_
on this_.Id = rev1_.PageId
WHERE this_.Name = 'foo' /* #p0 */
ORDER BY rev1_.Revised desc
What the problem to have LatestRevision property and corresponding column in Page table?
public class Page
{
public Page()
{
Revisions = new HashedSet<Revision>();
}
public virtual ISet<Revision> Revisions { get; private set; } // lazy="true"
public virtual Revision LatestRevision { get; private set; } // lazy="false"
public virtual Revision Revise()
{
var revision = new Revision();
// ...
revision.Entry = this;
revision.Revised = DateTime.UtcNow;
Revisions.Add(revision);
LatestRevision = revision; // <- there you have latest revision
return revision;
}
}

Can I flush my NHibernate session and get a new session without committing the transaction?

I'm using Castle ActiveRecord for persistence, and I'm trying to write a base class for my persistence tests which will do the following:
Open a transaction for each test case and roll it back at the end of the test case, so that I get a clean DB for each test case without me having to rebuild the schema for each test case.
Provide the ability to flush my NHibernate session and get a new one in the middle of a test, so that I know that my persistence operations have really hit the DB rather than just the NHibernate session.
In order to prove that my base class (ARTestBase) is working, I've come up with the following sample tests.
[TestFixture]
public class ARTestBaseTest : ARTestBase
{
[Test]
public void object_created_in_this_test_should_not_get_committed_to_db()
{
ActiveRecordMediator<Entity>.Save(new Entity {Name = "test"});
Assert.That(ActiveRecordMediator<Entity>.Count(), Is.EqualTo(1));
}
[Test]
public void object_created_in_previous_test_should_not_have_been_committed_to_db()
{
ActiveRecordMediator<Entity>.Save(new Entity {Name = "test"});
Assert.That(ActiveRecordMediator<Entity>.Count(), Is.EqualTo(1));
}
[Test]
public void calling_flush_should_make_nhibernate_retrieve_fresh_objects()
{
var savedEntity = new Entity {Name = "test"};
ActiveRecordMediator<Entity>.Save(savedEntity);
Flush();
// Could use FindOne, but then this test would fail if the transactions aren't being rolled back
foreach (var entity in ActiveRecordMediator<Entity>.FindAll())
{
Assert.That(entity, Is.Not.SameAs(savedEntity));
}
}
}
Here is my best effort at the base class. It correctly implements Flush(), so the third test case passes. However it does not rollback the transactions, so the second test fails.
public class ARTestBase
{
private SessionScope sessionScope;
private TransactionScope transactionScope;
[TestFixtureSetUp]
public void InitialiseAR()
{
ActiveRecordStarter.ResetInitializationFlag();
ActiveRecordStarter.Initialize(typeof (Entity).Assembly, ActiveRecordSectionHandler.Instance);
ActiveRecordStarter.CreateSchema();
}
[SetUp]
public virtual void SetUp()
{
transactionScope = new TransactionScope(OnDispose.Rollback);
sessionScope = new SessionScope();
}
[TearDown]
public virtual void TearDown()
{
sessionScope.Dispose();
transactionScope.Dispose();
}
protected void Flush()
{
sessionScope.Dispose();
sessionScope = new SessionScope();
}
[TestFixtureTearDown]
public virtual void TestFixtureTearDown()
{
SQLiteProvider.ExplicitlyDestroyConnection();
}
}
Note that I'm using a custom SQLite provider with an in-memory database. My custom provider, taken from this blog post, keeps the connection open at all times to maintain the schema. Removing this and using a regular SQL Server database doesn't change the behaviour.
Is there a way to acheive the required behaviour?
Not too sure about ActiveRecord, but in NHibernate a transaction belongs to a session, not the otherway round.
If you've used ADO.Net a lot, this will make more sense, as to create an IDbTransaction you need to use the connection. ActiveRecord's TransactionScope (and NHibnerate's ITransaction) essentially wrap an IDbTransaction, so you need to create the SessionScope before the TransactionScope.
What you might also find (depending on if you're using NHibernate 1.2 GA or NHibernate 2.*, and what FlushMode your SessionScope has) is that your call to FindAll() may cause the session to flush anyway, as NHibernate will realise that it can't retrieve the correct data without actioning the last call to Save.
This all said and done, have you tried using SessionScope.Flush() instead of creating a new SessionScope?
Using SessionScope.Flush() makes my third test fail. As I understand it, Flush() executes the SQL to push my records into the DB, but does not evict objects from the session. That fits with what you say about FindAll() causing a flush.
What I really want is SessionScope.Flush() (to synchronise state of DB with session) plus SessionScope.EvictAll() (to ensure I get fresh objects in subsequent queries). My new SessionScope() was an attempt to simulate EvictAll().
Your comments about the session enclosing the transaction rather than the other way round did give me an idea. I'm not sure how kosher it is to create a new SessionScope inside a TransactionScope inside a flushed SessionScope, and expect it to participate in the transaction, but it seems to work:
public abstract class ARTestBase
{
private SessionScope sessionScope;
private TransactionScope transactionScope;
private bool reverse;
private IList<SessionScope> undisposedScopes;
[TestFixtureSetUp]
public void InitialiseAR()
{
ActiveRecordStarter.ResetInitializationFlag();
ActiveRecordStarter.Initialize(typeof (Entity).Assembly, ActiveRecordSectionHandler.Instance);
ActiveRecordStarter.CreateSchema();
InitialiseIoC();
undisposedScopes = new List<SessionScope>();
}
[SetUp]
public virtual void SetUp()
{
sessionScope = new SessionScope();
transactionScope = new TransactionScope(OnDispose.Rollback);
transactionScope.VoteRollBack();
base.CreateInstanceUnderTest();
reverse = false;
}
[TearDown]
public virtual void TearDown()
{
if (reverse)
{
sessionScope.Dispose();
transactionScope.Dispose();
}
else
{
transactionScope.Dispose();
sessionScope.Dispose();
}
}
[TestFixtureTearDown]
public virtual void TestFixtureTearDown()
{
foreach (var scope in undisposedScopes)
{
scope.Dispose();
}
SQLiteProvider.ExplicitlyDestroyConnection();
}
protected void Flush()
{
reverse = true;
sessionScope.Flush();
undisposedScopes.Add(sessionScope);
sessionScope = new SessionScope();
}
}
On further thought, this won't allow you to flush more than once in each test case. I think I can handle that by tracking the scopes more carefully. I might look into it later.