在NHibernate中实现具有可翻译属性的实体的最佳方法

在NHibernate中实现具有可翻译属性的实体的最佳方法,nhibernate,nhibernate-mapping,Nhibernate,Nhibernate Mapping,考虑以下课程(简化以关注核心问题): 和表格: Question - QuestionId ((primary key, identity column and key) - Code QuestionTranslation - QuestionTranslationId (primary key, identity column; not really relevant to the association) - QuestionId (composite key element 1) -

考虑以下课程(简化以关注核心问题):

和表格:

Question
- QuestionId ((primary key, identity column and key)
- Code

QuestionTranslation
- QuestionTranslationId (primary key, identity column; not really relevant to the association)
- QuestionId (composite key element 1)
- CultureName (composite key element 2) (sample value: en-US, en-CA, es-ES)
- Text
- Hint
如何映射问题类,以便使用当前线程的区域性填充文本和提示属性。如果线程的区域性被更改,我希望文本和提示属性自动返回适当的值,而无需重新加载问题实体


注意,我只是从业务方面概述了相关的类和属性。我完全接受实现所需功能所需的任何新类或属性。

编辑以反映更改后的答案:

public class Question
{
    public virtual string QuestionId { get; set; }

    public virtual string Text
    {
        get
        {
            var currentculture = CultureInfo.CurrentCulture.Name;
            return Translations
                .Where(trans => trans.CultureName == currentculture)
                .Select(trans => trans.Text)
                .FirstOrDefault();
        }
        set
        {
            var currentculture = CultureInfo.CurrentCulture.Name;
            var translation = Translations
                .Where(trans => trans.CultureName == currentculture)
                .FirstOrDefault();
            if (translation == null)
            {
                translation = new QuestionTranslation();
                Translations.Add(translation);
            }
            translation.Text = value;
        }
    }
    public virtual string Hint
    {
        get
        {
            var currentculture = CultureInfo.CurrentCulture.Name;
            return Translations
                .Where(trans => trans.CultureName == currentculture)
                .Select(trans => trans.Hint)
                .FirstOrDefault();
        }
        set
        {
            var currentculture = CultureInfo.CurrentCulture.Name;
            var translation = Translations
                .Where(trans => trans.CultureName == currentculture)
                .FirstOrDefault();
            if (translation == null)
            {
                translation = new QuestionTranslation();
                Translations.Add(translation);
            }
            translation.Hint = value;
        }
    }

    protected virtual ICollection<QuestionTranslation> Translations { get; set; }
}

class QuestionTranslation
{
    public virtual int Id { get; protected set; }
    public virtual string CultureName { get; set; }
    public virtual string Text { get; set; }
    public virtual string Hint { get; set; }
}

<class name="Question" xmlns="urn:nhibernate-mapping-2.2">
  <id name="QuestionId" column="QuestionId"/>

  <bag name="Translations" table="QuestionTranslation" lazy="true">
    <key>
      <column name="QuestionId"/>
    </key>
    <one-to-many class="QuestionTranslation"/>
  </bag>
</class>

<class name="QuestionTranslation" table="QuestionTranslation" xmlns="urn:nhibernate-mapping-2.2">
  <id name="QuestionTranslationId"/>
  <many-to-one name="ParentQuestion" column="QuestionId"/>
</class>
公开课问题
{
公共虚拟字符串QuestionId{get;set;}
公共虚拟字符串文本
{
得到
{
var currentculture=CultureInfo.currentculture.Name;
返回翻译
.Where(trans=>trans.CultureName==currentculture)
.Select(trans=>trans.Text)
.FirstOrDefault();
}
设置
{
var currentculture=CultureInfo.currentculture.Name;
var翻译=翻译
.Where(trans=>trans.CultureName==currentculture)
.FirstOrDefault();
if(translation==null)
{
翻译=新问题翻译();
翻译。添加(翻译);
}
翻译。文本=值;
}
}
公共虚拟字符串提示
{
得到
{
var currentculture=CultureInfo.currentculture.Name;
返回翻译
.Where(trans=>trans.CultureName==currentculture)
.Select(trans=>trans.Hint)
.FirstOrDefault();
}
设置
{
var currentculture=CultureInfo.currentculture.Name;
var翻译=翻译
.Where(trans=>trans.CultureName==currentculture)
.FirstOrDefault();
if(translation==null)
{
翻译=新问题翻译();
翻译。添加(翻译);
}
translation.Hint=value;
}
}
受保护的虚拟ICollection转换{get;set;}
}
课堂提问与翻译
{
公共虚拟整数Id{get;protected set;}
公共虚拟字符串CultureName{get;set;}
公共虚拟字符串文本{get;set;}
公共虚拟字符串提示{get;set;}
}

如果你有很多翻译,那么将
ICollection translations{get;set;}
更改为
IDictionary translations{get;set;}
,并映射为
,但通常情况下,上述方法应该可以做到这一点

这是Firo答案的替代方法(是的,我复制了它并对它进行了修改,对此感觉很糟糕)

它使用字典并将翻译映射为复合元素(因此根本不需要id)

公开课问题
{
公共虚拟字符串QuestionId{get;set;}
公共虚拟字符串文本
{
得到
{
var translation=Translations[CultureInfo.CurrentCulture.Name];
if(translation!=null)返回translation.Text
返回null;
}
设置
{
GetTranslation(CultureInfo.CurrentCulture.Name).Text=value;
}
}
公共虚拟字符串提示
{
得到
{
var translation=Translations[CultureInfo.CurrentCulture.Name];
if(translation!=null)返回translation.Hint
返回null;
}
设置
{
GetTranslation(CultureInfo.CurrentCulture.Name).Hint=value;
}
}
私有问题翻译GetTranslation(CultureInfo.CurrentCulture.Name)
{
问题翻译;
if(!Translations.TryGetValue(CultureInfo.CurrentCulture.Name,输出翻译))
{
翻译=新问题翻译()
翻译[CultureInfo.CurrentCulture.Name]=翻译;
}
返回翻译;
}
受保护的虚拟IDictionary翻译{get;private set;}
}
课堂提问与翻译
{
//没有id,区域性名称
公共虚拟字符串文本{get;set;}
公共虚拟字符串提示{get;set;}
}
映射:

<class name="Question">
  <id name="QuestionId" column="QuestionId"/>

  <map name="Translations" table="QuestionTranslation" lazy="true">
    <key column="QuestionId"/>
    <index column="CultureName"/>
    <composite-element class="QuestionTranslation">
      <property name="Text"/>
      <property name="Hint"/>
    </composite-element>
  </bag>
</class>


是否将
(QuestionId,CultureName)
作为一个选项?您也可以将
CultureName
保留在答案之外,因为访问问题的主键不会触发延迟loading@Firo:制作复合键可以是一个选项。你能详细说明你的回答吗?谢谢。数据库是只读的还是也应该更新?@Stefan:在公共应用程序中,大多数实体都是只读的,但在管理应用程序中是读写的。谢谢您的回复。。。我会测试你的解决方案,然后回复你。我已经重写了问题,所以最好解释一下我的问题,因为我觉得我写问题的时候不清楚。你介意回顾一下这个问题并相应地修改你的回答吗?我已经用你的回答作为我自己解决问题的起点。我不确定映射是否能够处理插入/更新翻译,例如,翻译表上的QuestionTranslationId列没有映射。有一个输入错误
应该是
,QuestionTranslation缺少Id属性。
public class Question
{
    public virtual string QuestionId { get; set; }

    public virtual string Text
    {
        get
        {
            var translation = Translations[CultureInfo.CurrentCulture.Name];
            if (translation != null) return translation.Text
            return null;
        }
        set
        {
            GetTranslation(CultureInfo.CurrentCulture.Name).Text = value;
        }
    }

    public virtual string Hint
    {
        get
        {
            var translation = Translations[CultureInfo.CurrentCulture.Name];
            if (translation != null) return translation.Hint
            return null;
        }
        set
        {
            GetTranslation(CultureInfo.CurrentCulture.Name).Hint = value;
        }
    }


    private QuestionTranslation GetTranslation(CultureInfo.CurrentCulture.Name)
    {
        QuestionTranslation translation;
        if (!Translations.TryGetValue(CultureInfo.CurrentCulture.Name, out translation))
        {
           translation = new QuestionTranslation()
           Translations[CultureInfo.CurrentCulture.Name] = translation;
        }
        return translation;
    }

    protected virtual IDictionary<string, QuestionTranslation> Translations { get; private set; }
}

class QuestionTranslation
{
    // no id, culture name
    public virtual string Text { get; set; }
    public virtual string Hint { get; set; }
}
<class name="Question">
  <id name="QuestionId" column="QuestionId"/>

  <map name="Translations" table="QuestionTranslation" lazy="true">
    <key column="QuestionId"/>
    <index column="CultureName"/>
    <composite-element class="QuestionTranslation">
      <property name="Text"/>
      <property name="Hint"/>
    </composite-element>
  </bag>
</class>