首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >nHibernate无效强制转换与自定义PrimitiveType

nHibernate无效强制转换与自定义PrimitiveType
EN

Stack Overflow用户
提问于 2016-12-12 17:56:09
回答 1查看 296关注 0票数 0

我正在试图找出为什么使用以下代码对nHibernate进行无效的强制转换异常:

代码语言:javascript
复制
AutoMap.Source(new TypeSource(recordDescriptors))
    .Conventions.Add(new EncryptedStringConvention());

代码语言:javascript
复制
[AttributeUsage(AttributeTargets.Property)]
public class EncryptedDbString : Attribute { }

代码语言:javascript
复制
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);
    }
}

代码语言:javascript
复制
[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:

代码语言:javascript
复制
public class someclass {
   public virtual string id {get;set;}
   [EncryptedDbString]
   public virtual string abc {get;set;}
}

失败的POCO:

代码语言:javascript
复制
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而不是一个字符串保存在代码中,而且我在这里看不到失败的意义。

好像我错过了一些小东西。我想我忽略了泛型的一些东西,但我只在那里找到了代码片段,而不是这样的完整示例。

编辑:

好吧,所以我想这是因为

代码语言:javascript
复制
Get(IDataReader rs, int index) 

没有返回Guid对象。

因此,我想您可以在EncryptedStringType Get/Set方法中序列化/反序列化,例如在Get()中可以更改为:

代码语言:javascript
复制
if (typeof(T) == typeof(string))
    return decrypted;

var obj = JsonConvert.DeserializeObject(decrypted);
return obj;

但这似乎很可怕,尤其是如果您有要迁移的现有数据。

我也不想将内容存储为二进制,因为团队希望能够通过SQL手动检查/测试/审核哪些列是加密的(这在文本中是显而易见的,但不是二进制的)。

POCO中的string back字段可以通过简单的get/set方法将Guid转换为字符串并再次返回,这可能是最好的选择,但我不知道如何在解决方案中实现自动化,或者它有多麻烦?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-12-13 09:21:36

睡过觉后,我想我想错了。

我现在已经意识到,我之所以不愿意在数据库中存储json,是因为我存储的是带有字符串倾向的对象--即自然转换为文本字段的东西,而不是完整的对象。myGuid.ToString()给您一个guid字符串,myDateTime.ToString()给您一个日期时间字符串等等。

因此,考虑到对象序列化本身在我的情况下是不需要的,而只是转换成字符串,Andrew的建议似乎是一个很好的解决方案。

更新代码:

代码语言:javascript
复制
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 (),而键入是,但是我会留下它,因为我已经花了足够的时间!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41106678

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档