【问题标题】:Collection in model missing the data模型中的集合缺少数据
【发布时间】:2019-09-03 21:44:17
【问题描述】:

我有这个管理餐厅和评论的页面。我可以完美地创建餐厅和评论,但是当我想将评论链接到餐厅中的集合时,从视图中获取它时会消失

这是我的餐厅模型,我想在其中存储每家餐厅的评论:

public class Restaurant
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string City { get; set; }
    public string Country { get; set; }
    public float AverageRating { get; set; }
    public DateTime Data { get; set; }
    public virtual ICollection<Review> Reviews { get; set; }
}

审查模型:

public class Review
{
    public int ID { get; set; }
    public int Rating { get; set; }
    public string Body { get; set; }
    public string ReviewerName { get; set; }
    public int RestaurantID { get; set; }
    public DateTime Data { get; set; }
}

API 帖子:

public IActionResult Post(ReviewDTO r)
{
    try
    {
        if (!ModelState.IsValid)
            return BadRequest("Not a valid model");
        else
        {
            Review review = _mapper.Map<ReviewDTO, Review>(r);
            //review.Restaurant = _restaurantAppService.Find(review.RestaurantID);
            _reviewAppService.Add(review);

            //_restaurantAppService.Find(review.RestaurantID).Reviews.Add(review);
            _restaurantAppService.Average(_restaurantAppService.Find(review.RestaurantID));
            return Ok();
        }
    }
    catch (Exception ex)
    {
        //Log.Log.Error("Api error - Review - Post::" + ex.Message + "::" + ex.InnerException.Message + "::" + ex.InnerException.InnerException.Message);
        throw new Exception("Error Api - Review - Post \n", ex);
    }
}

最后是存储库:

public void Add(TEntity e)
{
    try
    {
        db.Set<TEntity>().Add(e);
        db.SaveChanges();
    }
    catch (Exception ex)
    {
        throw new Exception("Error in Infra.DATA - Add" + ex.Message, ex);
    }
}

public IEnumerable<TEntity> All()
{
    try
    {
        return db.Set<TEntity>().AsNoTracking().ToList();
    }
    catch (Exception ex)
    {
        throw new Exception("Error in Infra.DATA - All", ex);
    }
}

那么问题来了:

  • 评论已创建
  • API 调用应用程序方法
  • 实体类将评论插入数据库
  • api调用应用方法将评论插入到餐厅列表'reviews'中
  • 评论插入成功,调试可以看到数据库中餐厅更新了评论插入
  • 控制器发回餐厅以查看餐厅的详细信息,其中列出了其评论,但在此 步骤,评论丢失并设置为空。

我可以编辑、删除和创建餐厅和评论,但我无法将它们与餐厅一起存储。

我在框架而不是核心中完成了同样的项目并且没有问题,有人知道为什么会发生这种情况吗?

【问题讨论】:

  • 可以在选择餐厅的地方添加控制器/服务代码吗?

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


【解决方案1】:

很难准确地说出你在做什么,因为你没有展示你实际上是如何获得餐馆列表的。但是,我假设您在实际上不是存储库的存储库上使用 All() 方法。

首先,对于您的主要问题。 EF Core 从不自动查询相关实体。您必须通过急切、显式或延迟加载关系来指示它这样做。

  1. 预加载是首选方法,因为 EF 会按照您的预期发出 JOIN,并且所有信息都将在单个查询中返回。这需要使用 Include 方法(您的“存储库”没有功能)。无论如何,该代码看起来像:

    var restaurants = await _context.Restaurants.Include(x => x.Reviews).ToListAsync();
    
  2. 显式加载值得注意,但它很少使用。这种方法的唯一真正好处是能够实际过滤返回的相关项目,而不是只返回所有项目。

    var reviews = await _context.Entry(restaurant).Collection(x => x.Reviews).LoadAsync();
    

    如果您想过滤评论:

    var reviews = await _context.Entry(restaurant).Collection(x => x.Reviews).Query()
        .Where(x => x.Rating > 3)
        .ToListAsync();
    
  3. 最后,还有延迟加载。这是不推荐的,应该诚实地避免。它的工作方式是 EF 将动态派生实体的代理类并覆盖集合属性上的 getter 以从上下文的对象缓存中请求相关项目,或者如果它们不存在,则发出查询以获取它们.然后,这实际上具有将数据库查询隐藏在幕后的效果。您只需与相关项目进行交互,EF 就会神奇地去获取它们,以便它们在那里。然而,这也是这种方法的问题。在没有意识到的情况下,用查询来锤击你的数据库太容易太容易了。例如,如果您遍历所有餐厅,然后访问每个集合评论,则您正在为每个餐厅单独发出查询。如果您在多个级别进行挖掘,情况会呈指数级恶化。就像假设您有一个写评论的用户的外键,然后您可以访问它。然后,您将针对每家餐厅的每条评论发出一个查询,这很容易达到数百甚至数千个查询。

    不过,如果你想走这条路,你需要两件事:1)集合属性必须是 virtual,它已经是,2)你必须明确打开它:

    services.AddDbContext<MyContext>(o =>
        o.UseSqlServer(Configuration.GetConnectionString("Default"))
         .UseLazyLoadingProxies());
    

正如我提到的,您的“存储库”在这里会出现问题。您最大的问题是存储库实际上应该返回可查询对象(即支持在将查询发送到数据库之前对其进行进一步更改)。相反,您正在返回具体化列表(即查询已经发送)。然后,更进一步,您实际上根本不应该在此处拥有存储库,因为 EF 已经提供了工作单元和存储库模式。拥有自己的层只会增加额外的维护和测试问题,使您的应用更脆弱,更难使用。

【讨论】:

  • 就是这样!我必须覆盖 All() 方法,但是在使用 Include 时,它​​与实体相关。我还将方法更改为它们是查询,老实说这更有意义。一切正常,谢谢。
【解决方案2】:

使用 EF 时,您可以在数据库上下文的 OnModelCreating 方法中定义关系或在模型类中设置。我建议在大多数情况下在模型本身上设置这种关系,期望多对多的关系场景,因为这不是你的情况,你可以执行以下操作:

public class Review
{
        public int ID { get; set; }
        public int Rating { get; set; }
        public string Body { get; set; }
        public string ReviewerName { get; set; }
        public int RestaurantID { get; set; }
        public DateTime Data { get; set; }

        public Restaurant Restaurant { get; set; }
}

现在运行 dotnet ef add migration 命令生成迁移,这将在 Restaurant 和 Review 之间创建一对多关系

【讨论】:

  • 这里不需要添加导航属性。它不会造成伤害,但也无法解决问题。
  • @ChrisPratt 你是对的。直到添加集合导航属性它将创建关系。
猜你喜欢
  • 1970-01-01
  • 2018-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
相关资源
最近更新 更多