i have old legacy DB which has dead links in their tables. I have class mapped in nhibernate like this:
<class name="Visible" table="table_visible">
<composite-id>
<key-many-to-one column="object_id" name="ObjectA" />
<key-many-to-one column="sub_object_id" name="SubObject" />
</composite-id>
<property column="visible" name="VisibleRow" />
</class>
and:
public class Visible
{
public virtual ObjectAClass ObjectA { get; set; }
public virtual SubObjectClass SubObject { get; set; }
public virtual bool VisibleRow { get; set; }
public override bool Equals(object obj)
{
var other = ((Visible)obj);
return this.ObjectA.Equals(other.ObjectA) && this.SubObject.Equals(other.SubObject);
}
public override int GetHashCode()
{
return this.ObjectA.GetHashCode() + (this.SubObject != null? this.SubObject.GetHashCode(): 0);
}
}
Now all works fine when all joins in database are correct, but when i find such sub_object_id which doesnt have entity, nhibernate throws me error
No row with the given identifier exists:[SubObject#123]
Is there a way to map composite key so that when its subentity is not found, the whole entity wouldnt be loaded (like with inner join)?
NHibernate v2.0.50727
Following Daniel Schilling idea of fetch Visible entities with a where exists sub-query, found that there is loader element available in mappings.
<class name="ObjectA" table="table_object">
.........
<set name="VisibleList" cascade="all" lazy="false" inverse="true">
<key column="object_id" />
<one-to-many class="Visible" />
<loader query-ref="valid_entities"/>
</set>
</class>
<sql-query name="valid_entities">
<load-collection alias="v" role="ObjectA.VisibleList"/>
SELECT {v.*}
FROM table_visible v
INNER JOIN table_sub_entities e ON e.sub_entity_id=v.sub_entity_id
WHERE v.object_id=?
</sql-query>
And nothing else needed to be changed.
<key-many-to-one column="sub_object_id" name="SubObject" not-found="ignore" />
... may be helpful. From the NHibernate Documentation...
ignore will treat a missing row as a null association
Please be aware of the performance penalty associated with using this option. Whenever NHibernate fetches a Visible entity, it will also have to fetch SubObject. If you don't go ahead and fetch it in your query, this means that NHibernate will be issuing lots of lazy loads.
This doesn't meet your "when its sub-entity is not found, the whole entity wouldn't be loaded" goal. Instead NHibernate would give you an entity with a null sub-entity. If you want that inner-join-like behavior, then I think you would need to fetch your Visible entities with a where exists sub-query to make sure the SubObject actually exists.
The best option would be to fix the data in the database and add a foreign key constraint.
I just ran across this: Relations with not-found="ignore". I promise I'm not copying Ricci's content - I'm writing this from my own experience.
I have an interface like this:
public interface ICoreType {
int TypeID {get;}
}
And an NHibernate class that implements it like this:
public class DueDateType : ICoreType {
public virtual int DueDateTypeID {get;set;}
...
public virtual int TypeID { get{ return this.DueDateTypeID; } }
}
Here's my mapping:
<class name="DueDateType" table="tdfDueDateType" lazy="true" >
<cache usage="read-write" include="all"/>
<id name="DueDateTypeID" column="DueDateTypeID" >
<generator class="identity"/>
</id>
...
</class>
When I get an instance of the object from NHibernate, and then return it outside the scope of the active session it was created in, I can get the value DueDateTypeID just fine, but accessing TypeID throws a LazyInitializationException. There is no mapping for TypeID, as I simply want it to return whatever DueDateTypeID's value is.
Marking the class lazy="false" in my mapping erases the problem. From what I've been reading, that is because there is no proxy generated for a non-lazy mapped class.
I would like to use the lazy loading features of NHibernate in this case; do I need to implement IInterceptor or some such, in order to get NHibernate to treat this property differently?
The problem is that all members of the entity trigger lazy initialization when accessed (except of the id). NH can't know what you actually are doing within the member. When triggering lazy initialization outside of the session, you get an error.
Suggestions:
Try to avoid any access to entities outside the session. It is only safe when turning off lazy loading - which is usually not a way to go.
Don't wrap the id. If you access TypeID, the entity is initialized although it is not necessary, which is bad for performance.
I'm trying to write an audit tracking for Nhibernate that hooks into the PreUpdate event. I have an AuditLogEntry class (when, who, etc), that contains a list of AuditLogEntryDetails (i.e. individual properties that changed). If I isolate the AuditLogEntry class from the entity being audited then my code runs with no errors. However, if I add a list of AuditLogEntry's to the entity being audited then my code throws a
collection [DomainObjects.AuditTracking.AuditLogEntry.Details] was not processed by
flush()
assertion failure when I attempt to save the modified list inside the event listener. This only happens when the audited item already has one (or more) AuditLogEntry instance in the list. If there are no entries then a new list is created and added to the entity being audited and this is fine.
I think by isolating the issue to the above it would appear to be around (lazy) loading the existing list to add the new instance of AuditLogEntry too. However I've been unable to progress any further. Adding 'Lazy="False"' to the list mapping doesn't appear to help. I'm really in the early days of using NHibernate, having borrowed concepts from both the HN 3.0 Cookbook and this blog post. My code is very similar to this, but attempts to add the audit history to the item being audited in a list (and as such I think that I need to also do that in the pre, rather than post update event).
A snap shot of the entity interfaces/classes in question are:
public class AuditLogEntry : Entity
{
public virtual AuditEntryTypeEnum AuditEntryType { get; set; }
public virtual string EntityFullName { get; set; }
public virtual string EntityShortName { get; set; }
public virtual string Username { get; set; }
public virtual DateTime When { get; set; }
public virtual IList<AuditLogEntryDetail> Details { get; set; }
}
public interface IAuditTrackedEntity
{
Guid Id { get; }
IList<AuditLogEntry> ChangeHistory { get; set; }
}
public class AuditTrackedEntity : StampedEntity, IAuditTrackedEntity
{
public virtual IList<AuditLogEntry> ChangeHistory { get; set; }
}
public class LookupValue : AuditTrackedEntity
{
public virtual string Description { get; set; }
}
For the mappings I have:
AuditTrackedEntry.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainObjects" namespace="DomainObjects.AuditTracking">
<class name="AuditLogEntry">
<id name="Id">
<generator class="guid.comb" />
</id>
<version name="Version" />
<property name="AuditEntryType"/>
<property name="EntityFullName"/>
<property name="EntityShortName"/>
<property name="Username"/>
<property name="When" column="`When`"/>
<list name ="Details" cascade="all">
<key column="AuditLogEntryId"/>
<list-index column="DetailsIndex" base="1"/>
<one-to-many class="AuditLogEntryDetail"/>
</list>
</class>
</hibernate-mapping>
lookupvalue.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainObjects" namespace="DomainObjects">
<class name="LookupValue">
<id name="Id">
<generator class="guid.comb" />
</id>
<discriminator type="string">
<column name="LookupValueType" unique-key="UQ_TypeName" not-null="true" />
</discriminator>
<version name="Version" />
<property name="Description" unique-key="UQ_TypeName" not-null="true" />
<property name="CreatedBy" />
<property name="WhenCreated" />
<property name="ChangedBy" />
<property name="WhenChanged" />
<list name ="ChangeHistory">
<key column="EntityId"/>
<list-index column="ChangeIndex" base="1"/>
<one-to-many class="DomainObjects.AuditTracking.AuditLogEntry"/>
</list>
</class>
</hibernate-mapping>
The EventListener PreUpdate event handler calls the follow code:
The lines that cause the problem are commented near the end of the code block
public void TrackPreUpdate(IAuditTrackedEntity entity, object[] oldState, object[] state, IEntityPersister persister, IEventSource eventSource)
{
if (entity == null || entity is AuditLogEntry)
return;
var entityFullName = entity.GetType().FullName;
if (oldState == null)
{
throw new ArgumentNullException("No old state available for entity type '" + entityFullName +
"'. Make sure you're loading it into Session before modifying and saving it.");
}
var dirtyFieldIndexes = persister.FindDirty(state, oldState, entity, eventSource);
var session = eventSource.GetSession(EntityMode.Poco);
AuditLogEntry auditLogEntry = null;
foreach (var dirtyFieldIndex in dirtyFieldIndexes)
{
if (IsIngoredProperty(persister, dirtyFieldIndex))
continue;
var oldValue = GetStringValueFromStateArray(oldState, dirtyFieldIndex);
var newValue = GetStringValueFromStateArray(state, dirtyFieldIndex);
if (oldValue == newValue)
{
continue;
}
if (auditLogEntry == null)
{
auditLogEntry = new AuditLogEntry
{
AuditEntryType = AuditEntryTypeEnum.Update,
EntityShortName = entity.GetType().Name,
EntityFullName = entityFullName,
Username = Environment.UserName,
//EntityId = entity.Id,
When = DateTime.Now,
Details = new List<AuditLogEntryDetail>()
};
//**********************
// The next three lines cause a problem when included,
// collection [] was not processed by flush()
//**********************
if (entity.ChangeHistory == null)
entity.ChangeHistory = new List<AuditLogEntry>();
entity.ChangeHistory.Add(auditLogEntry);
session.Save(auditLogEntry);
}
var detail = new AuditLogEntryDetail
{
//AuditLogEntryId = auditLogEntry.Id,
PropertyName = persister.PropertyNames[dirtyFieldIndex],
OldValue = oldValue,
NewValue = newValue
};
session.Save(detail);
auditLogEntry.Details.Add(detail);
}
session.Flush();
}
As previously stated, in this configuration I get an assertion failure "collection [] was not processed by flush()". If I remove the three lines above and the list mapping in the lookupcode.hmb.xml then everything works as expected, other than the entity being audited no longer contains a reference to it's own audited items.
we were facing very similar problem, exactly same exception, but in different situation. No solution found yet...
We have NH event listener implementing IPreUpdateEventListener and OnPreUpdate method used for audit log. Everything is fine for simple properties updating, dirty checking works well, but there are problems with lazy collections. When updating some object which has lazy collection and accessing any object field in the event listener OnPreUpdate method, the same exception as mentioned above is thrown. When lazy set to false, problem disappears.
So it seems there is some problem with lazy collections (and no influence of collection initialization before saving). Our problem isn't connected with creating new collection items; only reading existing object, only its field accessing from the event listener causes the problem.
So in your case, maybe, lazy set to false only for the associatioon could fix the problem, but on the other hand probably you really want to have the collection to be lazy. So hard to say, if the problem has resolution or IInterceptor have to be used instead.
Ok, I have found your problem, this line is actually causing the problem.
Details = new List<AuditLogEntryDetail>()
You can't initialize an empty collection before you save because the EntityPersister will not persist the collection, but it will error that the collection has not been processed.
Also, once nHibernate calls event listeners, cascades do not work (not sure if this is by design or not). So even though you are adding the detail item to the collection later, you are only calling save on the detail, not the parent, so the change is not propagated. I would recommend re-factoring so that items are completed in this order...
Detail, then save,
AuditLogEntry, then save,
Entity, then update.
I had exactly same problem while using EventListener. I was looping through properties one-by-one to detect changes, that included enumerating collections. However when I added a check for the collection using NHibernateUtil.IsInitialized(collection), problem disappeared. I wouldn't catch-and-ignore the AssertionFailure exception since it might have unknown side-effects.
There's an issue still open to solve this problem. There's a patch at the end of topic that solved it to me.
https://nhibernate.jira.com/browse/NH-3226
I don't like the idea of proxy and lazy loading. I don't need that. I want pure POCO. And I want to control loading associations explicitly when I need.
Here is entity
public class Post
{
public long Id { get; set; }
public long OwnerId { get; set; }
public string Content { get; set; }
public User Owner { get; set; }
}
and mapping
<class name="Post">
<id name="Id" />
<property name="OwnerId" />
<property name="Content" />
<many-to-one name="Owner" column="OwnerId" />
</class>
However if I specify lazy="false" in the mapping, Owner is always eagerly fetched.
I can't remove many-to-one mapping because that also disables explicit loading or a query like
from x in session.Query<Post>()
where x.Owner.Title == "hello"
select x;
I specified lazy="true" and set use_proxy_validator property to false. But that also eager loads Owner.
Is there any way to load only Post entity?
In short, it is not possible with out of box NH. But here is attempt at just, lazy loading without proxies
http://thinkbeforecoding.com/post/2009/02/07/Lazy-load-and-persistence-ignorance
Set the class User to lazy = false on the mapping
<class name="User" table="Users" lazy="false">
Remove this property <property name="OwnerId" />... to get the owner id you can use Owner.Id. This will not trigger a lazy load. Owner will only be loaded if you hit any property besides the id. To make it a flat/simple POCO, you can use projections and ResultTransformers.
Davy Brion - Must Everything be Virtual with NHibernate
I have the following class
public class Person
{
private IList<Person> _children;
public IEnumerable<Person> Children { get; }
public void AddChild(Person child)
{
// Some business logic and adding to the internal list
}
}
What changes would I have to make for NHibenrate to be able to persist the Child collection (apart from making everything virtual, I know that one).
Do I have to add a setter to the children property which does something like a _children.Clear(); _children.AddRange(value). Currently the model expresses my intent quite nicely but I'm not sure how much alteration is need for NH to be able to help me out with persistence.
NHibernate is able to map private fields. Access and naming strategies are discussed in the property section of the reference documentation.
Making your public members virtual is required for proxies to work. These will usually be runtime-generated subclasses of your entity classes.
In this example mapping the field _children will be Children in HQL and Criteria queries.
<class name="Person" table="person">
<bag name="Children" access="field.camelcase-underscore">
<key column="parentid" />
<one-to-many class="Person" />
</bag>
</class>