一对多关系不会在实体框架中检索数据

maw*_*k_1 2 c# asp.net entity-framework entity-framework-core aspnetboilerplate

我正在学习 C# & .NET 和 EF(使用aspnetboilerplate),我想出了创建一些虚拟项目的想法,以便我可以练习。但过去 4 小时我遇到了这个错误,希望这里有人可以帮助我。

我创建的(至少我认为我创建正确)是两个类,称为“成分”和“大师”

我想用它来将成分分类为“Master”类。

例如成分如

  • 鸡胸肉
  • 鸡腿

它们都属于肉类(女巫在“Master”数据库中输入),这是我的代码

成分.cs

public class Ingrident : Entity
{
    public string Name { get; set; }
    public Master Master { get; set; }
    public int MasterId { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Master.cs

public class Master : Entity
{
  public string Name { get; set; }
  public List<Ingrident> Ingridents { get; set; } = new();    
}
Run Code Online (Sandbox Code Playgroud)

IngridientAppService.cs

public List<IngridientDto> GetIngWithParent()
{
   var result = _ingRepository.GetAllIncluding(x => x.Master);
   //Also I try this but doesn`t work 
   // var result = _ingRepository.GetAll().Where(x => x.MasterId == x.Master.Id);
   return ObjectMapper.Map<List<IngridientDto>>(result);
 }
Run Code Online (Sandbox Code Playgroud)

IngridientDto.cs

[AutoMap(typeof(IndexIngrident.Entities.Ingrident))]
public class IngridientDto : EntityDto
{
    public string Name { get; set; }
    public List<MasterDto> Master { get; set; }
    public int MasterId { get; set; }
 }
Run Code Online (Sandbox Code Playgroud)

MasterDto.cs

[AutoMap(typeof(IndexIngrident.Entities.Master))]
public class MasterDto : EntityDto
{
  public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

当我创建(最后一次练习)M -> M 关系时,这种方法与 .getAllInclusion 一起工作,但现在当我有 One -> Many 时,它就不起作用了。

希望有人能够帮助我,或者至少给我一些好的提示。

祝你今天过得愉快 !

Ste*_* Py 5

直接说,您可能提到的示例(关于存储库等)过于复杂,并且在大多数情况下,这不是您想要实现的。

我看到的第一个问题是,虽然您的实体设置为从 Master 到 Ingredients 的一对多关系,但您的 DTO 是从 Ingredient 到 Masters 设置的,这肯定无法正确映射。

从最简单的事情开始。摆脱存储库并摆脱 DTO。我不确定基类“Entity”的作用是什么,但我猜测它公开了一个名为“Id”的公共关键属性。对于初学者来说,我可能也会放弃它。当涉及到主键时,通常有两种命名方法,每个表使用名为“Id”的 PK,或者每个表使用 TableName 后缀为“Id”的 PK。即“Id”与“IngredientId”。就我个人而言,我发现第二个选项在配对 FK 和 PK 时非常清楚,因为它们具有相同的名称。

当涉及通过导航属性表示关系时,一个重要的细节是确保导航属性链接到其各自的 FK 属性(如果存在),或者更好地使用 FK 的影子属性。

例如,对于您的成分表,摆脱实体基类:

[Table("Ingredients")]
public class Ingredient : Entity
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int IngredientId { get; set; }
    public string Name { get; set; }
    public int MasterId { get; set; }
    [ForeignKey("MasterId")]
    public virtual Master Master { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

此示例使用 EF 属性来帮助告诉 EF 如何将实体属性解析为相应的表和列,以及 Ingredient 和 Master 之间的关系。EF 可以按照约定解决大部分问题,但最好明确地理解和应用它,因为最终您会遇到约定无法按预期工作的情况。

识别(主)键并指示它是一个 Identity 列还告诉 EF 期望数据库将自动填充 PK。(强烈推荐)

在Master方面我们做了类似的事情:

[Table("Masters")]
public class Master : Entity
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int MasterId { get; set; }
    public string Name { get; set; }
    [InverseProperty("Master")]
    public virtual ICollection<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
}
Run Code Online (Sandbox Code Playgroud)

我们再次表示主键,对于我们的 Ingredients 集合,我们告诉 EF 应该使用另一侧(Ingredient)的什么属性来使用该InverseProperty属性关联到此 Master 的 Ingredients 列表。

属性只是设置关系等的一种选项。其他选项是使用实现的配置类IEntityConfiguration<TEntity>(EF Core),或者将它们配置为OnModelCreatingDbContext 中事件的一部分。我只推荐最后一个选项用于非常小的项目,因为它可以很快开始成为一种上帝方法。您可以将其分解为对各种私有方法的调用,但您也可以只使用IEntityConfiguration类。

现在,当你去拿它的主料的原料,或者一个主料及其原料时:

using (var context = new AppDbContext())
{
    var ingredients = context.Ingredients
        .Include(x => x.Master)
        .Where(x => x.Master.Name.Contains("chicken"))
        .ToList();
    // or

    var masters = context.Master
        .Include(x => x.Ingredients)
        .Where(x => x.Name.Contains("chicken"))
        .ToList();

   // ...
}
Run Code Online (Sandbox Code Playgroud)

存储库模式是一个更高级的概念,有一些很好的实施理由,但在大多数情况下它们不是必需的,并且是 EF 实现中的反模式。我认为通用存储库始终是 EF 实现的反模式。即,Repository<Ingredient>使用存储库(尤其是带有 EF 的通用存储库)的主要原因是,您会自动增加实现的复杂性和/或削弱 EF 为您的解决方案带来的功能。正如您从示例中看到的那样,简单地通过急切加载到存储库意味着写入复杂的参数,而这仅涵盖了急切加载。如果没有 EF 提供的开箱即用的这些功能,支持投影、分页、排序等会增加更多样板复杂性或限制您的解决方案和性能。Expression<Func<TEntity>>

考虑研究存储库实现 /w EF 的一些充分理由:

  • 促进单元测试。(存储库比 DbContexts/DbSets 更容易模拟)
  • 集中低级数据规则,例如租赁、软删除和授权。

考虑存储库的一些不好的(尽管很常见)原因:

  • 从对 EF 的依赖关系的引用或知识中抽象代码。
  • 抽象代码以便可以替换 EF。

投影到 DTO 或 ViewModel 是使用 EF 构建高效且安全的解决方案的一个重要方面。目前尚不清楚“ObjectMapper”是什么,它是 Automapper Mapper 实例还是其他东西。Select我强烈建议通过使用 Linq 的语法从模型中填充所需的 DTO 来开始掌握投影。正确使用投影时的第一个关键区别是,当您投影对象图时,您无需担心预先加载相关实体。投影 ( Select) 中引用的任何相关实体/属性将根据需要自动加载。稍后,如果您想利用 Automapper 这样的工具来帮助消除混乱的Select语句,您将需要配置映射配置,然后使用 Automapper 的ProjectTo方法而不是Map. ProjectTo与 EF 的IQueryable实现一起工作,将您的映射解析为 SQL Select,就像那样,其中Map需要返回所有急切加载的内容以便填充相关数据。ProjectTo并且Select可以产生比急切加载整个对象图更有效的查询,可以更好地利用索引。(数据库和服务器/应用程序之间的线路上的数据较少) Map仍然非常有用,例如您想要将值从 DTO 复制回加载的实体的场景。