我试图找出为什么我收到一个无效的转换异常与NHibernate用下面的代码:NHibernate的无效的转换与定制PrimitiveType
AutoMap.Source(new TypeSource(recordDescriptors))
.Conventions.Add(new EncryptedStringConvention());
。
[AttributeUsage(AttributeTargets.Property)]
public class EncryptedDbString : Attribute { }
。
public class EncryptedStringConvention : IPropertyConvention {
public void Apply(IPropertyInstance instance) {
if (!instance.Property.MemberInfo.IsDefined(typeof(EncryptedDbString), false))
return;
var propertyType = instance.Property.PropertyType;
var generic = typeof(EncryptedStringType<>);
var specific = generic.MakeGenericType(propertyType);
instance.CustomType(specific);
}
}
。
[Serializable]
public class EncryptedStringType<T> : PrimitiveType
{
const int MaxStringLen = 1000000000;
public EncryptedStringType() : this(new StringSqlType(MaxStringLen)) { }
public EncryptedStringType(SqlType sqlType) : base(sqlType) { }
public override string Name {
get { return typeof(T).Name; }
}
public override Type ReturnedClass {
get { return typeof(T); }
}
public override Type PrimitiveClass {
get { return typeof(T); }
}
public override object DefaultValue {
get { return default(T); }
}
public override object Get(IDataReader rs, string name) {
return Get(rs, rs.GetOrdinal(name));
}
public override void Set(IDbCommand cmd, object value, int index) {
if (cmd == null) throw new ArgumentNullException("cmd");
if (value == null) {
((IDataParameter)cmd.Parameters[index]).Value = null;
}
else {
((IDataParameter)cmd.Parameters[index]).Value = Encryptor.EncryptString((string)value);
}
}
public override object Get(IDataReader rs, int index) {
if (rs == null) throw new ArgumentNullException("rs");
var encrypted = rs[index] as string;
if (encrypted == null) return null;
return Encryptor.DecryptString(encrypted);
}
public override object FromStringValue(string xml) {
// i don't think this method actually gets called for string (i.e. non-binary) storage
throw new NotImplementedException();
}
public override string ObjectToSQLString(object value, Dialect dialect) {
// i don't think this method actually gets called for string (i.e. non-binary) storage
throw new NotImplementedException();
}
}
POCO的作品:
public class someclass {
public virtual string id {get;set;}
[EncryptedDbString]
public virtual string abc {get;set;}
}
POCO失败:
public class otherclass {
public virtual string id {get;set;}
[EncryptedDbString]
public virtual Guid def {get;set;}
}
这一切automapped用流利。
Guid类型和字符串类型都是SQL数据库中的nvarchar(500)。
如前所述,第一POCO工作正常,加密/解密如预期,但第二POCO失败,这就是我在日志中看到:
NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(对象实体,对象[]值) {“无效角色(检查为属性的类型不匹配的映射);的otherclass设定器”}
注意,第二POCO对象正常工作与nHib如果删除EncryptedDbString attibute,即,它将Guid保存为nvarchar没有问题。
显然这里的问题是,它是一个GUID作为字符串的情况下工作,但我不希望它保持为GUID不是字符串中的代码,我不能在这里看到故障点。
好像我缺少一些小东西。我想我错过了泛型的东西,但我只找到了代码片段,而不是像这样的完整示例。
编辑:
好了,我想通了,我认为这是因为
Get(IDataReader rs, int index)
没有返回的Guid对象。
所以我想你可以在EncryptedStringType Get/Set方法中进行序列化/反序列化。在获取(),你可以改变为:
if (typeof(T) == typeof(string))
return decrypted;
var obj = JsonConvert.DeserializeObject(decrypted);
return obj;
,但似乎太可怕了,特别是如果你有现有的数据迁移。
我不希望存储的东西,无论是二进制,作为球队希望能够检查/测试/手动审核通过的列进行加密SQL(这是与文本明显的,但不是二进制)。
在我的POCO字符串支持字段将GUID转换为字符串,并通过简单的get/set方法返回可能是最好的选择,但我不知道如何通过解决方案自动映射或如何混乱它是?
你试过类似[this](http://stackoverflow.com/a/393787/497356)吗?它可能工作。此外,你的意思是“这看起来很可怕,特别是如果你有现有的数据迁移?” –
其实你是对的。我以为我不想在列中出现不必要的json,但我可以只有一个字符串,但是无论如何都会看到它的加密,因为它不会在SQL查询中手动显示/查询无论如何。感谢您的评论,因为它确实消除了对json的需求,并且看到了我的结果答案:) – jimasp
顺便说一句,如果有人读到这个确实想要例如将更复杂的对象存储为二进制而不是字符串,请参阅:http://blog.muonlab.com/2010/04/20/encrypting-data-with-nhibernate/ – jimasp