I have ended up doing my own DuplicateEntityChecker and have changed ComponentRegistrar to use that instead of SharpArch one.
My class looks like this.
using NHibernate;
using SharpArch.Core.DomainModel;
using System.Reflection;
using System;
using NHibernate.Criterion;
using SharpArch.Core;
using SharpArch.Core.PersistenceSupport;
using System.Linq;
using SharpArch.Data.NHibernate;
namespace your.namespace.Data.NHibernate
{
public class EntityDuplicateChecker : IEntityDuplicateChecker
{
/// <summary>
/// Provides a behavior specific repository for checking if a duplicate exists of an existing entity.
/// </summary>
public bool DoesDuplicateExistWithTypedIdOf<IdT>(IEntityWithTypedId<IdT> entity) {
Check.Require(entity != null, "Entity may not be null when checking for duplicates");
ISession session = GetSessionFor(entity);
FlushMode previousFlushMode = session.FlushMode;
// We do NOT want this to flush pending changes as checking for a duplicate should
// only compare the object against data that's already in the database
session.FlushMode = FlushMode.Never;
Criteria = session.CreateCriteria(entity.GetType())
.Add(Expression.Not(Expression.Eq("Id", entity.Id)))
.SetMaxResults(1);
AppendSignaturePropertyCriteriaTo<IdT>(Criteria, entity);
bool doesDuplicateExist = Criteria.List().Count > 0;
session.FlushMode = previousFlushMode;
return doesDuplicateExist;
}
public ICriteria Criteria { get; protected set; }
private void AppendSignaturePropertyCriteriaTo<IdT>(ICriteria criteria, IEntityWithTypedId<IdT> entity) {
foreach (PropertyInfo signatureProperty in entity.GetSignatureProperties()) {
Type propertyType = signatureProperty.PropertyType;
object propertyValue = signatureProperty.GetValue(entity, null);
if (propertyType.IsEnum) {
criteria.Add(
Expression.Eq(ResolvePropertyName(string.Empty, signatureProperty), (int)propertyValue));
}
else if (propertyType.GetInterfaces()
.Any(x => x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEntityWithTypedId<>))) {
AppendEntityCriteriaTo<IdT>(criteria, signatureProperty, propertyValue);
}
else if (propertyType == typeof(DateTime)) {
AppendDateTimePropertyCriteriaTo(criteria, string.Empty, signatureProperty, propertyValue);
}
else if (propertyType == typeof(String)) {
AppendStringPropertyCriteriaTo(criteria, string.Empty, signatureProperty, propertyValue);
}
else if (propertyType.IsValueType) {
AppendValuePropertyCriteriaTo(criteria, string.Empty, signatureProperty, propertyValue);
}
else if(typeof(ValueObject).IsAssignableFrom(propertyType)) {
AppendValueObjectSignaturePropertyCriteriaTo(criteria, signatureProperty.Name + ".", propertyValue as ValueObject);
}
else {
throw new ApplicationException("Can't determine how to use " + entity.GetType() + "." +
signatureProperty.Name + " when looking for duplicate entries. To remedy this, " +
"you can create a custom validator or report an issue to the S#arp Architecture " +
"project, detailing the type that you'd like to be accommodated.");
}
}
}
private void AppendValueObjectSignaturePropertyCriteriaTo(ICriteria criteria, string parentPropertyName, ValueObject valueObject)
{
if(valueObject == null)
return;
foreach (PropertyInfo signatureProperty in valueObject.GetSignatureProperties())
{
Type propertyType = signatureProperty.PropertyType;
object propertyValue = signatureProperty.GetValue(valueObject, null);
if (propertyType.IsEnum)
{
criteria.Add(
Expression.Eq(ResolvePropertyName(parentPropertyName, signatureProperty), (int)propertyValue));
}
else if (propertyType == typeof(DateTime))
{
AppendDateTimePropertyCriteriaTo(criteria, parentPropertyName, signatureProperty, propertyValue);
}
else if (propertyType == typeof(String))
{
AppendStringPropertyCriteriaTo(criteria, parentPropertyName, signatureProperty, propertyValue);
}
else if (propertyType.IsValueType)
{
AppendValuePropertyCriteriaTo(criteria, parentPropertyName, signatureProperty, propertyValue);
}
else if (typeof(ValueObject).IsAssignableFrom(propertyType))
{
AppendValueObjectSignaturePropertyCriteriaTo(criteria, ResolvePropertyName(parentPropertyName, signatureProperty), propertyValue as ValueObject);
}
else
{
throw new ApplicationException("Can't determine how to use " + valueObject.GetType() + "." +
signatureProperty.Name + " when looking for duplicate entries. To remedy this, " +
"you can create a custom validator or report an issue to the S#arp Architecture " +
"project, detailing the type that you'd like to be accommodated.");
}
}
}
private void AppendStringPropertyCriteriaTo(ICriteria criteria, string parentPropertyName,
PropertyInfo signatureProperty, object propertyValue)
{
var propertyName = ResolvePropertyName(parentPropertyName, signatureProperty);
if (propertyValue != null)
{
criteria.Add(
Expression.InsensitiveLike(propertyName, propertyValue.ToString(), MatchMode.Exact));
}
else
{
criteria.Add(Expression.IsNull(propertyName));
}
}
private void AppendDateTimePropertyCriteriaTo(ICriteria criteria, string parentPropertyName,
PropertyInfo signatureProperty, object propertyValue)
{
var propertyName = ResolvePropertyName(parentPropertyName, signatureProperty);
if ((DateTime) propertyValue > UNINITIALIZED_DATETIME)
{
criteria.Add(Expression.Eq(propertyName, propertyValue));
}
else
{
criteria.Add(Expression.IsNull(propertyName));
}
}
private void AppendValuePropertyCriteriaTo(ICriteria criteria, string parentPropertyName,
PropertyInfo signatureProperty, object propertyValue) {
var propertyName = ResolvePropertyName(parentPropertyName, signatureProperty);
if (propertyValue != null) {
criteria.Add(Expression.Eq(propertyName, propertyValue));
}
else {
criteria.Add(Expression.IsNull(propertyName));
}
}
private static void AppendEntityCriteriaTo<IdT>(ICriteria criteria,
PropertyInfo signatureProperty, object propertyValue) {
if (propertyValue != null) {
criteria.Add(Expression.Eq(signatureProperty.Name + ".Id",
((IEntityWithTypedId<IdT>)propertyValue).Id));
}
else {
criteria.Add(Expression.IsNull(signatureProperty.Name + ".Id"));
}
}
private static string ResolvePropertyName(string parentPropertyName, PropertyInfo signatureProperty)
{
if (string.IsNullOrEmpty(parentPropertyName))
return signatureProperty.Name;
if (parentPropertyName.EndsWith(".") == false)
parentPropertyName += ".";
return string.Format("{0}{1}", parentPropertyName, signatureProperty.Name);
}
private static ISession GetSessionFor(object entity)
{
var factoryKey = SessionFactoryAttribute.GetKeyFrom(entity);
return NHibernateSession.CurrentFor(factoryKey);
}
private readonly DateTime UNINITIALIZED_DATETIME = default(DateTime);
}
}
Hope this helps anyone with the same problem.