1

Following this article (What are best practices for multi-language database design?), I have all my database tables splitted in two: the first table contains only language-neutral data (primary key, etc.) and the second table contains one record per language, containing the localized data plus the ISO code of the language. The relationship between the two tables is one to many. Here a screenshot of the datamodel: https://dl.dropboxusercontent.com/u/17099565/datamodel.jpg

Because the website has 8 languages, for each record in table "CourseCategory" I have 8 record in table "CourseCategoryContents". The same happens with "Course" and "CourseContent"

Then I use Entity Splitting in order to have only one entity for the Course Category and one entity for the Course:

public class CourseCategoryConfiguration : EntityTypeConfiguration<WebCourseCategory>
      {
          public CourseCategoryConfiguration()
          {
              Map(m =>
              {
                  m.Properties(i => new { i.Id, i.Order, i.Online });
                  m.ToTable("CourseCategories");
              });

              Map(m =>
              {
                  m.Properties(i => new { i.LanguageCode, i.Name, i.Permalink, i.Text, i.MetaTitle, i.MetaDescription, i.MetaKeywords });
                  m.ToTable("CourseCategoryContents");
              });
          }
      }

      public class CourseConfiguration : EntityTypeConfiguration<WebCourse>
      {
          public CourseConfiguration()
          {
              Map(m =>
              {
                  m.Properties(i => new { i.Id, i.CategoryId, i.Order, i.Label, i.ThumbnailUrl, i.HeaderImageUrl });
                  m.ToTable("Courses");
              });

              Map(m =>
              {
                  m.Properties(i => new { i.LanguageCode, i.Name, i.Permalink, i.Text, i.MetaTitle, i.MetaDescription, i.MetaKeywords, i.Online });
                  m.ToTable("CourseContents");
              });
          }
      }

Then to retrive the courses in a desired language including their category I do this:

using (WebContext dbContext = new WebContext())
{
  // all courses of all categories in the desired language
          return dbContext.Courses
              .Include(course => course.Category)
              .Where(course => course.LanguageCode == lan
                     && course.Category.LanguageCode == lan)
              .ToList();
      }
}

Entity splitting works fine with one-to-one relationships, but here I have one-to-many relationships.

The website has contents (CourseCategories and Courses) in 3 languages ("en", "de", "fr"). EF correctly returns all the Courses with their Category in the right language (eg. in english), but returns each record 3 times. This is because I have the CourseCategory in 3 languages too.

The only one working solution I came up is avoiding using ".Include(Category)", getting all the courses in the desired language in first, then, in a foreach cycle, for each Course retriving its Category in language. I don't like this lazy loading approach, I would like to retrive all the desired data in one shot.

Thanks!

Community
  • 1
  • 1
Bruno M.
  • 363
  • 1
  • 5

2 Answers2

0

No fix-all answer i'm afraid, every way has a compromise.

I've used both the database approach (10+ language dependent tables) and the resource file approach in fairly large projects, if the data is static and doesn't change (i.e you don't charge a different price or whatever) I would definately consider abstracting language away from your database model and using Resource keys then loading your data from files.

The reason or this is the problem you are experiencing right now where you can't filter includes (this may have changed in EF6 perhaps? I know it's on the list of things to do). You might be able to get away with reading it into memory and filtering them though like you're doing but this meant it wasn't very performant for us and I had to write Stored Procedures that I just passed the iso language and executed in EF.

From a maintenance point of view it was easier as well, for the DB project I had to write an admin console so people could log on and edit values for different languages etc. Using resource files I just copy-pasted the values into excel and emailed them to the people we use to translate.

It depends on the complexity of your project and what you prefer, i'd still consider both approaches in future.

TLDR: options that i've found are:

1) filter in memory 2) lazy load with filter 3) write stored procedure to EF and map that result 4) use resources instead

Hope this helps

EDIT: After looking at diagram it looks like you may need to search against the language dependant values? In that case resources probably won't work. If you're just letting them navigate off a menu then you're good to go.

Peter Lea
  • 1,731
  • 2
  • 15
  • 24
0

The best solution is to map tables to the model as it then in your model Course class will have a navigation property ICollection<CourseCategoryContent>.

In this case you just project this model to DTO or ViewModel "according to your application design"

e.g. Your model will look like this

public class Course
{
    public int Id {get; set;}
    public int Order {get; set;}
    public ICollection<CourseCategoryContent> CourseCategoryContents {get; set;}
}

public class CourseCategoryContent
{
    public string LanguageId {get; set;}
    public string Name {get; set;}
}

Then just create new DTO or ViewModel like :

public class CourseDTO
{
    public int Id {get; set;}
    public int Order {get; set;}
    public string Name {get; set;}
}

Finally do the projection

public IQueryable<CourseDTO> GetCourseDTOQuery ()
{  
    return dbContext.Courses.Select(x=>new CourseDTO{
    Id = x.Id,
    Order = x.Order,
    Name = x.CourseCategoryContents.FirstOrDefault(lang => lang.LanguageId == lang).Name,
    });      
} 

And note that the return type is IQueryable so you could do any filter, Order or grouping operation on it before hitting the database.

hope this helped

Wahid Bitar
  • 13,776
  • 13
  • 78
  • 106