NHibernate,试图将对象作为字符串存储在数据库中

NHibernate, trying to store an object as a string in DB

我想要实现的是以某种形式序列化一个对象并将其存储在数据库中,然后重新创建它。

这是结构:我正在使用 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/using-conversion-operators 进行转换

 public class Order : AggregateRoot
 {
    private string _cityList;
    public virtual CityList CityList { get => (CityList)_cityList; }
 }

 public class CityList : ValueObject
 {
     private string _cities { get; }

     public CityList(string cities)
     {
         _cities = cities;
     }

     public static explicit operator CityList(string cityList)
     {
         return new CityList(cityList);
     }

     public static implicit operator string(CityList cityList)
     {
         return (string)cityList;
     }
  }

这是配置

 mapping
        .Map(Reveal.Member<Order>("CityList"))
        .CustomType("string")
        .Column("CityList");

这就是我得到的

An unhandled exception occurred while processing the request. InvalidCastException: Unable to cast object of type CityList to type System.String

NHibernate.Type.AbstractStringType.Set(DbCommand cmd, object value, int index, ISessionImplementor session)PropertyValueException: Error dehydrating property value for CityList Command.Stack.Adapters.Base.UnitOfWork.IUnitOfWork.CommitAsync(CancellationToken cancellationToken)

为什么我会得到这个?以及如何解决?

需要说明的是,这不是您问题的确切答案,但希望提供替代方法来实现相同的目的。我们创建了名为 JsonMappable Type 的自定义 IUserType。这种类型将保存到DB中的数据序列化和反序列化为JSON字符串,需要覆盖NullSafeGet和Set,然后在映射文件中很容易这样映射,

        Property(p => p.UserAvailability, m =>
        {
            m.Type<JsonMappableType<UserAvailability>>();
        });

这个link提供了如何实现iusertype

How to implement correctly IUserType?

您可以使用 IUserType 接口。

[Serializable]
public class AsClobStringUserType<T> : IUserType
{
    public AsClobStringUserType()
    {
        
    }

    /// <summary>
    /// The type returned by NullSafeGet()
    /// </summary>
    Type IUserType.ReturnedType => typeof(T);
    bool IUserType.IsMutable => false;
    SqlType[] IUserType.SqlTypes => new SqlType[] { new StringClobSqlType() };

    /// <summary>
    /// Used for casching, the object is immutable, just return 
    /// </summary>
    /// <param name="cached"></param>
    /// <param name="owner"></param>
    /// <returns></returns>
    object IUserType.Assemble(object cached, object owner)
    {
        return cached;
    }

    /// <summary>
    /// Deep copy the Translation by creating a new instance with the same contents
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    object IUserType.DeepCopy(object value)
    {
        if (value == null) return null;

        if (value.GetType() != typeof(T)) return null;

        string jsonString = JsonSerializationService.Instance.Serialize((T)value);

        return JsonSerializationService.Instance.Deserialize<T>(jsonString);
    }

    /// <summary>
    /// Used for casching, the object is immutable, just return 
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    object IUserType.Disassemble(object value)
    {
        return value;
    }

    /// <summary>
    /// Use json string to check the equality
    /// </summary>
    /// <param name="object1"></param>
    /// <param name="object2"></param>
    /// <returns></returns>
    bool IUserType.Equals(object object1, object object2)
    {
        if (ReferenceEquals(object1, null) && ReferenceEquals(object2, null)) return true;

        if (ReferenceEquals(object1, null) || ReferenceEquals(object2, null)) return false;

        return JsonSerializationService.Instance.Serialize((T)object1) == JsonSerializationService.Instance.Serialize((T)object2);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="object"></param>
    /// <returns></returns>
    int IUserType.GetHashCode(object @object)
    {
        if (@object != null && @object.GetType() == typeof(T))
        {
            return JsonSerializationService.Instance.Serialize((T)@object).GetHashCode();
        }

        return @object.GetHashCode();
    }

    #region Serialization
    /// <summary>
    /// Retrieve an instance of the mapped class from.
    /// </summary>
    /// <remarks>
    /// Should handle possibility of null values.
    /// </remarks>
    /// <param name="owner">the entity to work with. (serialized object)</param>
    object IUserType.NullSafeGet(DbDataReader rs, string[] names, ISessionImplementor session, object owner)
    {
        /// => Translation should be stored in single column 
        if (names.Length != 1) throw new InvalidOperationException("Translation should be stored in single column.");

        string value = rs[names[0]] as string;

        if (!string.IsNullOrWhiteSpace(value)) return JsonSerializationService.Instance.Deserialize<T>(value);

        return null;
    }

    /// <summary>
    /// Write an instance of the mapped class to a prepared statement.
    /// </summary>
    /// <remarks>
    /// Should handle possibility of null values.
    /// </remarks>
    /// <param name="cmd">the object to be serialized and written to persistence store.</param>
    /// <param name="value">the object to be serialized and written to persistence store.</param>
    void IUserType.NullSafeSet(DbCommand cmd, object value, int index, ISessionImplementor session)
    {
        DbParameter parameter = cmd.Parameters[index];

        if (value == null)
        {
            parameter.Value = DBNull.Value;
        }
        else
        {
            parameter.Value = JsonSerializationService.Instance.Serialize(value);
        }
    }
    #endregion

    /// <summary> 
    /// As our object is immutable just return the original
    /// </summary>
    /// <param name="original"></param>
    /// <param name="target"></param>
    /// <param name="owner"></param>
    /// <returns></returns>
    object IUserType.Replace(object original, object target, object owner)
    {
        return original;
    }
}