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.
Related
In my domain I have something called Project which basically holds a lot of simple configuration propeties that describe what should happen when the project gets executed. When the Project gets executed it produces a huge amount of LogEntries. In my application I need to analyse these log entries for a given Project, so I need to be able to partially successively load a portion (time frame) of log entries from the database (Oracle). How would you model this relationship as DB tables and as objects?
I could have a Project table and ProjectLog table and have a foreign key to the primary key of Project and do the "same" thing at object level have class Project and a property
IEnumerable<LogEntry> LogEntries { get; }
and have NHibernate do all the mapping. But how would I design my ProjectRepository in this case? I could have a methods
void FillLog(Project projectToFill, DateTime start, DateTime end);
How can I tell NHibernate that it should not load the LogEntries until someone calls this method and how would I make NHibernate to load a specifc timeframe within that method?
I am pretty new to ORM, maybe that design is not optimal for NHibernate or in general? Maybe I shoul design it differently?
Instead of having a Project entity as an aggregate root, why not move the reference around and let LogEntry have a Product property and also act as an aggregate root.
public class LogEntry
{
public virtual Product Product { get; set; }
// ...other properties
}
public class Product
{
// remove the LogEntries property from Product
// public virtual IList<LogEntry> LogEntries { get; set; }
}
Now, since both of those entities are aggregate roots, you would have two different repositories: ProductRepository and LogEntryRepository. LogEntryRepository could have a method GetByProductAndTime:
IEnumerable<LogEntry> GetByProductAndTime(Project project, DateTime start, DateTime end);
The 'correct' way of loading partial / filtered / criteria-based lists under NHibernate is to use queries. There is lazy="extra" but it doesn't do what you want.
As you've already noted, that breaks the DDD model of Root Aggregate -> Children. I struggled with just this problem for an absolute age, because first of all I hated having what amounted to persistence concerns polluting my domain model, and I could never get the API surface to look 'right'. Filter methods on the owning entity class work but are far from pretty.
In the end I settled for extending my entity base class (all my entities inherit from it, which I know is slightly unfashionable these days but it does at least let me do this sort of thing consistently) with a protected method called Query<T>() that takes a LINQ expression defining the relationship and, under the hood in the repository, calls LINQ-to-NH and returns an IQueryable<T> that you can then query into as you require. I can then facade that call beneath a regular property.
The base class does this:
protected virtual IQueryable<TCollection> Query<TCollection>(Expression<Func<TCollection, bool>> selector)
where TCollection : class, IPersistent
{
return Repository.For<TCollection>().Where(selector);
}
(I should note here that my Repository implementation implements IQueryable<T> directly and then delegates the work down to the NH Session.Query<T>())
And the facading works like this:
public virtual IQueryable<Form> Forms
{
get
{
return Query<Form>(x => x.Account == this);
}
}
This defines the list relationship between Account and Form as the inverse of the actual mapped relationship (Form -> Account).
For 'infinite' collections - where there is a potentially unbounded number of objects in the set - this works OK, but it means you can't map the relationship directly in NHibernate and therefore can't use the property directly in NH queries, only indirectly.
What we really need is a replacement for NHibernate's generic bag, list and set implementations that knows how to use the LINQ provider to query into lists directly. One has been proposed as a patch (see https://nhibernate.jira.com/browse/NH-2319). As you can see the patch was not finished or accepted and from what I can see the proposer didn't re-package this as an extension - Diego Mijelshon is a user here on SO so perhaps he'll chime in... I have tested out his proposed code as a POC and it does work as advertised, but obviously it's not tested or guaranteed or necessarily complete, it might have side-effects, and without permission to use or publish it you couldn't use it anyway.
Until and unless the NH team get around to writing / accepting a patch that makes this happen, we'll have to keep resorting to workarounds. NH and DDD just have conflicting views of the world, here.
I might be in the process of trying something (bad), just to see what I come up with.
For starters, we built an application in a DDD fashion - our opinion. The design is "classic" DDD, meaning we have repositories for aggregate roots.
We do have a base Entity where we override Equals, GetHashCode, etc. Our entities are only logical deleted ie we use an IsActive field.
As ORM we use NHibernate >3.
The thing I'd like to try: I'd like to be able to remove an entity from a list inside an aggregate root with a syntax like this:
aggregateRoot.Entities.Remove(entity);
In the persistence layer, the default NHibernate behaviour for "entity" ("entity" has a back-reference to "aggregateRoot") is to update the "entity" with a NULL on "aggregateRoot" column. What we actually want to do is this:
repository.Delete(entity);
which just marks "entity" as being inactive while "entity" remains in the "aggregateRoot" 's collection.
Most probably my idea is just plain stupid (as I said once again), but I'd like to try to teach NHibernate that "entity" should not be updated with a null reference to "aggregateRoot", just make it inactive. Why? Because I want to use the repository explicitly where it is required.
What I am asking is if this is achievable through NHibernate Interceptors; I haven't tried them yet and I want to prioritize my backlog.
Why don't you just implement a Delete method on your entities? You may hide it behind a core interface. The advantage is a completely persistence ignorant implementation which doesn't require NH to exist.
class Root
{
// ...
public void Remove(Entity entity)
{
IRootManaged managed = (IRootManaged)entity
managed.Delete();
}
}
class Entity : IRootManaged
{
// ...
public bool IsDeleted { get; private set; }
public void IRootManaged.Delete()
{
this.IsDeleted = true;
}
}
Sorry if I missed the point here ...
In a classic DDD, aggregateRoot.Entities.Remove(entity); is a bad practice anyway. You'd better create a method on the root entity, say RemoveEntity(Entity e), and there you will encapsulate the Delete mechanism in which you will set the Entity.IsActive to false.
Take a look at this: http://ayende.com/blog/4157/avoid-soft-deletes
I been reading nhibernate for beginners 3.0 and been reading about common mistakes(a few of them I been making)
I am wondering what are some strategies for making one or more records readonly. Right now I get all the rows back and loop through them making them readonly through session.Readonly().
I like in the book that they do with fluent
class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
SchemaAction.None();
ReadOnly();
// Mappings
}
}
What I am not sure is what happens if I need these records to not be Readonly? To me this means I have to make this exact same mapping minus those 2 lines of code.
So I would like to do this and have ReadonlyEntityMap and EntityMap but I rather not have to duplicate all the settings twice.
Anyone have ideas on how do to this? Or better ideas for readonly?
Using ReadOnly() on your mapping will disable any updates back to the database when you modify the properties of the object.
If you want to apply some sort of ReadOnly and Writeable strategy I would suggest using a Fluent Convention and a marker interface or similar.
You can also apply ReadOnly() to a property if you want to disable writes on just that one property. I use that technique to avoid writing back to Computed Columns in SQL Server.
Something like this:
public interface IReadOnly{} //Mark entities with this interface
public class ReadOnlyConvention
: IClassConvention, IClassConventionAcceptance<IClassInspector>
{
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.EntityType Is IReadOnly);
}
public void Apply(IClassInstance instance)
{
instance.ReadOnly();
}
}
Update:
If you want to do you time conversion thing I would suggest creating an IUserType for your DateTime object which does the conversion to the user time without modifying the underlying data.
Date Time Support in NHibernate
Custom IUserType for DateTime
It depends on what you are trying to achieve. If you are trying to optimize memory consumption you should consider improving your session management strategy or using other performance improvement techniques. You can for example:
Clear the session that loaded your objects and it will release all the memory allocated by first level cache. The objects can later be reattached (Merged) to a new session if needed. This way you don't need objects to be readonly in the first place.
Evict some of the objects when you no longer need them. This will keep the other objects in the first level cache.
Use IStatelessSession which does not use 1st level cache at all.
Again, the answer depends on your application architecture.
Everyone knows that there is cache in session.
This cache generally could be cleared by 2 methods:
Session.Evict
Session.Clear
Second method removes all cache not only for single entry.
I have business method. It receives id of large object (from aspx site) or sometimes several ids. And do native sql operation in database (using sql-query with complex logic to not load all data in C#). Then I need to invalidate cache. So every potential load of object goes without cache direct from database.
Unfortunately evict accepting only objects. Also its implementation DefaultEvictEventListener has clear separation in code path - separate for proxy and not proxied classes. I have tried simply to create entity, fill id manually and pass it to Evict. This will not works. As I understand Evict by not proxied class use GetHashCode to find and remove object from cache. So if I am not overriding it it will not works. I have a lot native sql batch operations so overriding all GetHashcode in all entities objects will create a lot of work. Also I am not sure is this case removes proxies from cache or no.
Update: As far as I have tried for me overriding GetHashCode also not helped. StatefulPersistenceContext.RemoveEntry not found entity because it uses RuntimeHelpers.GetHashCode. So this solution is not even possible
Using sources of NHibernate I have produced following solution:
public static class NHSessionHelper: DefaultEvictEventListener
public static void RemoveEntityFromCache(this ISession session, Type type, object entityId)
{
ISessionImplementor sessionImpl = session.GetSessionImplementation();
IPersistenceContext persistenceContext = sessionImpl.PersistenceContext;
IEntityPersister persister = sessionImpl.Factory.GetEntityPersister(type.FullName);
if (persister == null)
{
return;
}
EntityKey key = new EntityKey(entityId, persister, sessionImpl.EntityMode);
persistenceContext.RemoveProxy(key);
object entity = persistenceContext.RemoveEntity(key);
if (entity != null)
{
EntityEntry e = persistenceContext.RemoveEntry(entity);
DoEvict(entity, key, e.Persister, (IEventSource)sessionImpl);
}
}
It just uses part of NHibenate implementation. But it seem to me not good idea to duplicate code. May be anyone have other ideas?
If you are sure that the object is in the cache, Session.Get(id) will not hit the db. It's probably easiest to do that and then Evict the object you get back:
Model m = Session.Get(id);
Session.Evict(m);
Edit
It is not clear to me if you are talking about the first-level cache or the second-level cache. The above will evict something from the first-level cache. To evict from the second-level cache, use the evict method on SessionFactory.
Edit in response to comment
In that case, you might try Session.Load:
Model m = Session.Load(id);
Session.Evict(m);
If m is in the cache, Session.Load will return the instance which you can evict. If not it returns a proxy (no db hit). My tests suggest that Session.Evict will not throw if you try to evict the proxy, so this should work.
It sounds like you could use a Stateless session for this and not bother with cache at all.
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.