Hello everyone!
I want to log property changes on a few of my objects...
I have the following object graph:
Customer
BillingAccount
Device
DeviceHistory
Customers have a set of BillingAccounts
BillingAccounts have a set of Devices
Device has a set of DeviceHistories
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="OmegaWeb.domain.Customer, OmegaWeb" table="customer" optimistic-lock="version">
<id name="Id" column="CustomerId" type="Int32">
<generator class="native" />
</id>
<version name="Version" type="Int32" />
<property name="CustomerName" />
<property name="Notes" />
<property name="Created" />
<many-to-one name="CreatedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="CreatedBy" />
</many-to-one>
<property name="LastModified" />
<many-to-one name="LastModifiedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="LastModifiedBy" />
</many-to-one>
<property name="Deactivated" />
<many-to-one name="DeactivatedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="DeactivatedBy" />
</many-to-one>
<set name="BillingAccountSet" table="billingaccount" cascade="all-delete-orphan" inverse="true" lazy="true">
<key>
<column name="CustomerId" />
</key>
<one-to-many class="OmegaWeb.domain.BillingAccount, OmegaWeb" />
</set>
<set name="DeviceGroupSet" table="devicegroup" cascade="all-delete-orphan" inverse="true" lazy="false">
<key>
<column name="CustomerId" />
</key>
<one-to-many class="OmegaWeb.domain.DeviceGroup, OmegaWeb" />
</set>
<set name="CustomerAdminUserSet" table="CustomerAdminUser" cascade="all-delete-orphan" inverse="true" lazy="true">
<key>
<column name="CustomerId" />
</key>
<one-to-many class="OmegaWeb.domain.CustomerAdminUser, OmegaWeb" />
</set>
<set name="AlertRecipientSet" table="AlertRecipient" cascade="delete-orphan" inverse="true" lazy="true">
<key>
<column name="CustomerId" />
</key>
<one-to-many class="OmegaWeb.domain.AlertRecipient, OmegaWeb" />
</set>
</class>
</hibernate-mapping>
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="OmegaWeb.domain.BillingAccount, OmegaWeb" table="billingaccount" optimistic-lock="version">
<id name="Id" column="BillingAccountId">
<generator class="native" />
</id>
<version name="Version" type="Int32" />
<many-to-one name="Customer" cascade="all" class="OmegaWeb.domain.Customer, OmegaWeb" lazy="false">
<column name="CustomerId" />
</many-to-one>
<many-to-one name="CreatedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="CreatedBy" />
</many-to-one>
<property name="BillingAccountNumber" />
<property name="Notes" />
<property name="Created" />
<property name="LastModified" />
<many-to-one name="LastModifiedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="LastModifiedBy" />
</many-to-one>
<property name="Deactivated" />
<many-to-one name="DeactivatedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="DeactivatedBy" />
</many-to-one>
<set name="DeviceSet" table="device" cascade="all-delete-orphan" inverse="true" lazy="true">
<key>
<column name="BillingAccountId" />
</key>
<one-to-many class="OmegaWeb.domain.Device, OmegaWeb" />
</set>
</class>
</hibernate-mapping>
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="OmegaWeb.domain.Device, OmegaWeb" table="device" optimistic-lock="version">
<id name="Id" column="DeviceId" type="Int32">
<generator class="native" />
</id>
<version name="Version" type="Int32" />
<many-to-one name="BillingAccount" cascade="all" class="OmegaWeb.domain.BillingAccount, OmegaWeb" lazy="false">
<column name="BillingAccountId" />
</many-to-one>
<property name="Password" />
<property name="MaxAllowedUsage" />
<property name="Notes" />
<property name="Created" />
<many-to-one name="CreatedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="CreatedBy" />
</many-to-one>
<property name="LastModified" />
<many-to-one name="LastModifiedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="LastModifiedBy" />
</many-to-one>
<property name="Deactivated" />
<many-to-one name="DeactivatedBy" cascade="none" class="OmegaWeb.domain.AuthUser, OmegaWeb" lazy="false">
<column name="DeactivatedBy" />
</many-to-one>
<one-to-one name="LastSession" class="OmegaWeb.domain.LastSession, OmegaWeb" constrained="false" />
<set name="DeviceHistorySet" table="devicehistory" cascade="all-delete-orphan" inverse="true" lazy="false">
<key>
<column name="DeviceId" />
</key>
<one-to-many class="OmegaWeb.domain.DeviceHistory, OmegaWeb" />
</set>
<set name="SessionSet" table="session" cascade="save-update" lazy="true">
<key column="DeviceId" />
<one-to-many class="OmegaWeb.domain.Session, OmegaWeb" />
</set>
<set name="BillingSet" table="billing" cascade="all-delete-orphan" inverse="true" lazy="true">
<key>
<column name="DeviceId" />
</key>
<one-to-many class="OmegaWeb.domain.Billing, OmegaWeb" />
</set>
<set name="DeviceGroupSet" table="devicegroupdevice" cascade="save-update" lazy="true">
<key column="DeviceId" />
<many-to-many class="OmegaWeb.domain.DeviceGroup, OmegaWeb" column="DeviceGroupId" />
</set>
</class>
</hibernate-mapping>
I've implemented the IInterceptor interface as follows:
Code:
public bool OnFlushDirty(object entity, object id, object[] state, object[] previousState, string[] propertyNames, IType[] types)
{
bool modified = false;
if (entity is Domain)
{
AuthUser authUser = (AuthUser)HttpContext.Current.Session[AuthUser.SESSION_KEY];
if (authUser == null)
{
throw new Exception("Security Circumvention - AuthUser cannot be null in Audit Interceptor");
}
Hashtable indexes = GetAuditPropertyIndexes(propertyNames);
int index;
index = (int)indexes["lastmodified"];
state[index] = DateTime.Now;
index = (int)indexes["lastmodifiedby"];
state[index] = authUser;
modified = true;
}
else
{
modified = false;
}
//Handle logging
Type type = entity.GetType();
foreach (Attribute attr in type.GetCustomAttributes(true))
{
LogPropertyChanges logger = attr as LogPropertyChanges;
if (logger != null)
{
AuthUser authUser = (AuthUser)HttpContext.Current.Session[AuthUser.SESSION_KEY];
string HibernateSessionId = HttpContext.Current.Items["HibernateSessionId"] as string;
if (authUser == null || HibernateSessionId == null)
{
throw new Exception("Security Circumvention - Neither AuthUser nor SaveSessionId cannot be null in Logging Interceptor");
}
string[] PropertiesToLog = logger.Attributes;
Hashtable indexes = GetLogPropertyIndexes(PropertiesToLog, propertyNames);
string typeName = type.Name;
ChangeSet changeSet = new ChangeSet();
changeSet.ChangeValueSet = new HashedSet<ChangeValue>();
changeSet.AuthUser = authUser;
changeSet.ChangeDate = DateTime.Now;
changeSet.HibernateSessionId = HibernateSessionId;
changeSet.ModifiedId = int.Parse(id.ToString());
changeSet.ModifiedTable = typeName;
DAOGeneric<ChangeSet> daoChangeSet = new DAOGeneric<ChangeSet>();
foreach (DictionaryEntry entry in indexes)
{
ChangeValue changeValue = new ChangeValue();
changeValue.ColumnName = entry.Key.ToString();
object oldValue = previousState[(int)entry.Value];
object newValue = state[(int)entry.Value];
if (newValue != oldValue)
{
changeValue.OldValue = oldValue != null ? oldValue.ToString() : null;
changeValue.NewValue = newValue != null ? newValue.ToString() : null;
changeValue.ChangeSet = changeSet;
changeSet.ChangeValueSet.Add(changeValue);
}
}
daoChangeSet.SaveChangeSet(changeSet);
}
}
return modified;
}
I also have two logging objects, one called ChangeSet, the other ChangeValue
ChangeSet has a set of ChangeValues
Lets say I modify a DeviceHistory record, then call Save on Device, I get some duplication.. my database looks something like:
1997 2008-03-29 17:27:55.000 Device 5924 84 5ce4b2bd-2772-47bc-ae86-6eab4fa5b478
1998 2008-03-29 17:27:55.000 DeviceHistory 5924 84 5ce4b2bd-2772-47bc-ae86-6eab4fa5b478
1999 2008-03-29 17:27:55.000 Device 5924 84 5ce4b2bd-2772-47bc-ae86-6eab4fa5b478
2000 2008-03-29 17:27:55.000 DeviceHistory 5924 84 5ce4b2bd-2772-47bc-ae86-6eab4fa5b478
42 1997 MaxAllowedUsage 500000 564898000
43 1997 Notes NULL blahksdlfjkl
44 1998 Deactivated NULL 3/29/2008 5:27:55 PM
45 1999 MaxAllowedUsage 500000 564898000
46 1999 Notes NULL blahksdlfjkl
47 2000 Deactivated NULL 3/29/2008 5:27:55 PM
48 2000 DeactivatedBy NULL OmegaWeb.domain.AuthUser
I know this is a lot.. but any suggestions on how to do this? Am I going down the wrong path?