此问题与Mapping interface or abstract class component有关 我也试图映射一个声明为接口的组件,但我正在使用内置的按代码映射/一致的方法。
假设我有一个实体Login
(C#):
public class Login
{
public virtual int Id { get; set; }
public virtual string UserName { get; set; }
public virtual IPassword Password { get; set; }
}
我想抽象出密码的存储方式,所以我定义了一个简单的接口IPassword
public interface IPassword
{
bool Matches(string password);
}
示例实现是HashedPassword
:
public class HashedPassword : IPassword
{
public virtual string Hash { get; set; }
public virtual string Salt { get; set; }
public virtual bool Matches(string password){ /* [snip] */ }
}
我想将Login.Password
映射为一个组件,而不是多对一或一对一的关系。使用XML我会像这样映射:
<?xml version="1.0"?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="..." namespace="...">
<class name="Login">
<id name="Id">...</id>
<property name="UserName" .../>
<component name="Password" class="HashedPassword">
<property name="Hash" not-null="true" length="32"/>
<property name="Salt" not-null="true" length="32"/>
</component>
</class>
</hibernate-mapping>
这可以按预期工作。
这是我尝试使用NHibernate内置的逐代码映射工具来映射它:
public class LoginMapping : ClassMapping<Login>
{
public LoginMapping()
{
Id(x => x.Id, map => map.Generator(Generators.HighLow));
Property(x => x.UserName, map => map.Length(32));
Component(x => x.Password, comp =>
{
comp.Class<HashedPassword>();
comp.Property("Salt", map => map.Length(32));
comp.Property("Hash", map => map.Length(32));
});
}
}
当我使用此映射时,我得到以下异常:
NHibernate.MappingException:找不到成员。 IPassword类型中不存在成员'Salt'
虽然Salt不是IPassword
的成员,但是我使用comp.Class<HashedPassword>()
您知道如何在不获取异常的情况下映射此方案吗?
到目前为止,我还没有找到问题本身的解决方案。目前有两种解决方法:
求助于XML映射或FluentNHibernate。这可能仅适用于“有问题”的映射。
使用用户类型代替组件。这就是我现在正在做的事情。我的案例中的类型(散列密码)是不可变的,可以存储为单个列,因此用户类型非常简单。
这是我目前正在使用的用户类型(为了完成)。我使用PBKDF2来创建安全哈希。请注意,在我的应用程序中,所有数据(salt,hash和PBKDF2迭代计数)都存储在HashedPassword的一个属性(简称为Hash
)中。
public abstract class ImmutableValue<T> : IUserType where T : class
{
public abstract SqlType[] SqlTypes { get; }
public virtual Type ReturnedType
{
get { return typeof (T); }
}
public bool IsMutable
{
get { return false; }
}
bool IUserType.Equals(object x, object y)
{
return InternalEquals(x, y);
}
protected virtual bool InternalEquals(object x, object y)
{
return Equals(x, y);
}
public virtual int GetHashCode(object x)
{
return x == null ? 0 : x.GetHashCode();
}
public virtual object NullSafeGet(IDataReader rs, string[] names, object owner)
{
return Load(rs, names, owner);
}
protected abstract T Load(IDataReader rs, string[] names, object owner);
public virtual void NullSafeSet(IDbCommand cmd, object value, int index)
{
Save(cmd, (T) value, index);
}
protected abstract void Save(IDbCommand cmd, T value, int index);
public virtual object DeepCopy(object value)
{
return value;
}
public virtual object Replace(object original, object target, object owner)
{
return original;
}
public virtual object Assemble(object cached, object owner)
{
return cached;
}
public virtual object Disassemble(object value)
{
return value;
}
protected void SetParameter(IDbCommand cmd, int index, object value)
{
var parameter = (IDataParameter) cmd.Parameters[index];
var parameterValue = value ?? DBNull.Value;
parameter.Value = parameterValue;
}
}
public class HashedPasswordType : ImmutableValue<HashedPassword>
{
public override SqlType[] SqlTypes
{
get { return new SqlType[] {SqlTypeFactory.GetString(HashedPassword.ContentLength)}; }
}
protected override HashedPassword Load(IDataReader rs, string[] names, object owner)
{
var str = NHibernateUtil.String.NullSafeGet(rs, names[0]) as string;
return HashedPassword.FromContent(str);
}
protected override void Save(IDbCommand cmd, HashedPassword value, int index)
{
SetParameter(cmd, index, value == null ? null : value.Hash);
}
}
所需的映射比较简单:
Property(x => x.Password, map =>
{
map.Type<HashedPasswordType>();
map.NotNullable(true);
});
答案 0 :(得分:1)
由于我还没有找到使用组件映射的解决方案,但我现在将添加变通方法作为答案。
可能的解决方法:
FluentNH中所需映射的示例。具体类型作为泛型参数传入。
Component<HashedPassword>(x => x.Password, comp =>
{
comp.Map(x => x.Hash);
comp.Map(x => x.Salt);
});
问题末尾给出了用户类型的示例。在实际示例中,我使用PBKDF2来创建安全哈希。请注意,在问题中显示的用户类型中,所有数据(salt,hash和PBKDF2迭代计数)都存储在 one 列中,以保持类型简单。
我目前的结论是,按代码映射根本不支持我要求的内容。我现在正在考虑从映射到代码转移到FluentNH。
如果有与此问题相关的新闻,我会相应地更新答案和/或提问。
答案 1 :(得分:0)
我对代码映射并不熟悉,但在使用Fluent NHibernate时,我已经取得了成功
Map(x => (x as HashedPassword).Hash)
你可以尝试
comp.Property(x => (x as HashedPassword).Hash)