NHibernate:通过基于用户文化交换列来进行本地化

时间:2011-05-05 13:57:14

标签: nhibernate fluent-nhibernate localization nhibernate-mapping

我已阅读有关NHibernate本地化的few good articles,但似乎没有什么能够很好地适应这种情况。我们的数据库使用以下模式存储本地化信息:

TABLE NAME: PRODUCT
COLUMNS: 
ID
PRODUCT_CODE
PRODUCT_NAME
DESCRIPTION1
DESCRIPTION2
DESCRIPTION3

字段DESCRIPTION1,DESCRIPTION2和DESCRIPTION3包含产品的本地化描述。总是有三个本地化字符串,数字总是指相同的文化(例如3是英语)。在其他一些表中,我们有NAME1,NAME2和NAME3等列。

我想使用(Fluent)NHibernate以这样的方式映射到这些列,即模型类只包含一个名为“Description”的属性,它将包含从正确的字段获得的值,基于当前线程的文化。 NHibernate似乎有多个扩展点,但哪个是最好的?

将字符串属性动态映射到正确的列?

在我的梦想世界中,我将Description-field作为字符串。

public class Product
{
    /* ... */

    public virtual string Description { get; set; }
}

在ClassMap中,我会告诉Fluent NHibernate在运行时评估正确的列名:

public class ProductMap : ClassMap<Product>
{
    public ProductMap()
    {
        /* ... */

        MapAsLocalized(x => x.Description);
    }
}

当使用此映射执行查询时,我希望我的代码自动检查线程的文化并根据它选择正确的列。例如,如果文化是en-US,则查询会将DESCRIPTION3-column中的值提取到Description-property中。

自定义IUSERTYPE?

但如果这不可能,我可以选择自定义LocalizedString类,这样我就可以像这样定义Product-class:

public class Product
{
    /* ... */

    public virtual LocalizedString Description { get; set; }
}

然后获取本地化值,例如:

        // Product prod;
        // Get product from db

        Console.WriteLine(prod.Description.Value);

在LocalizedString示例中,想法是dev仍然可以创建映射而无需创建到每个DESCRIPTION-column的映射:

public class ProductMap : ClassMap<Product>
{
    public ProductMap()
    {
        /* ... */

        Map(x => x.Description);
    }
}

当使用此映射执行查询时,它会在LocalizedString(内部是某些排序的集合)内返回DESCRIPTION1,DESCRIPTION2和DESCRIPTION3的值。

有关如何实施解决方案1或解决方案2的任何建议?或者两者兼而有之?

2 个答案:

答案 0 :(得分:2)

你可以使用usertype

public class LocalizationUserType : ImmutableUserType
{
    public override object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        switch(Languageprovider.GetLanguage())
        {
             case language1:
                  return NHibernateUtil.String.NullSafeGet(cmd, value, index);
             case language2:
                  return NHibernateUtil.String.NullSafeGet(cmd, value, index + 1);
             ...
        }
        return null;
    }

    public override void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        switch(Languageprovider.GetLanguage())
        {
             case language1:
                  NHibernateUtil.String.NullSafeSet(cmd, value, index);
                  break;
             case language2:
                  NHibernateUtil.String.NullSafeSet(cmd, value, index + 1);
                  break;
             ...
        }
    }

    public override Type ReturnedType
    {
        get { return typeof(String); }
    }

    public override SqlType[] SqlTypes
    {
        get { return new[]
        {
            SqlTypeFactory.GetString(1000),
            SqlTypeFactory.GetString(1000),
            SqlTypeFactory.GetString(1000),
        }; }
    }
}

ImmutableUserType是一个基类,实现了一些IUserType方法。如果你想要我可以发送实现

用法:

public class ProductMap : ClassMap<Product>
{
     ProductMap
     {
         ...
         Map(p => p.Name)
             .Columns.Add("name1", "name2", "name3")
             .CustomType<LocalizationUserType>();

         ...
         Map(p => p.Description)
             .Columns.Add("desc1", "desc2", "desc3")
             .CustomType<LocalizationUserType>();

答案 1 :(得分:0)

虽然NHibernate确实提供了在运行时修改映射的工具。每次要进行查询时,它都会严重阻碍NHibernate重建会话工厂的性能。我建议使用你的第二个想法。您甚至不需要自定义IUserType。您可以将其映射为组件。

Component(x => x.Description, c => 
     {
          c.Map(d => d.Description1);
          ...
     });

或者,您可以将每个描述映射到Product类中的后备字段,并提供一个公共Description属性,该属性在运行时选择适当的字段。