简体   繁体   English

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

[英]Is there a way to make NHibernate treat string keys as case-insensitive?

I'm using NHibernate's second level cache on an entity whos primary key is a string column. 我在主键是字符串列的实体上使用NHibernate的二级缓存。 The database is case insensitive however when I retrieve the entity using the same Id but with different casing then NHibernate is treating the entity as a different entity in the cache. 数据库不区分大小写但是当我使用相同的Id检索实体但具有不同的大小时,NHibernate将实体视为缓存中的不同实体。 For example, I have a Setting table with a Name column as the primary key. 例如,我有一个设置表,其中Name列为主键。 If I try to retrieve the entity by using session.Get<Setting>("TestMode") , Get<Setting>("testmode") or Get<Setting>("TESTMODE") that will go to the database 3 times and put 3 separate entities in the 2nd level cache. 如果我尝试使用session.Get<Setting>("TestMode")Get<Setting>("testmode")Get<Setting>("TESTMODE")来检索实体,它将进入数据库3次并放入第二级缓存中有3个独立的实体。 If I then change something on my Setting entity through NHibernate, it will update the cached entity that actually matches the casing on the entity as it is stored in the database (in this case the one named "TestMode"). 如果我然后通过NHibernate更改我的设置实体上的内容,它将更新实际匹配实体上的外壳的缓存实体,因为它存储在数据库中(在本例中是名为“TestMode”的实体)。 The problem is then that the other entities will still be in the cache but will be stale. 问题是,其他实体仍将在缓存中,但将是陈旧的。

I understand why NHibernate would treat Ids as case-sensitive by default but there doesn't appear to be a way to set the case insensitivity at an entity level or configuration level as far as I can find. 我理解为什么NHibernate会默认将Ids视为区分大小写,但似乎没有办法在实体级别或配置级别设置不区分大小写,就我所知。 Can this be done, and if so how? 可以这样做,如果是这样,怎么办?

If it helps, I am using Fluent NHibernate. 如果它有帮助,我使用Fluent NHibernate。 My Id column is specified as such: 我的Id列指定如下:

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

UPDATE Included below are the implementations of Equals() and GetHashCode() from my Setting entity. 更新下面包括我的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();
}

UPDATE 2 I've been profiling with NHibernate Profiler, and doing the retrievals for "TestMode", "testmode", "TestMode", "Testmode", "TESTMODE" (in that order) shows only a cache hit for the second "TestMode". 更新2我一直在使用NHibernate Profiler进行分析,并且对“TestMode”,“testmode”,“TestMode”,“Testmode”,“TESTMODE”(按此顺序)进行检索仅显示第二个“TestMode”的缓存命中”。 It shows "2nd level cache load Setting (TestMode /* id */)", all the rest show SELECT ... FROM Setting setting0_ WHERE setting0_.Name = "testmode" (in whatever casing the call was asking for). 它显示“第二级缓存加载设置(TestMode / * id * /)”,其余所有显示SELECT ... FROM设置设置0_ WHERE setting0_.Name =“testmode”(在调用要求的任何大小写中)。 When clicking the link "See the 1 row(s) resulting from this statement" it show me the row with the Name column having the correct casing of "TestMode", so it's clearly retrieving the entity with the right casing for the column itself, but it seems it's definately associating the entity with the id that the Get() function was called with. 当单击链接“查看此语句产生的1行”时,它会向我显示名称列具有正确“TestMode”外壳的行,因此它清楚地检索具有该列本身的正确外壳的实体,但它似乎肯定将实体与调用Get()函数的id相关联。 This doesn't seem like it should be the normal behaviour but I haven't been able to find any information about the specifics of how the 2nd level cache works, only the basics of how to use it from the NHibernate Cookbook. 这似乎不应该是正常行为,但我无法找到有关二级缓存如何工作的具体信息,只有NHibernate Cookbook中如何使用它的基础知识。

Although I doubt it should make a difference to, I am using the HashtableCacheProvider for all these tests. 虽然我怀疑它应该有所作为,但我正在使用HashtableCacheProvider进行所有这些测试。 (for production we'll be using a paid-for caching provider) (对于生产,我们将使用付费缓存提供商)

If you think of the cache as a dictionary with a string key, it's obvious that the keys "TEST" and "test" are different because C# is case sensitive. 如果您将缓存视为带字符串键的字典,很明显键“TEST”和“test”是不同的,因为C#区分大小写。

But something doesn't seem right here. 但有些事情似乎并不合适。 How do you know that NH is putting three entities in 2nd level cache? 你怎么知道NH正在将三个实体放在二级缓存中? Assuming you're using a case-insensitive database, all three queries generated by the Get method will return the same row and the ID property on the entity will be set using the value returned by the query, not by the value supplied to the Get method. 假设您使用的是不区分大小写的数据库,Get方法生成的所有三个查询都将返回相同的行,并且将使用查询返回的值来设置实体上的ID属性,而不是通过提供给Get的值来设置方法。 The entities will be cached using the entity's ID also. 实体也将使用实体的ID进行缓存。 Do all three Gets return instances with the ID in the same case ("TestMode")? 是否所有三个获取返回实例的ID都在相同的情况下(“TestMode”)?


Update based on the edits to your question, if I understand you correctly: 如果我理解正确的话,请根据您对问题的修改进行更新:

  • Get("TestMode") caches the object as expected and retrieves it from cache on subsequent requests for "TestMode" Get(“TestMode”)按预期缓存对象,并在后续“TestMode”请求中从缓存中检索它
  • Get("testmode") or any other case goes back to the database Get(“testmode”)或任何其他情况返回数据库

That's the behavior I would expect. 这就是我期望的行为。 The value supplied for id is used to attempt to locate the entity in the cache. 为id提供的值用于尝试在缓存中定位实体。 If a match is found then the entity is retrieved from cache; 如果找到匹配,则从缓存中检索实体; if not the database is queried. 如果不是,则查询数据库。 After the entity is retrieved, NHibnernate attempts to add it to the cache using its id as a key, but it already exists so the cached value is updated, replaced or ignored. 检索实体后,NHibnernate尝试使用其id作为键将其添加到缓存中,但它已经存在,因此缓存的值将被更新,替换或忽略。

The key point is that the id supplied to the query is used as a key to locate the entity in the cache, but the entity's id is used as the key when it is added to the cache. 关键点是提供给查询的id用作在缓存中定位实体的键,但实体的id在添加到缓存时用作键。

The cache depends on the Equals() implementation provided. 缓存取决于提供的Equals()实现。

So, modify your Equals() implementation for this class to something that will take into account case issues. 因此,将此类的Equals()实现修改为将考虑案例问题的内容。 Either compare with insensitive settings or just .ToUpper()/.ToLower() the ID's. 要么与不敏感设置进行比较,要么只与.ToUpper()/.ToLower()进行比较。

UPDATE: there seems to be a duplicate question at NHibernate issue with assigned string ID and different case chars . 更新:在NHibernate问题上似乎有一个重复的问题, 分配了字符串ID和不同的案例字符 Try to use the accepted answer solution 尝试使用已接受的答案解决方案

I think you can get around this by defining a custom string type, say CaseInsensitiveStringType , and then 我认为你可以通过定义自定义字符串类型来解决这个问题,比如CaseInsensitiveStringType ,然后

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM