实体拆分一对多表关系

时间:2014-07-16 08:07:53

标签: linq entity-framework split entity multilingual

按照这篇文章(What are best practices for multi-language database design?),我将所有数据库表分成两部分:第一个表只包含语言中性数据(主键等),第二个表包含每种语言一个记录,包含本地化数据加上语言的ISO代码。两个表之间的关系是一对多的。 这是数据模型的屏幕截图:https://dl.dropboxusercontent.com/u/17099565/datamodel.jpg

因为网站有8种语言,对于表格中的每条记录" CourseCategory"我在表格中有8条记录" CourseCategoryContents"。同样的情况发生在"课程"和" CourseContent"

然后我使用Entity Splitting,只有一个实体用于课程类别,一个实体用于课程:

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

然后用所需的语言(包括他们的类别)来检索课程,我这样做:

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

实体拆分与一对一的关系很好,但在这里我有一对多的关系。

该网站有3种语言的内容(课程类别和课程)(&#34; en&#34;,&#34; de&#34;,&#34; fr&#34;)。 EF正确地以正确的语言返回所有课程及其类别(例如英语),但每次返回记录3次。这是因为我也有3种语言的CourseCategory。

我提出的唯一一个工作解决方案是避免使用&#34;。包括(类别)&#34;,首先使用所需语言获取所有课程,然后在foreach周期中为每个课程重新学习它的语言分类。我不喜欢这种延迟加载的方法,我想一次性检索所有想要的数据。

谢谢!

2 个答案:

答案 0 :(得分:0)

没有解决方法 - 所有答案我都害怕,每一种方式都有妥协。

我已经在相当大的项目中使用了数据库方法(10+语言相关表)和资源文件方法,如果数据是静态的并且没有变化(即你不要收取不同的价格或其他)我肯定会考虑从您的数据库模型中抽象出语言并使用资源键然后从文件中加载数据。

原因或者这就是您现在遇到的问题,您可以在其中过滤包含(这可能在EF6中有所改变?我知道它在要做的事情列表中)。你可能能够将它读入内存并过滤它们,就像你正在做的那样,但这意味着它对我们来说并不是非常高效,而且我必须编写我刚刚通过iso语言的存储过程并在EF中执行。

从维护的角度来看,它也更容易,对于DB项目我必须编写管理控制台,以便人们可以登录并编辑不同语言的值等。使用资源文件我只是将值复制粘贴到excel并通过电子邮件发送给我们用来翻译的人。

这取决于项目的复杂程度和您的偏好,我将来仍然会考虑这两种方法。

TLDR:我发现的选项是:

1)在内存中过滤 2)带过滤器的延迟负载 3)将存储过程写入EF并映射该结果 4)改用资源

希望这有帮助

编辑:看完图后,您可能需要搜索与语言相关的值?在那种情况下,资源可能不会起作用。如果您只是让他们离开菜单,那么您就可以了。

答案 1 :(得分:0)

最佳解决方案是将表格映射到模型,然后在模型中Course类将具有导航属性ICollection<CourseCategoryContent>

在这种情况下,您只需根据应用程序设计将此模型投影到DTO或ViewModel

e.g。 你的模型看起来像这样

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

然后只需创建新的DTO或ViewModel:

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

最后做投影

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

请注意,返回类型是IQueryable,因此您可以在访问数据库之前对其执行任何过滤,排序或分组操作。

希望这有帮助