Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
569 views
in Technique[技术] by (71.8m points)

entity framework - Best Practices to localize entities with EF Code first

I am developing a domain model using EF Code First to persist the data. I have to add support for multilanguage and I would like not to contaminate the domain model with location concepts.

I like that in database exists a ProductTranslate table with title and Language fields but in my domain title belongs to the Product entity.

Someone knows how to get this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here is what I use and works well with code first.

Define a base Translation class:

using System;

public abstract class Translation<T> where T : Translation<T>, new()
{

  public Guid Id { get; set; }

  public string CultureName { get; set; }

  protected Translation()
  {
    Id = Guid.NewGuid();
  }

}

Define a TranslationCollection class:

using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;

public class TranslationCollection<T> : Collection<T> where T : Translation<T>, new()
{

  public T this[CultureInfo culture]
  {
    get
    {
      var translation = this.FirstOrDefault(x => x.CultureName == culture.Name);
      if (translation == null)
      {
        translation = new T();
        translation.CultureName = culture.Name;
        Add(translation);
      }

      return translation;
    }
    set
    {
      var translation = this.FirstOrDefault(x => x.CultureName == culture.Name);
      if (translation != null)
      {
        Remove(translation);
      }

      value.CultureName = culture.Name;
      Add(value);
    }
  }

  public T this[string culture]
  {
    get
    {
      var translation = this.FirstOrDefault(x => x.CultureName == culture);
      if (translation == null)
      {
        translation = new T();
        translation.CultureName = culture;
        Add(translation);
      }

      return translation;
    }
    set
    {
      var translation = this.FirstOrDefault(x => x.CultureName == culture);
      if (translation != null)
      {
        Remove(translation);
      }

      value.CultureName = culture;
      Add(value);
    }
  }

  public bool HasCulture(string culture)
  {
    return this.Any(x => x.CultureName == culture);
  }

  public bool HasCulture(CultureInfo culture)
  {
    return this.Any(x => x.CultureName == culture.Name);
  }

}

You can then use those classes in your entities, e.g.:

using System;
using System.Globalization;

public class HelpTopic
{

  public Guid Id { get; set; }

  public string Name { get; set; }

  public TranslationCollection<HelpTopicTranslation> Translations { get; set; }

  public string Content
  {
    get { return Translations[CultureInfo.CurrentCulture].Content; }
    set { Translations[CultureInfo.CurrentCulture].Content = value; }
  }

  public HelpTopic()
  {
    Id = Guid.NewGuid();
    Translations = new TranslationCollection<HelpTopicTranslation>();
  }

}

With HelpTopicTranslation defined as:

using System;

public class HelpTopicTranslation : Translation<HelpTopicTranslation>
{

  public Guid Id { get; set; }

  public Guid HelpTopicId { get; set; }

  public string Content { get; set; }

  public HelpTopicTranslation()
  {
    Id = Guid.NewGuid();
  }

}

Now, for the code first specific side of things, use the following configuration:

using System.Data.Entity.ModelConfiguration;
using Model;

internal class HelpTopicConfiguration : EntityTypeConfiguration<HelpTopic>
{

  public HelpTopicConfiguration()
  {
    Ignore(x => x.Content); // Ignore HelpTopic.Content since it's a 'computed' field.
    HasMany(x => x.Translations).WithRequired().HasForeignKey(x => x.HelpTopicId);
  }

}

And add it to your context configurations:

public class TestContext : DbContext
{

  public DbSet<HelpTopic> HelpTopics { get; set; }

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Configurations.Add(new HelpTopicConfiguration());
  }

}

When all of this is done, the following migration is generated:

using System.Data.Entity.Migrations;

public partial class AddHelpTopicTable : DbMigration
{

  public override void Up()
  {
    CreateTable(
      "dbo.HelpTopics",
      c => new
      {
        Id = c.Guid(false),
        Name = c.String(),
      })
      .PrimaryKey(t => t.Id);

    CreateTable(
      "dbo.HelpTopicTranslations",
      c => new
      {
        Id = c.Guid(false),
        HelpTopicId = c.Guid(false),
        Content = c.String(),
        CultureName = c.String(),
      })
      .PrimaryKey(t => t.Id)
      .ForeignKey("dbo.HelpTopics", t => t.HelpTopicId, true)
      .Index(t => t.HelpTopicId);
  }

  public override void Down()
  {
    DropForeignKey("dbo.HelpTopicTranslations", "HelpTopicId", "dbo.HelpTopics");
    DropIndex("dbo.HelpTopicTranslations", new[] { "HelpTopicId" });
    DropTable("dbo.HelpTopicTranslations");
    DropTable("dbo.HelpTopics");
  }

}

Any comments and/or improvements are welcome...


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...