我有很多具有TextID列的表,它引用了转换表。翻译表还需要LanguageID来获得所需语言的翻译文本。我的问题是我的数据库中没有LanguageID,它是在系统中预定义的,我不知道如何使用Fluent API定义它,即这可以是我的模型:
public partial class MyEntity
{
public short ID { get; set; }
public Nullable<int> TextID { get; set; }
[NotMapped]
public Nullable<int> LanguageID { get; set; }
public virtual TEXT_TRANSLATION Translation { get; set; }
}
和翻译表:
public partial class TEXT_TRANSLATION
{
[Key, Column(Order = 0)]
public int TextID { get; set; }
[Key, Column(Order = 1)]
public int LanguageID { get; set; }
public string TranslatedText { get; set; }
}
基本上我需要这样的导航:
myEntity.Translation.TranslatedText
使用SQL时,我会这样做:
Left Join TEXT_TRANSLATION ON
MyEntity.TextID = TEXT_TRANSLATION.TextID
AND TEXT_TRANSLATION.LanguageID = 1033
基本上我想使用TextID外键并获得 ONLY ONE 翻译 - LanguageID是静态的,并在上下文中预定义。
我无法更改现有的数据库架构。如果我不需要在我的代码中映射LanguageID字段,只需在映射中使用它就像系统参数一样,这将是完美的。甚至可以用EF吗?
答案 0 :(得分:5)
如果LanguageID
是静态的,您可以尝试使用此黑客。
定义您的实体,如:
public class Entity {
public int Id { get; set; }
public int TextId { get; set; }
public Translation Translation { get; set; }
}
// No LanguageId in translation
public class Translation {
public int TextId { get; set; }
public string TranslatedText { get; set; }
}
并将此流畅的映射添加到派生的OnModelCreating
中的DbContext
:
// Define foreign key
modelBuilder.Entity<Entity>()
.HasRequired(e => e.Translation)
.WithMany()
.HasForeignKey(e => e.TextId);
// Trick - EF believes that only TextId is PK. Without this trick you cannot
// make navigation property on your entity
modelBuilder.Entity<Translation>()
.HasKey(t => t.TextId);
// If you are going to insert translations as well your TextId cannot be
// handled as autogenerated column
modelBuilder.Entity<Translation>()
.Property(t => t.TextId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
// The HACK - conditional mapping. This tells EF to "see" only records
// with LanguageId set to 1033. Without this hack you cannot filter
// translations for only single language.
modelBuilder.Entity<Translation>()
.Map(m => {
m.Requires("LanguageId").HasValue(1033);
m.ToTable("Translations");
});
hack基于用于TPH映射的概念,但在这种情况下,您只使用单个实体类型来仅加载具有预定义LanguageId
的记录子集。即使来自主要实体的FK也应该有效,因为您不能使用相同的TextId
翻译 - 这意味着它们也具有相同的LanguageId
,这是不可能的,因为TextId
和{{ 1}}表格主键。
我不确定此解决方案中是否存在任何隐藏问题。我只是快速尝试一下它就有用了。
答案 1 :(得分:2)
如果您做了类似的事情该怎么办:
public partial class MyEntity
{
public short ID { get; set; }
public Nullable<int> TextID { get; set; }
[NotMapped]
public Nullable<int> LanguageID { get; set; }
public virtual ICollection<TEXT_TRANSLATION> Transations {get;set;}
public IQueryable<TEXT_TRANSLATION> Translation
{
get
{
return this.Translations.Where( t => t.LanguageID == this.LanguageID );
}
}
}
虚拟ICollection将存储所有翻译的列表,无论语言如何,翻译属性都会为您执行.Where()
答案 2 :(得分:2)
即使T_T表很大,我也不知道你怎么能以另一种方式做到这一点(好吧,至少需要“加载所有地方”)。
public static class Translator {
private static Dictionary<int, Dictionary<int, string>> translations_;
static Translator() {
translations_ = new Dictionary<int, Dictionary<int, string>>();
}
public static void PopulateTranslator(Repository repo) {//or something to go to your db
translations_ = repo.TEXT_TRANSLATIONs.ToList()
.GroupBy(m => m.LanguageId)
.ToDictionary(g => g.Key,
g=> g.ToDictionary(x => x.TextID,
x=> x.TranslatedText)
);
}
public static string GetTranslation(int languageId, int? textId) {
if (textId == null || !translations.ContainsKey(languageId)
return something or throw;
var dic = translations[languageId];
var id = Convert.ToInt32(textId);
if (!dic.ContainsKey(id)
return something or throw;
return dic[id);
}
如果用户可以更新翻译,您可以在应用程序启动时调用Translator.PopulateTranslator(<something to access your db>)
,可能会重置。
然后在您的实体中,而不是
public virtual TEXT_TRANSLATION Translation { get; set; }
你会有
public string TranslatedText {
get { return Translator.GetTranslation(LanguageID, TextId);}
}
修改强> 另一种方法是使用resx文件而不是db数据,但我想你有其他约束。