【问题标题】:mapping T from DbSet<T> into Dabase internal representation using EF使用 EF 将 T 从 DbSet<T> 映射到 Dabase 内部表示
【发布时间】:2023-04-01 17:57:01
【问题描述】:

我是 .NET 和 Entity Framework 的初学者,只是关于映射的问题,假设我们有 Recipes 类,其属性是成分,所以我们有两个类 Recipes 和成分。

我们通过以下方式进行映射:

public DbSet<Recipe> Recipes { get; set; }

所以 EF 将检查 Recipe 的属性并创建一个 Recipes 表,最终它会发现成分作为属性,因此它将创建一个成分表。 到目前为止看起来很简单,但这是一个非常简单的结构,所以我们知道Recipes包含成分,所以我们不喜欢

public DbSet<Recipe> Recipes { get; set; }
public DbSet<Ingredient> Ingredient { get; set; } //duplicated 

但是想象一下,在一个复杂的应用程序中,我们可能有数百个类,所以当我们这样做时

public DbSet<T> T{ get; set; }
public DbSet<U> U{ get; set; }
public DbSet<V> V{ get; set; }
...

T 可能包含 U 作为属性,U 可能包含 V 作为属性, 所以我们只需要编写如下代码:

public DbSet<T> T{ get; set; }

所以我很困惑,我们应该在 DbContext 中包含所有类吗?如果我们这样做,将会有重复的声明,但如果我们不这样做,我们可能会丢失映射。 所以我想我们只是为所有类做 tat 并期望 EF 足够聪明以忽略重复的东西?

【问题讨论】:

    标签: c# asp.net entity-framework entity-framework-core


    【解决方案1】:

    EF 足够聪明,不会创建重复的表,最好包含所有DbSet&lt;&gt;

    当您有一个名称为 Ingredient 的类并且您在 Recept 中引用它时,将只有一个名称为 Ingredient 的表,并且您不能有两个同名的表,因此不会有重复的表即使您也想创建。

    【讨论】:

      【解决方案2】:

      您必须知道您的 dbContext 代表您的数据库。 DbSet 代表数据库中的表

      在实体框架中,表的列由类的非虚拟属性表示。虚拟属性表示表之间的关系(一对多,多对多,...)

      您的数据库将包含RecipesIngredient。每个Recipe 都会有零个或多个Ingredients,而每个Ingredient 会使用零个或多个Recipes:简单的多对多关系。

      您将拥有表 RecipesIngredients,因此您的 dbContext 将为它们提供 DbSets

      class MyDbContext : DbContext
      {
          public DbSet<Recipe> Recipes {get; set;}
          public DbSet<Ingredient> Ingredients {get; set;}
      }
      
      class Recipe
      {
          public int Id {get; set;}  // primary key
          ...                        // other columns
      
          // every Recipe has zero or more ingredients (many-to-many)
          public virtual ICollection<Ingredient> Ingredients {get; set;}
      }
      
      class Ingredient
      {
          public int Id {get; set;}  // primary key
          ...                        // other columns
      
          // every Ingredient is used in zero or more Recipes( many-to-many)
          public virtual ICollection<Recipe> Recipes {get; set;}
      }
      

      因为我在两边都使用了virtual,Recipes 和 Ingredients,实体框架可以检测到我设计了一个多对多。实体框架甚至会为我创建一个联结表,我不必声明它。

      // Fetch all Desserts with their Bitter ingredients
      var desertsWithoutSugar = myDbContext.Recipes
          .Where(recipe => recipy.Type == RecipyType.Dessert)
          .Select(recipe => new
          {
              // select only the properties that I plan to use:
              Id = recipe.Id,
              Name = recipe.Name,
              ...
              // not needed: you know the value: Type = recipe.Type
      
              Ingredients = recipe.Ingredients
                 .Where(ingredient => ingredient.Taste == Taste.Bitter)
                 .Select(ingredient => new
                 {
                     // again: only the properties you plan to use
                     Id = ingredient.Id,
                     Name = ingredient.Name,
                 })
                 .ToList(),
           })
      

      实体框架知道您的多对多关系,并且足够聪明,可以检测到三个表(包括联结表)需要(组)联接。

      如果你想设计一个一对多的关系,例如一个学校和他的学生,你只在一侧声明virtual ICollection。另一方得到外键。这是您表中的一列,因此是非虚拟的

      class School
      {
          public int Id {get; set;}
          ...                     
      
          // every School has zero or more Students (one-to-many)
          public virtual ICollection<Student> Students {get; set;}
      }
      class Student
      {
          public int Id {get; set;}
          ...   
      
          // Every Student studies at one School, using foreign key
          public int SchoolId {get; set;}
          public virtual School School {get; set;}
      }
      public SchoolContext : DbContext
      {
          public DbSet<School> Schools {get; set;}
          public DbSet<Student> Students {get; set;}
      }
      

      根据我的经验,由于我使用实体框架,我几乎不必再进行 (Group-)join,我使用集合:

      给我所有学校和他们的学生

      var Result = dbContext.Schools
          .Where(school => ...)
          .Select(school => new
          {
              Id = school.Id,
              ...
      
              Students = school.Students
                  .Where(student => ...)
                  .Select(student => new
                  {
                       Id = student.Id,
                       ...
                  })
                  .ToList(),
          });
      

      类似的:给我他们就读的学校的所有学生

      【讨论】:

        【解决方案3】:

        正如您已经说过的,EF 将在 Recipe 属性中找到 Ingredient 类型并创建 Ingredients 表。这意味着,即使您遗漏了public DbSet&lt;Ingredient&gt; Ingredient { get; set; },也会创建Ingredients 表。然而,仅仅因为你不需要public DbSet&lt;Ingredient&gt; Ingredient { get; set; },并不意味着你不能拥有它。您可以随意包含它,它不会造成任何伤害,也不会创建任何重复项。

        public DbSet&lt;Ingredient&gt; Ingredient { get; set; } 添加到您的DbContext 允许您直接从上下文访问Ingredient 条目:

        MyContext context = ...
        var ingredients = context.Ingredients;
        
        

        否则,您将不得不通过Recipes 属性访问Ingredient 条目:

        MyContext context = ...
        var recipe = context.Recipes...;
        var ingredients = recipe.Ingredients;
        

        总结:什么时候应该在 DbContext 中包含该类?

        • 当该类未被属于DbContext 的任何其他类引用时。将类添加到 DbContext 将确保它被映射。

        • 当您希望能够直接从 DbContext 实例访问该类的条目时。例如。 var ingredients = context.Ingredients

        • 当您想确保该类将被映射,并且不想依赖它被其他映射类引用时。

        那么,回答您的问题“我们应该在 DbContext 中包含所有类吗?”: 你可以,这是完全好的方法。这样,您将确保所有类(您想要被映射)都将被映射,而不必依赖它们被其他类引用。

        【讨论】:

          猜你喜欢
          • 2016-05-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-06
          • 1970-01-01
          相关资源
          最近更新 更多