有没有办法让NHibernate将字符串键视为不区分大小写?

时间:2011-12-20 14:25:11

标签: c# .net nhibernate fluent-nhibernate

我在主键是字符串列的实体上使用NHibernate的二级缓存。数据库不区分大小写,但是当我使用相同的Id检索实体但具有不同的大小时,NHibernate将实体视为缓存中的不同实体。例如,我有一个设置表,其中Name列为主键。如果我尝试使用session.Get<Setting>("TestMode")Get<Setting>("testmode")Get<Setting>("TESTMODE")来检索实体,它将在数据库中进行3次,并在第2级缓存中放入3个单独的实体。如果我然后通过NHibernate更改我的设置实体上的内容,它将更新实际匹配实体上的外壳的缓存实体,因为它存储在数据库中(在本例中是名为“TestMode”的实体)。问题是,其他实体仍将在缓存中,但将是陈旧的。

我理解为什么NHibernate会默认将Ids视为区分大小写,但似乎没有办法在实体级别或配置级别设置不区分大小写,就我所知。可以这样做,如果是这样的话?

如果有帮助,我正在使用Fluent NHibernate。我的Id列被指定为:

Id(x => x.Name).GeneratedBy.Assigned().Column("Name");

更新 下面是我的Setting实体中的Equals()和GetHashCode()的实现。

public override bool Equals(object obj)
{
    if (obj == null)
        return false;
    if (obj is Setting)
    {
        if (object.ReferenceEquals(this, obj))
            return true;
        if (this.Name.Equals(((Setting)obj).Name, StringComparison.OrdinalIgnoreCase)) // Ignore case because our databases are case insensitive!
            return true;
    }
    return false;
}

public override int GetHashCode()
{
    return Name.ToUpperInvariant().GetHashCode();
}

更新2 我一直在使用NHibernate Profiler进行分析,并且对“TestMode”,“testmode”,“TestMode”,“Testmode”,“TESTMODE”(按此顺序)进行检索仅显示第二个“TestMode”的缓存命中。它显示“第二级缓存加载设置(TestMode / * id * /)”,其余所有显示SELECT ... FROM设置设置0_ WHERE setting0_.Name =“testmode”(在调用要求的任何大小写中)。 当单击链接“查看此语句产生的1行”时,它会向我显示名称列具有正确“TestMode”外壳的行,因此它清楚地检索具有该列本身的正确外壳的实体,但它似乎肯定将实体与调用Get()函数的id相关联。这似乎不应该是正常行为,但我无法找到有关二级缓存如何工作的具体信息,只有NHibernate Cookbook中如何使用它的基础知识。

虽然我怀疑它应该有所作为,但我正在使用HashtableCacheProvider进行所有这些测试。 (对于生产,我们将使用付费缓存提供商)

3 个答案:

答案 0 :(得分:1)

如果您将缓存视为带字符串键的字典,很明显键“TEST”和“test”是不同的,因为C#区分大小写。

但有些事情似乎并不合适。你怎么知道NH正在将三个实体放在二级缓存中?假设您使用的是不区分大小写的数据库,Get方法生成的所有三个查询都将返回相同的行,并且将使用查询返回的值来设置实体上的ID属性,而不是通过提供给Get的值来设置方法。实体也将使用实体的ID进行缓存。是否所有三个获取返回实例的ID都在相同的情况下(“TestMode”)?


根据您对问题的修改进行更新,如果我理解正确的话:

  • Get(“TestMode”)按预期缓存对象,并在后续“TestMode”请求中从缓存中检索它
  • Get(“testmode”)或任何其他案例返回数据库

这就是我期望的行为。为id提供的值用于尝试在缓存中定位实体。如果找到匹配,则从缓存中检索实体;如果不是,则查询数据库。检索实体后,NHibnernate会尝试使用其id作为键将其添加到缓存中,但它已经存在,因此缓存的值会被更新,替换或忽略。

关键是提供给查询的id用作在缓存中定位实体的密钥,但实体的id在添加到缓存时用作密钥。

答案 1 :(得分:0)

缓存取决于提供的Equals()实现。

因此,请将此类的Equals()实施修改为考虑案例问题的内容。要么与不敏感设置进行比较,要么只与.ToUpper()/.ToLower() ID进行比较。

更新: 在NHibernate issue with assigned string ID and different case chars似乎有一个重复的问题。尝试使用已接受的答案解决方案

答案 2 :(得分:0)

我认为您可以通过定义自定义字符串类型来解决这个问题,比如CaseInsensitiveStringType,然后

Id(x => x.Name)
    .GeneratedBy
    .Assigned()
    .Column("Name")
    .CustomType<CaseInsensitiveStringType>();