我正在试图找出为什么使用以下代码对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;}
}这都是用流利的自动完成的。
在SQL数据库中,Guid类型和string类型都是nvarchar(500)。
如前所述,第一个POCO工作正常,并按预期进行加密/解密,但是第二个POCO失败了,这就是我在日志中看到的:
NHibernate.Tuple.Entity.PocoEntityTuplizer.SetPropertyValuesWithOptimizer(Object实体,Object[]值){“无效的强制转换(检查映射中的属性类型不匹配);其他类的设置者”}
注意,第二个POCO对象可以很好地工作在nHib中,如果我删除了EncryptedDbString attibute,也就是说,它将Guid保存到一个nvarchar中没有问题。
显然,这里的问题是,它是一个Guid,因为字符串用例工作,但是我确实希望它作为一个Guid而不是一个字符串保存在代码中,而且我在这里看不到失败的意义。
好像我错过了一些小东西。我想我忽略了泛型的一些东西,但我只在那里找到了代码片段,而不是这样的完整示例。
编辑:
好吧,所以我想这是因为
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中的string back字段可以通过简单的get/set方法将Guid转换为字符串并再次返回,这可能是最好的选择,但我不知道如何在解决方案中实现自动化,或者它有多麻烦?
发布于 2016-12-13 09:21:36
睡过觉后,我想我想错了。
我现在已经意识到,我之所以不愿意在数据库中存储json,是因为我存储的是带有字符串倾向的对象--即自然转换为文本字段的东西,而不是完整的对象。myGuid.ToString()给您一个guid字符串,myDateTime.ToString()给您一个日期时间字符串等等。
因此,考虑到对象序列化本身在我的情况下是不需要的,而只是转换成字符串,Andrew的建议似乎是一个很好的解决方案。
更新代码:
public override void Set(IDbCommand cmd, object value, int index) {
var prm = ((IDataParameter) cmd.Parameters[index]);
if (cmd == null) throw new ArgumentNullException("cmd");
if (value == null) {
prm.Value = null;
return;
}
string str;
try {
// guid becomes a simple guid string, datetime becomes a simple
// datetime string etc. (ymmv per type)
// note that it will use the currentculture by
// default - which is what we want for a datetime anyway
str = TypeDescriptor.GetConverter(typeof(T)).ConvertToString(value);
}
catch (NotSupportedException) {
throw new NotSupportedException("Unconvertible type " + typeof(T) + " with EncryptedDbString attribute");
}
prm.Value = Encryptor.EncryptString(str);
}
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;
var decrypted = Encryptor.DecryptString(encrypted);
object obj;
try {
obj = (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(decrypted);
}
catch (NotSupportedException) {
throw new NotSupportedException("Unconvertible type " + typeof(T) + " with EncryptedDbString attribute");
}
catch (FormatException) {
// consideration - this will log the unencrypted text
throw new FormatException(string.Format("Cannot convert string {0} to type {1}", decrypted, typeof(T)));
}
return obj;
}EncryptedStringConvention的一个改进是将Accept()方法添加到预检查中,以确保所有标记为EncryptedDbString属性的类型都是可转换的。也许我们可以使用IConvertible (),而键入是,但是我会留下它,因为我已经花了足够的时间!
https://stackoverflow.com/questions/41106678
复制相似问题