Entity Framework - relationship with fake Foreign Key (no foreign key in the db)

依然范特西╮ 提交于 2019-12-03 14:01:58

If your LanguageID is static you can try to use this hack.

Define your entities like:

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; }
}

And add this fluent mapping to OnModelCreating in your derived 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");   
                    });

The hack is based on concept used for TPH mapping but in this case you are only using single entity type to load only subset of records with predefined LanguageId. Even FK from the main entity should work because you cannot have to translations with the same TextId - it would mean that they also have the same LanguageId which is not possible because TextId and LanguageId form primary key.

I'm not sure if there is any hidden issue in this solution. I just gave it a quick try and it worked.

What if you did something like this:

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 );
      }
    }
}

The virtual ICollection would store a list of all your translations regardless of language, and the Translation property would do the .Where() for you

Even if T_T table is huge, I don't see how you could do this another way (well, at least the need to "load all somewhere").

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);

  }

you would call Translator.PopulateTranslator(<something to access your db>) on start of your application, with maybe resettings, if users can update translations.

Then in your entities, instead of

public virtual TEXT_TRANSLATION Translation { get; set; }

you would have

public string TranslatedText {
    get { return Translator.GetTranslation(LanguageID, TextId);}
}

EDIT Another way would be to work with resx files instead of db datas, but I imagine you have other constraints.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!