我已阅读有关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的任何建议?或者两者兼而有之?
答案 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属性,该属性在运行时选择适当的字段。