简体   繁体   English

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

[英]One to many relationship doesn`t retrieve data in entity framework

I`m in process of learning C# & .NET and EF (with aspnetboilerplate ) and I came up with idea to create some dummy project so I can practice.我正在学习 C# & .NET 和 EF(使用aspnetboilerplate ),我想出了创建一些虚拟项目的想法,以便我可以练习。 But last 4 hour Im stuck with this error and hope someone here can help me.但是最后 4 小时我坚持这个错误,希望这里有人可以帮助我。

What I create( well at least I think I create it correctly ) is 2 class called "Ingredient" and "Master"我创建的(至少我认为我正确地创建了它)是 2 个名为“成分”和“大师”的类

I want to use it for categorize Ingredient with "Master" class.我想用它对“大师”类的成分进行分类。

For example ingredient like例如成分像

  • Chicken breast鸡胸肉
  • chicken drumstick鸡腿

Both of them belong to Meat ( witch is input in "Master" database ) and here is my code它们都属于 Meat(女巫在“Master”数据库中输入),这是我的代码

Ingredient.cs成分.cs

   public class Ingrident : Entity
{

    public string Name { get; set; }
    public Master Master { get; set; }
    public int MasterId { get; set; }

}

Master.cs硕士.cs

public class Master : Entity

    {
        public string Name { get; set; }
        public List<Ingrident> Ingridents { get; set; } = new();
    
    }

IngridientAppService.cs 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 MasterDto.cs

    [AutoMap(typeof(IndexIngrident.Entities.Master))]

    public class MasterDto : EntityDto
    {
     public string Name { get; set; }
    }

When I created ( for last practice ) M -> M relationship this approach with .getAllIncluding work but now when I have One -> Many it won`t work.当我创建(最后一次练习)M -> M 关系时,这种方法与 .getAllIn包括工作,但现在当我有一个 -> 许多时,它就行不通了。

Hope someone will be able to help me or at least give me some good hint.希望有人能够帮助我或至少给我一些好的提示。

Have a nice day !祝你今天过得愉快 !

Straight up the examples you are probably referring to (regarding the repository etc.) are overcomplicated and for most cases, not what you'd want to implement.直接说明您可能引用的示例(关于存储库等)过于复杂,并且在大多数情况下,不是您想要实现的。

The first issue I see is that while your entities are set up for a 1-to-many relationship from Master to Ingredients, your DTOs are set up from Ingredient to Masters which definitely won't map properly.我看到的第一个问题是,虽然您的实体设置为从主到成分的一对多关系,但您的 DTO 是从成分到主设置的,这肯定不会正确映射。

Start with the simplest thing.从最简单的事情开始。 Get rid of the Repository and get rid of the DTOs.摆脱存储库并摆脱 DTO。 I'm not sure what the base class "Entity" does, but I'm guessing it exposes a common key property called "Id".我不确定基类“Entity”的作用,但我猜它公开了一个名为“Id”的通用键属性。 For starters I'd probably ditch that as well.对于初学者,我可能也会放弃。 When it comes to primary keys there are typically two naming approaches, every table uses a PK called "Id", or each table uses a PK with the TableName suffixed with "Id".当涉及到主键时,通常有两种命名方法,每个表都使用一个名为“Id”的 PK,或者每个表使用一个表名后缀为“Id”的 PK。 Ie "Id" vs. "IngredientId".即“Id”与“IngredientId”。 Personally I find the second option makes it very clear when pairing FKs and PKs given they'd have the same name.我个人认为第二个选项在配对 FK 和 PK 时非常清楚,因为它们具有相同的名称。

When it comes to representing relationships through navigation properties one important detail is ensuring navigation properties are linked to their respective FK properties if present, or better, use shadow properties for the FKs.当谈到通过导航属性表示关系时,一个重要的细节是确保导航属性链接到它们各自的 FK 属性(如果存在),或者更好的是,为 FK 使用阴影属性。

For example with your Ingredient table, getting rid of the Entity base class:例如,对于您的 Ingredient 表,摆脱 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; }
}

This example uses EF attributes to aid in telling EF how to resolve the entity properties to respective tables and columns, as well as the relationship between Ingredient and Master.此示例使用 EF 属性来帮助告诉 EF 如何将实体属性解析为相应的表和列,以及 Ingredient 和 Master 之间的关系。 EF can work much of this out by convention, but it's good to understand and apply it explicitly because eventually you will come across situations where convention doesn't work as you expect. EF 可以通过约定来解决大部分问题,但最好理解并明确应用它,因为最终您会遇到约定无法按预期工作的情况。

Identifying the (Primary)Key and indicating it is an Identity column also tells EF to expect that the database will populate the PK automatically.识别(主)键并指出它是一个身份列也告诉 EF 期望数据库将自动填充 PK。 (Highly recommended) (强烈推荐)

On the Master side we do something similar:在 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>();
}

Again we denote the Primary Key, and for our Ingredients collection, we tell EF what property on the other side (Ingredient) it should use to associate to this Master's list of Ingredients using the InverseProperty attribute.我们再次表示主键,对于我们的成分集合,我们告诉 EF 在另一侧(成分)应该使用什么属性来关联到使用InverseProperty属性的 Master 的成分列表。

Attributes are just one option to set up the relationships etc. The other options are to use configuration classes that implement IEntityConfiguration<TEntity> (EF Core), or to configure them as part of the OnModelCreating event in the DbContext.属性只是设置关系等的一个选项。其他选项是使用实现IEntityConfiguration<TEntity> (EF Core) 的配置类,或将它们配置为OnModelCreating事件的一部分。 That last option I would only recommend for very small projects as it can start to become a bit of a God method quickly.最后一个选项我只推荐用于非常小的项目,因为它可以很快开始成为一种上帝的方法。 You can split it up into calls to various private methods, but you may as well just use IEntityConfiguration classes then.您可以将其拆分为对各种私有方法的调用,但您也可以只使用IEntityConfiguration类。

Now when you go to fetch Ingredients with it's Master, or a Master with its Ingredients:现在,当您使用 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();

   // ...
}

Repository patterns are a more advanced concept that have a few good reasons to implement, but for the most part they are not necessary and an anti-pattern within EF implementations.存储库模式是一个更高级的概念,有几个很好的理由来实现,但在大多数情况下,它们不是必需的,并且是 EF 实现中的反模式。 I consider Generic repositories to always be an anti-pattern for EF implementations.我认为通用存储库始终是 EF 实现的反模式。 Ie Repository<Ingredient> The main reason not to use repositories, especially Generic repositories with EF is that you are automatically increasing the complexity of your implementation and/or crippling the capabilities that EF can bring to your solution.Repository<Ingredient>使用存储库,尤其是 EF 的通用存储库的主要原因是您会自动增加实现的复杂性和/或削弱 EF 可以为您的解决方案带来的功能。 As you see from working with your example, simply getting across an eager load through to the repository means writing in complex Expression<Func<TEntity>> parameters, and that just covers eager loading.正如您在使用示例时看到的那样,简单地将急切加载传递到存储库意味着编写复杂的Expression<Func<TEntity>>参数,而这仅涵盖了急切加载。 Supporting projection, pagination, sorting, etc. adds even more boiler-plate complexity or limits your solution and performance without these capabilities that EF can provide out of the box.如果没有 EF 开箱即用的这些功能,支持投影、分页、排序等会增加更多样板复杂性或限制您的解决方案和性能。

Some good reasons to consider studying up on repository implementations /w EF:考虑研究存储库实现 /w EF 的一些充分理由:

  • Facilitate unit testing.促进单元测试。 (Repositories are easier to mock than DbContexts/DbSets) (存储库比 DbContexts/DbSets 更容易模拟)
  • Centralizing low-level data rules such as tenancy, soft deletes, and authorization.集中低级数据规则,例如租赁、软删除和授权。

Some bad (albeit very common) reasons to consider repositories:考虑存储库的一些不好(尽管很常见)的原因:

  • Abstracting code from references or knowledge of the dependency on EF.从对 EF 的依赖关系的引用或知识中提取代码。
  • Abstracting the code so that EF could be substituted out.抽象代码,以便可以替换 EF。

Projecting to DTOs or ViewModels is an important aspect to building efficient and secure solutions with EF.投影到 DTO 或 ViewModel 是使用 EF 构建高效且安全的解决方案的一个重要方面。 It's not clear what "ObjectMapper" is, whether it is an Automapper Mapper instance or something else.目前尚不清楚“ObjectMapper”是什么,无论它是 Automapper Mapper 实例还是其他东西。 I would highly recommend starting to grasp projection by using Linq's Select syntax to fill in a desired DTO from the models.我强烈建议通过使用 Linq 的Select语法从模型中填充所需的 DTO 来开始掌握投影。 The first key difference when using Projection properly is that when you project an object graph, you do not need to worry about eager loading related entities.使用投影时,正确的第一个关键的区别是,当你的项目的对象图,你不必急于左右装载相关实体的担心。 Any related entity / property referenced in your projection ( Select ) will automatically be loaded as necessary.投影 ( Select ) 中引用的任何相关实体/属性将根据需要自动加载。 Later, if you want to leverage a tool like Automapper to help remove the clutter of Select statements, you will want to configure your mapping configuration then use Automapper's ProjectTo method rather than Map .稍后,如果您想利用 Automapper 之类的工具来帮助消除Select语句的混乱,您将需要配置映射配置,然后使用 Automapper 的ProjectTo方法而不是Map ProjectTo works with EF's IQueryable implementation to resolve your mapping down to the SQL just like Select does, where Map would need to return everything eager loaded in order to populate related data. ProjectTo与 EF 的IQueryable实现一起使用,将您的映射解析为 SQL,就像Select一样,其中Map需要返回所有预先加载的内容以填充相关数据。 ProjectTo and Select can result in more efficient queries that can better take advantage of indexing than Eager Loading entire object graphs. ProjectToSelect可以产生更高效的查询,与 Eager Loading 整个对象图相比,可以更好地利用索引。 (Less data over the wire between database and server/app) Map is still very useful such as scenarios where you want to copy values back from a DTO into a loaded entity. (数据库和服务器/应用程序之间的线路上的数据较少) Map仍然非常有用,例如您希望将值从 DTO 复制回已加载实体的场景。

Do it like this像这样做

public class Ingrident:Entity
{

    public string Name { get; set; }
    [ForeignKey(nameof(MasterId))]
    public Master Master { get; set; }
    public int MasterId { get; set; }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM