我们的系统必须提供仪表板,以便用户明确地将数据更改从环境发布到另一个环境。我们查看了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));
}
}
}
答案 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));
}
}
}