NHibernate变更审计和组件

时间:2014-02-14 16:59:02

标签: nhibernate

我们的系统必须提供仪表板,以便用户明确地将数据更改从环境发布到另一个环境。我们查看了NH Evers,但我们需要将许多特定于域的内容添加到体系结构中。我们已经成功地使用NHibernate的事件模型来跟踪和记录我们系统中的状态变化(到另一个表),但最近偶然发现了与组件有关的障碍。当触发IPostInsertEventListener和IPostUpdateEventListener时,它会发布一个表示实体当前状态的值数组。在更新的情况下,它还发布表示先前状态的数组。我们正在使用这些数组将“之前”和“之后”状态保存到表中。当属性是Component时,实际值(区域中的项)是组件实例本身 - 即无类型复杂对象。如果没有交叉引用元模型映射器并反映它以获取单个值,那么如何获得构成组件的实际值?我能够得到映射到Component成员的列名,但不能得到之前和之后的值。

我一直在挖掘NH源代码,而我并没有找到如何提取这些值,但显然NH知道如何在内部执行此操作,因为它能够正确地发布sql。这是我目前拥有的代码的修改/详细版本,突出了这个问题:

        public static RowChangedReport Load(IChangeTrackable trackable, object entityId, AbstractEntityPersister persister, object[] state, object[] oldState)
    {
        var report = new RowChangedReport
            {
                Entity = trackable,
                EntityTypeFullName = persister.EntityName,
                TableName = new TableName(persister.GetTableName()),
                EntityId = entityId,
            };

        var authContext = AuthenticationContext.Current;

        if (authContext != null)
            report.SecurityContextUserId = authContext.UserId;

        if (persister.PropertyNames != null && state != null)
        {
            report.ChangedType = oldState == null ? RowChangedTypes.New : RowChangedTypes.Modified;

            for (var index = 0; index < persister.PropertyNames.Length; index++)
            {
                var propertyName = persister.PropertyNames[index];

                IType propertyType = persister.PropertyTypes[index];

                if (!propertyType.IsCollectionType)
                {
                    AddColumnChangeReport(persister, state, oldState, index, propertyName, report);
                }
            }
        }

        report.FinalizeState();

        return report;
    }

    private static void AddColumnChangeReport(AbstractEntityPersister persister, object[] state, object[] oldState, int index, string propertyName, RowChangedReport report)
    {
        var currentValue = state[index];

        // for simple properties, this is always a single element array
        // for components, this is an array with an element for each member on the component - i.e. how the component is mapped
        string[] columns = persister.GetPropertyColumnNames(propertyName);

        var previousValue = oldState == null ? null : oldState[index];

        if (!Equals(currentValue, previousValue))
        {
            if (report.ChangedType == RowChangedTypes.Modified && propertyName == IsActivePropertyName)
                report.FlagAsDeleted();

            foreach (var column in columns)
            {
                // if this is a component, both the currentValue and the previousValue are complex objects
                // need to have a way to get the actual member value per column!
                report.AddChange(new ColumnChangedReport(report, propertyName, column, previousValue, currentValue));
            }
        }
    }

1 个答案:

答案 0 :(得分:2)

好的,几个小时后读了NH代码,我偶然发现了Tuplizers并将它们追溯到属性类型。因此解决方案非常简单 - 对于组件,您需要检测它们,转换为ComponentType类型,然后向ComponentType询问属性值。这里有一些代码对我有用:

        public static RowChangedReport Load(IChangeTrackable trackable, object entityId, AbstractEntityPersister persister, object[] state, object[] oldState)
    {
        var report = new RowChangedReport
            {
                Entity = trackable,
                EntityTypeFullName = persister.EntityName,
                TableName = new TableName(persister.GetTableName()),
                EntityId = entityId,
            };

        var authContext = AuthenticationContext.Current;

        if (authContext != null)
            report.SecurityContextUserId = authContext.UserId;

        if (persister.PropertyNames != null && state != null)
        {
            report.ChangedType = oldState == null ? RowChangedTypes.New : RowChangedTypes.Modified;

            for (var index = 0; index < persister.PropertyNames.Length; index++)
            {
                var propertyName = persister.PropertyNames[index];

                IType propertyType = persister.PropertyTypes[index];

                if (!propertyType.IsCollectionType)
                {
                    AddColumnChangeReport(persister, state, oldState, index, propertyName, propertyType, report);
                }
            }
        }

        report.FinalizeState();

        return report;
    }

    private static void AddColumnChangeReport(AbstractEntityPersister persister, object[] state, object[] oldState, int index, string propertyName, IType propertyType, RowChangedReport report)
    {
        var currentValue = state[index];

        string[] columns = persister.GetPropertyColumnNames(propertyName);

        var previousValue = oldState == null ? null : oldState[index];

        if (!Equals(currentValue, previousValue))
        {
            if (report.ChangedType == RowChangedTypes.Modified && propertyName == IsActivePropertyName)
                report.FlagAsDeleted();

            if (propertyType.IsComponentType)
            {
                ComponentType component = (ComponentType)propertyType;

                object[] componentCurrentValues = null;

                if (currentValue != null)
                    componentCurrentValues = component.GetPropertyValues(currentValue, EntityMode.Poco);

                object[] componentPreviousValues = null;

                if (currentValue != null)
                    componentPreviousValues = component.GetPropertyValues(previousValue, EntityMode.Poco);

                if ((componentCurrentValues != null && componentCurrentValues.Length != columns.Length) ||
                    (componentPreviousValues != null && componentPreviousValues.Length != columns.Length))
                    throw new ConventionViolationException(GetComponentArraysExceptionMessage(persister, propertyName, columns, componentPreviousValues, componentCurrentValues));

                for (int i = 0; i < columns.Length; i++)
                {
                    var column = columns[i];
                    var componentPreviousValue = componentPreviousValues == null ? null : componentPreviousValues[i];
                    var componentCurrnetValue = componentCurrentValues == null ? null : componentCurrentValues[i];

                    report.AddChange(new ColumnChangedReport(report, propertyName, column, componentPreviousValue, componentCurrnetValue));
                }
            }
            else
            {
                if (columns.Length > 1)
                    throw new ConventionViolationException("Expected only component properties to have multiple columns.  Property '{0}' on entity {1} is violating that assumption.".FormatWith(propertyName, persister.EntityName));

                report.AddChange(new ColumnChangedReport(report, propertyName, columns[0], previousValue, currentValue));
            }
        }
    }