【发布时间】:2016-12-12 17:56:09
【问题描述】:
我正在尝试使用以下代码找出为什么我会使用 nHibernate 得到一个无效的强制转换异常:
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;}
}
这一切都是用 Fluent 自动映射的。
在SQL数据库中Guid类型和字符串类型都是nvarchar(500)。
如前所述,第一个 POCO 工作正常并按预期加密/解密,但第二个 POCO 失败,这是我在日志中看到的:
NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(对象实体,对象 [] 值) {"Invalid Cast (检查你的映射是否有属性类型不匹配);其他类的设置器"}
请注意,如果我删除 EncryptedDbString 属性,第二个 POCO 对象可以与 nHib 一起正常工作,即将 Guid 保存到 nvarchar 时没有问题。
很明显,这里的问题是它是一个 Guid,因为字符串案例有效,但是 我确实希望它在代码中保留为 Guid 而不是字符串,我看不出有什么意义这里失败了。
好像我错过了一些小东西。我想我在泛型方面遗漏了一些东西,但我只找到了代码 sn-ps,而不是像这样的完整示例。
编辑:
好的,所以我想通了,我认为这是因为
Get(IDataReader rs, int index)
没有返回 Guid 对象。
所以我猜你可以在 EncryptedStringType Get/Set 方法中序列化/反序列化,例如在 Get() 中,您可以更改为:
if (typeof(T) == typeof(string))
return decrypted;
var obj = JsonConvert.DeserializeObject(decrypted);
return obj;
但这看起来很可怕,尤其是在您有现有数据要迁移的情况下。
我也不想将内容存储为二进制,因为团队希望能够通过 SQL 手动检查/测试/审计哪些列是加密的(这在文本中很明显,但是不是二进制)。
我的 POCO 中将 Guid 转换为字符串并通过简单的 get/set 方法再次返回的字符串支持字段可能是最佳选择,但我不知道如何通过解决方案中的自动映射来做到这一点,也不知道它有多混乱是吗?
【问题讨论】:
-
你试过this之类的吗?它可能会起作用。另外,“这看起来很可怕,尤其是在您有现有数据要迁移的情况下”是什么意思?
-
其实,你是对的。我在想当我可以有一个字符串时我不想在列中出现不必要的 json,但是无论如何看到它是加密的,它并没有太大的区别,因为它不会在 SQL 查询中手动可见/可查询无论如何分析仪。感谢您的评论,因为它确实消除了对 json 的需求,并查看我的结果答案:)
-
顺便说一句,如果有人读到这里确实想要例如将更复杂的对象存储为二进制而不是字符串,请参阅:blog.muonlab.com/2010/04/20/encrypting-data-with-nhibernate
标签: c# encryption nhibernate fluent-nhibernate orchardcms