How do you audit changed collections in NHibernate? - nhibernate

I've been using the IPreUpdateEventListener for auditing entities, in particular using the FindDirty method to find the changed properties:
public bool OnPreUpdate(PreUpdateEvent updateEvent)
{
int[] dirtyFieldIndices = updateEvent.Persister.FindDirty(updateEvent.State, updateEvent.OldState, updateEvent.Entity, updateEvent.Session);
// Get changed property names and audit...
}
This works fine for simple properties. However, my entity has a collection property of other entities. One of these entities has changed, and the change gets persisted, but FindDirty does not give me the index of that collection property. Is there any way of getting hold of the changed property in order to audit this change?

I have decided to have a method on my domain objects that receives the OldState collection, and applies its own processing on it, checking each object to see if it has changed.

Related

Setting an entity's ID manually

I'm facing a little issue that I cannot understand here.
Using this chunk of code:
IEntity myEntity = controller.entityFactory.createEntityInstance(MyEntity.class)
myEntity.straightSetProperty(IEntity.ID, "anId")
myEntity.setReferenceProperty(someReference)
I get an "UOW bad usage" error
BAD SESSION USAGE You are modifying an entity ()[MyEntity] that has not been previously merged in the session.
You should 1st merge your entities in the session by using the backendController.merge(...) method.
The property being modified is [referenceProperty].
But when switching the lines it's okay
IEntity myEntity = controller.entityFactory.createEntityInstance(MyEntity.class)
myEntity.setReferenceProperty(someReference)
myEntity.straightSetProperty(IEntity.ID, "anId")
Any idea why i'm facing this issue ?
Jspresso computes the hashcode of an entity based on its id. this hashcode is indirectly used by Jspresso internally to perform some controls and other operations by using it in Hash[Map|Set].
That's why it's mandatory that :
The id is assigned as soon as the entity instance is created and before any setter or operation is performed on the entity.
The id does not change during the lifetime of the entity.
When you call :
IEntity myEntity = entityFactory.createEntityInstance(MyEntity.class)
a generated id is assigned to the entity.
In scenario 1, you first change the id (which breaks the hashcode), and call a setter. Jspresso thinks this entity has not been correctly registered because it cannot retrieve its id from the internal, hashcode based, storage.
In scenario 2, same violation but you call the setter before changing the id. But I suppose that if you call another setter afterwards, it will fail the same way.
The solution is to use the other signature of the entityFactory create method that allows to pass an id as parameter, e.g.
IEntity myEntity = entityFactory.createEntityInstance(MyEntity.class, "anId")
myEntity.setReferenceProperty(someReference)
which will immediately assign your id to the entity and perform all necessary operations afterwards.

NHibernate SaveOrUpdate() improperly working with GeneratedBy.Assigned()

NHibernate's documentations specifically says
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.
But I am using assigned identifiers and session.SaveOrUpdate() and I'm not getting an error/warning of any sort.
What am I missing? Did they change how SaveOrUpdate behaves and now it can be used with assigned identifiers?
I am also using Fluent NHibernate's auto mapping.
Here's the code:
public class MyIDConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.GeneratedBy.Assigned();
}
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure().Database(SQLiteConfiguration.Standard.UsingFile("testDB.db"))
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Product>(cfg).Conventions.Add<MyIDConvention>()
.BuildSessionFactory();
}
I'm expecting an error when I do:
Product myProduct = new Product(presetID);
session.SaveOrUpdate(myProduct);
transaction.Commit();
but nothing happens.
If I had left out the myIDConvention, then I get the error:
NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect).
Can anyone explain to me what is going on?
When using assigned ids NHibernate can determine if the entity is persistent by comparing the id to the unsaved value setting. My understanding is that the unsaved value setting allows SaveOrUpdate to be used and the documentation you referenced is outdated/misleading.
For example, if your id is int the default unsaved-value is 0.
5.1.4
unsaved-value (optional - defaults to a "sensible" value): An
identifier property value that indicates that an instance is newly
instantiated (unsaved), distinguishing it from transient instances
that were saved or loaded in a previous session.

WCF: how to modify registers with different types of operations (update, add, delete...)

Well, in this post, I get to the conclusion that is better to work with services in per call mode, because it's more efficient. This makes me to have data context with a short life, the life of the method that I call.
In this example, I see how to modify data of a list of elements, only is needed to set the state of the entity to modified.
However, how could I do with one operation, modifies and updates, for example?
One case can be this. I have books and authors, and the client application have a view with two datagrids, one for authors and other for books. The user can add authors and modify their information in the first datagrid and do the same with the books in the second datagrid. Also can assign books to their authors.
I have the POCO classes, so I have a author class with a property that is a list of books. Well, I can add books to this list, and then when I call the service method updateAuthors, I only need to use the class of author as parameter, EF knows what to do with the books. It is not needed to pass the book too.
But what happens when in the list of books there are new books and also books that exists but its information is modified?
In the example of the post that I mention in the beginning it says how to do it when all the entity are modify, but if I want to add new registers, I need to set the state to add. So if there is mixed entities, how can I do it? does it exist any pattern or a some way to do this? I have to set the state of all the books? I can know the state of the books, because I use an autonumeric as ID, so if the ID is 0 is a new register, in other case is a modification.
Thanks.
Daimroc.
EDIT: Perhaps I don't be very clear in my question. What I want to know is how I can save the changes of many entities at once. For example, I have the Authors, Books and Custormers. I Add, modify and delete information of many of them. In my WCF client I have method to save changes of Authors, other method to save changes of books and other method to save changes of Customers.
How my service is per call, I need to make three calls, one for each type of entities, and this are independents. However, if I use directly entity framework, I can do many changes to many type of entities and I only need to call savechanges once, it make all the work. How can I do the same with WCF and POCO entities?
I am reading about self tracking entities, buy I have the same problem, I can use Applychanges(entity) but if I am not wrong, it applies changes only to one entity. do I need to call N times if I did changes to many entities?
Thanks.
Not sure if this will answer your question, but here is my suggestion:
Manage the state on your POCO entities by using flags (IsNew, IsDirty, IsDeleted);
When you pass the POCO entities to the object context, use the ObjectStateManager to change the attached entity state;
Recursively loop through the list of children entities and apply the same approach.
EDIT:
The following code is the AuthorStateManager class:
public partial class AuthorStateManager : IStateManager<Author, Context>
{
private IStateManager<Book, Context> _BookStateManager = new BookStateManager();
public void ChangeState(Author m, Context ctx)
{
if (m == null) return;
ctx.Authors.Attach(m);
if (m.IsDeleted)
{
ctx.ObjectStateManager.ChangeObjectState(m, System.Data.EntityState.Deleted);
}
else
{
if (m.IsNew)
{
ctx.ObjectStateManager.ChangeObjectState(m, System.Data.EntityState.Added);
}
else
{
if (m.IsDirty)
{
ctx.ObjectStateManager.ChangeObjectState(m, System.Data.EntityState.Modified);
}
}
}
SetRelationsState(m, ctx);
}
private void SetRelationsState(Author m, Context ctx)
{
foreach (Book varBook in m.Books)
{
_BookStateManager.ChangeState(varBook, ctx);
}
}
}
where Authors is the ObjectSet, m is a POCO entity of type Author, ctx is the object context, and SetRelationsState is the method that loops through all the children state managers to update their state.
After changing the state, in my repository object I call ctx.SaveChanges(). This is the Update method in AuthorRepository class:
public Author Update(Author m, bool commit)
{
_AuthorStateManager.ChangeState(m, _ctx);
if (commit)
{
_ctx.SaveChanges();
}
return m;
}
_BookStateManager is a private member of BookStateManager type which modifies the Book state in its own ChangeState() method.
I suggest you make the State Manager classes implement an interface called IStateManager, which has the ChangeState() method.
It seems a bit convoluted, but it gets easier if you generate code for these classes.
If you want to perform multiple actions in a single service call, then the action to take need to move from being a method call to an object. For example, you might have a InsertCustomerAction class which has a Customer instance tied to it. All of these actions would have a base class (Action) and your WCF method would take in a collection of Action instances.

How do I "deactivate" an entity instead of deleting it using NHibernate?

I'm not so sure if this is really trivial to do and I'm just over-complicating stuff, but I've been thinking about this for the good part of the past hour or so.
So I have entities. Hence, NHibernate. What I want to do is to only "deactivate" entities whenever I want to "delete" them, instead of actually removing them physically from the database. (Just cause we don't want to be really deleting records from our data store).
All my entities inherit from a BaseEntity class with a BaseEntity.Active property.
What I've got running right now is something like the following in the entity class' mapping file:
<sql-delete>
UPDATE SomeEntityTable SET Active = 0 WHERE Id = ?
</sql-delete>
This works fine, except that I'll have to inject that, customized with the table name, into every single HBM mapping file for every single entity (we're not implementing the BaseEntity inheritance in any subclassing strategy).
As you can see, that can be a bit menial. The coding would be tedious, the maintenance horrendous, and declaring the table name twice in the same mapping file just rubs me the wrong way.
What I was playing around earlier was whether or not I could implement an event listener; perhaps OnPreDelete or something, and update the entity's .Active property, like so:
class BaseEventListener : IPreDeleteListener
{
public bool OnPreDelete(PreDeleteEvent #event)
{
BaseEntity be = #event.Entity as BaseEntity;
if (be != null)
be.Active = false;
return false;
}
}
That way, the whole "deactivation" thingy is automated for all entities that support deactivation.
The problem is, I'm thinking that NHibernate would still build a proper DELETE SQL query that will burn my entity from the data store anyway instead of updating the thing, so this'll just be wasted automagic effort.
How should I go about this?
You can use an event listener. You have to add the listener to the configuration as well.
public class SoftDeleteEventListener : DefaultDeleteEventListener
{
protected override void DeleteEntity(IEventSource session, object entity, EntityEntry entityEntry, bool isCascadeDeleteEnabled, IEntityPersister persister, ISet transientEntities)
{
var softDeletable = entity as BaseEntity;
if (softDeletable != null)
{
softDeletable.Active = false;
}
else
{
base.DeleteEntity(session, entity, entityEntry, isCascadeDeleteEnabled, persister, transientEntities);
}
}
}
Since it's pretty clear that you never actually delete your persistent entities (as is the case with most applications), there's is no need to use the Delete method just because it's there.
An alternative approach:
Declare a base entity with an Active property
Set it to false for "delete" use cases. You can even add a Delete method to your base entity that does just that
You can, additionally, create a filter to avoid loading "deleted" entities
Yes, there is some work involved, but in the long run it's for the best, as you'll still have a maintainable, non-hacky implementation.
Some of the burden can be reduced if you use a code+convention-based mapping approach, like ConfORM or Fluent.

Determine if an entity field changed in NHibernate

I have a call that needs to determine if a field has changed. But calling get using that entities id returns the same entity not the prior version.
Entity e = Dao.Get(id);
//At this point e.Field is X
e.Field = y;
Dao.Save(e);
Entity Dao.Get(Guid id)
{
return Session.Get(id);
}
Entity Dao.Save(Entity e)
{
Entity olde = Session.Get(e.Id);
if (e.Field != olde.Field) <--- e.Field == olde.Field so it does not run.
DoBigMethod(e);
return e;
}
How do I handle this situation without adding an onChange method to the Entity class.
You only know one "version" of the entity: the current one. There is actually only one version of the entity. You have it in memory and you already changed it and forgot the previous state.
Call get to see the previous database state is dangerous. If changes are already flushed (NHibernate flushes before queries for instance), you get your changes. If you open another session, you see changes from other transactions.
Are you only interested in one single field? Then you can cache the old value somewhere.
If this wouldn't work, you need to tell me more about the reason why you need to know the previous value of this field.
EDIT:
Some more ideas:
cache the previous state of the field when you get the object, in DAO.Get
implement this property that it sets a flag if it changed.
consider to make this change an explicit operation called by the client, instead of an implicit operation that is called when the flag changes. For instance, if this flag is called "Activated", implement a "Activate" and "Deactivate" method. This methods change that flag and perform the "large set of code". The flag is read-only for the rest of the world.