一对多关系不会在实体框架中检索数据
Posted
技术标签:
【中文标题】一对多关系不会在实体框架中检索数据【英文标题】:One to many relationship doesn`t retrieve data in entity framework 【发布时间】:2021-11-17 21:15:34 【问题描述】:我正在学习 C# & .NET 和 EF(使用 aspnetboilerplate),我想出了创建一些虚拟项目的想法,以便我可以练习。但是过去 4 小时我遇到了这个错误,希望这里有人可以帮助我。
我创建的(至少我认为我正确地创建了它)是 2 个名为“Ingredient”和“Master”的类
我想用它来分类“大师”类的成分。
例如像这样的成分
鸡胸肉 鸡腿它们都属于 Meat(女巫在“Master”数据库中输入),这是我的代码
成分.cs
public class Ingrident : Entity
public string Name get; set;
public Master Master get; set;
public int MasterId get; set;
Master.cs
public class Master : Entity
public string Name get; set;
public List<Ingrident> Ingridents get; set; = new();
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);
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;
MasterDto.cs
[AutoMap(typeof(IndexIngrident.Entities.Master))]
public class MasterDto : EntityDto
public string Name get; set;
当我创建(最后一次练习)M -> M 关系时,这种方法与 .getAllIncluding 工作但现在当我有一个 -> 很多它不会工作。
希望有人能够帮助我,或者至少给我一些好的提示。
祝你有美好的一天!
【问题讨论】:
在 GitHub 上创建一个从 aspnetboilerplate/module-zero-core-template 派生的 repro 项目。 【参考方案1】:直截了当地提到的示例(关于存储库等)过于复杂,并且在大多数情况下,这不是您想要实现的。
我看到的第一个问题是,虽然您的实体设置为从 Master 到 Ingredients 的一对多关系,但您的 DTO 是从 Ingredient 到 Masters 设置的,这肯定不会正确映射。
从最简单的事情开始。摆脱存储库并摆脱 DTO。我不确定基类“Entity”的作用,但我猜它公开了一个名为“Id”的通用键属性。对于初学者,我可能也会放弃它。当涉及到主键时,通常有两种命名方法,每个表使用一个名为“Id”的 PK,或者每个表使用一个 TableName 后缀为“Id”的 PK。 IE。 “Id”与“IngredientId”。就我个人而言,我发现第二个选项在配对 FK 和 PK 时非常清楚,因为它们具有相同的名称。
在通过导航属性表示关系时,一个重要的细节是确保导航属性链接到它们各自的 FK 属性(如果存在),或者更好的是,为 FK 使用阴影属性。
以您的成分表为例,去掉 Entity 基类:
[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;
此示例使用 EF 属性来帮助告诉 EF 如何将实体属性解析为相应的表和列,以及成分和主对象之间的关系。 EF 可以通过约定解决大部分问题,但最好理解并明确应用它,因为最终您会遇到约定无法按预期工作的情况。
识别(主)键并指示它是一个身份列也告诉 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>();
我们再次表示主键,对于我们的成分集合,我们使用 InverseProperty
属性告诉 EF 它应该使用另一侧(成分)的哪个属性与此主成分列表相关联。
属性只是设置关系等的一个选项。其他选项是使用实现IEntityConfiguration<TEntity>
(EF Core) 的配置类,或者将它们配置为 DbContext 中OnModelCreating
事件的一部分。最后一个选项我只推荐用于非常小的项目,因为它可以很快开始成为一种上帝的方法。您可以将其拆分为对各种私有方法的调用,但您也可以使用 IEntityConfiguration
类。
现在,当你带着它的 Master 或 Master 带着它的原料去取成分时:
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();
// ...
存储库模式是一个更高级的概念,有一些很好的实施理由,但在大多数情况下,它们不是必需的,并且是 EF 实施中的反模式。我认为始终的通用存储库是 EF 实现的反模式。 IE。 Repository<Ingredient>
不使用存储库的主要原因,尤其是带有 EF 的通用存储库是您自动增加了实现的复杂性和/或削弱了 EF 可以为您的解决方案带来的功能。正如您从使用示例中看到的那样,简单地将急切加载传递到存储库意味着编写复杂的Expression<Func<TEntity>>
参数,而这仅涵盖急切加载。如果没有 EF 开箱即用的这些功能,支持投影、分页、排序等会增加更多样板复杂性或限制您的解决方案和性能。
考虑研究存储库实现 /w EF 的一些充分理由:
促进单元测试。 (存储库比 DbContexts/DbSets 更容易模拟) 集中低级数据规则,例如租赁、软删除和授权。考虑存储库的一些不好(尽管很常见)的原因:
从引用或对 EF 的依赖知识中提取代码。 抽象代码以便替换 EF。投影到 DTO 或 ViewModel 是使用 EF 构建高效且安全的解决方案的一个重要方面。目前尚不清楚“ObjectMapper”是什么,它是 Automapper Mapper 实例还是其他东西。我强烈建议通过使用 Linq 的 Select
语法从模型中填充所需的 DTO 来开始掌握投影。正确使用 Projection 的第一个关键区别是,当您投影对象图时,您确实不需要担心急切加载相关实体。投影中引用的任何相关实体/属性 (Select
) 将根据需要自动加载。稍后,如果您想利用 Automapper 之类的工具来帮助消除 Select
语句的混乱,您将需要配置映射配置,然后使用 Automapper 的 ProjectTo
方法而不是 Map
。 ProjectTo
与 EF 的 IQueryable
实现一起使用,将您的映射解析为 SQL,就像 Select
一样,Map
需要返回所有急切加载的内容以填充相关数据。 ProjectTo
和 Select
可以产生更有效的查询,与急切加载整个对象图相比,它们可以更好地利用索引。 (在数据库和服务器/应用程序之间通过网络传输的数据更少)Map
仍然非常有用,例如您希望将值从 DTO 复制回加载的实体的场景。
【讨论】:
【参考方案2】:这样做
public class Ingrident:Entity
public string Name get; set;
[ForeignKey(nameof(MasterId))]
public Master Master get; set;
public int MasterId get; set;
【讨论】:
以上是关于一对多关系不会在实体框架中检索数据的主要内容,如果未能解决你的问题,请参考以下文章