-
I am using NHibernate and ValueObject of CSharpFunctionalExtensions. I have defined the following ValueObject for my domain logic. However, I have a problem trying to persist it in the database with NHibernate (C#): public class Keycode : ValueObject
{
public Keycode(string inputCode)
{
if (inputCode.Length != 8)
throw new ArgumentOutOfRangeException(nameof(inputCode),
inputCode,
"Code must have a length of 8 characters,"
);
YearDigits = inputCode.Substring(0, 2);
RandomCodePart = inputCode.Substring(1, 6);
}
public string YearDigits { get; }
public string RandomCodePart { get; }
protected override IEnumerable<IComparable> GetEqualityComponents() =>
new[] { ToString() };
public override string ToString() =>
$"{YearDigits}{RandomCodePart}";
} It is used as usual in an ORM-mapped class: public class SomeTable
{
public virtual Keycode? Keycode { get; set; }
// ....
} Error:
Mapping defined as: Map(x => x.Keycode).Column(nameof(SomeTable.Keycode)); I just want it to persist it as a string, i.e. call I already saw if you use EFCore you can persist ObjectValue's like this and there is this Stackoverflow question, but both do not really help. Also I found out an That means I have a mapping from entity tom (View)Model, which causes issues here. Trying to change the mappingI tried telling Map(x => x.Keycode).Column(nameof(SomeTable.Keycode)).CustomType<string>(); This apparently works for saving the object in the database, but when reading it, NHibernate cannot cast it from
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Use the public abstract class UserType<TUserType, TUnderlyingType> : IUserType
{
public new bool Equals(object x, object y)
{
if (x == null && y == null)
return true;
if (x == null || y == null)
return false;
return x.Equals(y);
}
public object NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner)
{
object value = GetNHibernateType().NullSafeGet(rs, names, session, owner);
return WrapValue(value);
}
private object WrapValue(object value)
{
if (value == null)
{
if (IsNullable() == false)
throw new Exception("Value can't be null");
return Maybe<TUserType>.None;
}
TUserType userType = Read((TUnderlyingType)value);
if (IsNullable())
return (Maybe<TUserType>)userType;
return userType;
}
public void NullSafeSet(DbCommand cmd, object value, int index, ISessionImplementor session)
{
object unwrapedValue = UnwrapValue(value);
GetNHibernateType().NullSafeSet(cmd, unwrapedValue, index, session);
}
private object UnwrapValue(object value)
{
if (IsNullable() && value is not IMaybe<TUserType>)
throw new Exception("Nullable value must be Maybe");
if (IsNullable() == false && value is IMaybe<TUserType>)
throw new Exception("Non-nullable value must not be Maybe");
if (value is IMaybe<TUserType> maybeInterface)
{
var maybe = (Maybe<TUserType>)maybeInterface;
return maybe.Map(Write).GetValueOrDefault();
}
return Write((TUserType)value);
}
public int GetHashCode(object x) => x?.GetHashCode() ?? 0;
public object DeepCopy(object value) => value;
public object Replace(object original, object target, object owner) => original;
public object Assemble(object cached, object owner) => cached;
public object Disassemble(object value) => value;
public SqlType[] SqlTypes => new[] { new SqlType(GetDbType()) };
public Type ReturnedType => GetReturnedType();
public bool IsMutable => false;
private Type GetReturnedType()
{
if (IsNullable() == false)
return typeof(TUserType);
return typeof(Maybe<>).MakeGenericType(typeof(TUserType));
}
protected abstract DbType GetDbType();
protected abstract NullableType GetNHibernateType();
protected abstract bool IsNullable();
protected abstract TUserType Read(TUnderlyingType primitive);
protected abstract TUnderlyingType Write(TUserType complex);
}
// Usage:
public sealed class EmailUserType : UserType<Email, string>
{
protected override DbType GetDbType() => DbType.String;
protected override NullableType GetNHibernateType() => NHibernateUtil.String;
protected override bool IsNullable() => false;
protected override Email Read(string primitive) => Email.Create(primitive).GetValueOrThrow();
protected override string Write(Email complex) => complex.Value;
}
public sealed class ContactStatusUserType : UserType<ContactStatus, int>
{
protected override DbType GetDbType() => DbType.Int32;
protected override NullableType GetNHibernateType() => NHibernateUtil.Int32;
protected override bool IsNullable() => false;
protected override ContactStatus Read(int primitive) => ContactStatus.FromId(primitive).GetValueOrThrow();
protected override int Write(ContactStatus complex) => complex.Id;
}
public sealed class UuidUserType : UserType<Uuid, Guid>
{
protected override DbType GetDbType() => DbType.Guid;
protected override NullableType GetNHibernateType() => NHibernateUtil.Guid;
protected override bool IsNullable() => false;
protected override Uuid Read(Guid primitive) => Uuid.Create(primitive.ToString());
protected override Guid Write(Uuid complex) => complex.Value;
}
public sealed class ProductUserType : UserType<Product, string>
{
protected override DbType GetDbType() => DbType.String;
protected override NullableType GetNHibernateType() => NHibernateUtil.String;
protected override bool IsNullable() => false;
protected override Product Read(string primitive) => Product.FromId(primitive).GetValueOrThrow();
protected override string Write(Product complex) => complex.Id;
}
public sealed class NullableStringUserType : UserType<string, string>
{
protected override DbType GetDbType() => DbType.String;
protected override NullableType GetNHibernateType() => NHibernateUtil.String;
protected override bool IsNullable() => true;
protected override string Read(string primitive) => primitive;
protected override string Write(string complex) => complex;
}
public sealed class VaultPermissionsUserType : UserType<VaultPermissions, int>
{
protected override DbType GetDbType() => DbType.Int32;
protected override NullableType GetNHibernateType() => NHibernateUtil.Int32;
protected override bool IsNullable() => false;
protected override VaultPermissions Read(int primitive) => VaultPermissions.FromId(primitive).GetValueOrThrow();
protected override int Write(VaultPermissions complex) => complex.Id;
}
public sealed class NullableDateTimeOffsetUserType : UserType<DateTimeOffset, DateTimeOffset?>
{
protected override DbType GetDbType() => DbType.DateTimeOffset;
protected override NullableType GetNHibernateType() => NHibernateUtil.DateTimeOffset;
protected override bool IsNullable() => true;
protected override DateTimeOffset Read(DateTimeOffset? primitive) => primitive.Value;
protected override DateTimeOffset? Write(DateTimeOffset complex) => complex;
}
// Mapping:
public sealed class Email : SimpleValueObject<string>
{
private Email(string value)
: base(value)
{
}
public static Result<Email> Create(string email)
{
if (string.IsNullOrWhiteSpace(email))
return Errors.Common.ValueIsRequired();
email = email.ToLower().Trim();
if (email.Length > 200)
return Errors.Common.ValueIsInvalid();
if (!Regex.IsMatch(email, @"^(.+)@(.+)$"))
return Errors.Common.ValueIsInvalid();
return Result.Ok(new Email(email));
}
}
public class User : Entity
{
public virtual Email Email { get; }
}
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
Table("dbo.[User]");
Map(x => x.Email);
}
}
|
Beta Was this translation helpful? Give feedback.
Use the
IUserType
feature from NHibernate. Here are some examples from one of my projects.