【问题标题】:How to map NHibernate bag with a case-insensitive key如何使用不区分大小写的键映射 NHibernate 包
【发布时间】:2017-01-27 18:28:31
【问题描述】:

当我尝试加载具有映射为包的子实体的实体时,并且子实体和父实体之间的键是字符串,如果键的大小写与父实体的大小写不匹配,则不会填充结果包ID 属性。

如果 Office.ID == "MyOffice"PromotionalContent.ContextKey == "myoffice",集合不加载促销,即使 NHProfiler 显示它是由数据库返回的

如果 Office.ID == "MyOffice" 和 *PromotionalContent.ContextKey == "MyOffice",集合可以加载

    <class name="Office" table="Office" lazy="true">
        <id name="ID" column="OfficeNum">
            <generator class="assigned" />
        </id>
      <property name="Name" column="OfficeName" />
      <property name="PhoneNumber" column="PhoneNum" />
      <bag name="Promotions" lazy="true" where="ContextType='Office'"  >
        <key column="ContextKey"/>
        <one-to-many  class="PromotionalContent"/>
      </bag>...

这是 NHibernate 4.0.3 版。

我怎样才能映射它,或者修复它以便它总是加载而不管大小写?

【问题讨论】:

  • 底层数据库是什么?
  • MSSQL 2014。我使用了 NHProfiler 我能够证明 SQL Server 正在返回这两种情况的数据,所以这似乎是会话或缓存的问题。

标签: c# nhibernate one-to-many nhibernate-mapping case-insensitive


【解决方案1】:

在尝试使用事件侦听器解决问题几天后,调试,结果发现 NHibernate 中存在错误。

我能够通过更改实际的 NHibernate 源、重建和重新链接来解决问题。 我使用的技术使实际的实体键保持不变,因此保留了它来自或进入数据库时​​的大小写。

当 NHibernate 加载一个实体时,会创建一个 "key",它基本上只是一个 标记,它存储在一个 IDictionary 中并进行测试,它代表会话或实体及其相关的依赖集合的缓存。这个 “key” 不够聪明,无法进行不区分大小写的比较。为了做到这一点,我必须修改哈希码并调整 Equals() 以进行不区分大小写的比较,这样如果键中的大小写与测试值不同,它总是会受到影响。

如果您愿意构建 NHibernate 并拥有技术债务直到它被修复,这里是我的补丁:

修改 EntityKey.cs、CollectionKey.cs 和 CacheKey.cs

  1. GetHashCode() - 无论键的大小写如何,都需要返回一致的哈希码。否则,当情况不同时,Equals() 将 NEVER 被调用。
  2. Equals() - 无论大小写如何,都需要返回 true。否则,您的实体和/或其子集合将被遗漏。

我作弊只是使用大写进行比较,而不是不区分大小写的比较。

示例代码:

EntityKey.cs

    public override bool Equals(object other)
    {
        var otherKey = other as EntityKey;
        if(otherKey==null) return false;

        if (Identifier is string && otherKey.Identifier is string)
        {
            object thiskeySanitized = ((string)Identifier).ToUpper();
            object thatkeySanitized = ((string)otherKey.Identifier).ToUpper();
            return otherKey.rootEntityName.Equals(rootEntityName) && identifierType.IsEqual(thatkeySanitized, thiskeySanitized, entityMode, factory);
        }
        return
            otherKey.rootEntityName.Equals(rootEntityName)
            && identifierType.IsEqual(otherKey.Identifier, Identifier, entityMode, factory);
    }

    private int GenerateHashCode()
    {
        int result = 17;
        object sanitizedIdentifier = (identifier is string) ? ((string) identifier).ToUpper() : identifier;
        unchecked
        {
            result = 37 * result + rootEntityName.GetHashCode();
            result = 37 * result + identifierType.GetHashCode(sanitizedIdentifier, entityMode, factory);
        }
        return result;
    }

CollectionKey.cs

    public override bool Equals(object obj)
    {
        CollectionKey that = (CollectionKey)obj;
        if (this.key is string && that.key is string)
        {
            object thiskeySanitized = ((string)this.key).ToUpper();
            object thatkeySanitized = ((string)that.key).ToUpper();
            return that.role.Equals(role) && keyType.IsEqual(thatkeySanitized, thiskeySanitized, entityMode, factory);
        }
        return that.role.Equals(role) && keyType.IsEqual(that.key, key, entityMode, factory);
    }

    private int GenerateHashCode()
    {
        int result = 17;
        unchecked
        {
            result = 37 * result + role.GetHashCode();
            object sanitizedIdentifier = (key is string) ? ((string)key).ToUpper() : key;
            result = 37 * result + keyType.GetHashCode(sanitizedIdentifier, entityMode, factory);
        }
        return result;
    }

CacheKey.cs

    public CacheKey(object id, IType type, string entityOrRoleName, EntityMode entityMode, ISessionFactoryImplementor factory)
    {
        key = id;
        this.type = type;
        this.entityOrRoleName = entityOrRoleName;
        this.entityMode = entityMode;
        object sanitizedIdentifier = (key is string) ? ((string)key).ToUpper() : key;
        hashCode = type.GetHashCode(sanitizedIdentifier, entityMode, factory);
    }

    public override bool Equals(object obj)
    {
        CacheKey that = obj as CacheKey;
        if (that == null) return false;
        if (key is string && that.key is string)
        {
            object thiskeySanitized = ((string)key).ToUpper();
            object thatkeySanitized = ((string)that.key).ToUpper();
            return entityOrRoleName.Equals(that.entityOrRoleName) && type.IsEqual(thiskeySanitized, thatkeySanitized, entityMode);
        }
        return entityOrRoleName.Equals(that.entityOrRoleName) && type.IsEqual(key, that.key, entityMode);
    }

这个问题早在 2015 年就报告给了 NHibernate 团队 - https://nhibernate.jira.com/browse/NH-3833

【讨论】:

  • 因为不是错误而被丢弃。数据库也可以区分大小写。正如 Alexander 在 NH-3833 上所解释的那样,您可以通过将您的键映射到自定义类型来实现自己的相等语义。
  • 酷,谢谢,我会看看这是否有效并发布结果。 nhibernate.info/blog/2009/10/15/…
猜你喜欢
  • 1970-01-01
  • 2016-12-25
  • 2011-02-18
  • 2020-07-13
  • 1970-01-01
  • 2019-10-04
  • 2017-07-19
  • 2016-01-20
  • 1970-01-01
相关资源
最近更新 更多