EntityFramework 存储库,所有行都返回空值

问题描述

总结

修改一个 nopCommerce 解决方案以包含一个具有 CodeFirst 方法的新实体,它已成功更新数据库,如下图所示。

Photo that database has table updated

当我尝试访问通过 AutoFrac 插入的存储库表时,我得到存储库返回的行,但所有列都显示空值。 注意表中的行数和在数据库表上看到的一样,所以好像已经连接了但没有显示值。

Code calling Repository

{
    public partial class NutrientService : INutrientService
    {
        #region fields

        protected readonly IRepository<ProductNutrient> _productNutrientRepository;

        #endregion fields

        #region Ctor

        public NutrientService(IRepository<ProductNutrient> productNutrientRepository)
        {
            _productNutrientRepository = productNutrientRepository;
        }

        #endregion Ctor

        public IList<ProductNutrient> GetNutrients()
        {
            var query = from p in _productNutrientRepository.Table
                        select p as ProductNutrient;
            var list = query.ToList();
            return list;
        }

        public IList<ProductNutrient> GetNutrientsByProductID()
        {
           
            var query = from p in _productNutrientRepository.Table
                        select p as ProductNutrient;
            var list = query.ToList();
            return list;
        }
    }
} 

Debugging showing Null values returned from repository

Table Definition in SQL Management Studio

Entity Definition in Code

    public class ProductNutrient : BaseEntity
    {
        public int NutrientID;

        public int ProductID;

        public string Nutrient;

        public bool ShowLessthan;

        public decimal Value;

        public string Unit;
    }
}

Repository 确实适用于其他表,但无论如何这里是存储库代码

      /// </summary>
        public virtual IQueryable<TEntity> Table => Entities;

        /// <summary>
        /// Gets an entity set
        /// </summary>
        protected virtual ITable<TEntity> Entities => _entities ?? (_entities = _dataProvider.GetTable<TEntity>());


        #endregion
    }

解决方法

我怀疑您要么迷失在抽象概念中,要么它们至少隐藏了一个问题。

首先剥离所有抽象,成为存储库。改为注入 DbContext 作为起点:

protected readonly AppDbContext _context = null;

public NutrientService(AppDbContext context)
{
    _context = context ?? throw new ArgumentNullException("context");
}

public IList<ProductNutrient> GetNutrients()
{
    var productNutrients = context.ProductNutrients
        .ToList();

    return productNutrients;
}

这是返回完整数据还是空值?如果它仍然为空,那么您将需要检查运行时正在使用的连接字符串,因为它可能指向比您现在正在查看的更旧的数据库实例。这似乎是一个相当普遍的问题,人们使用 SSMS 或设计器来查看数据库架构,但是运行时 web/app.config 指向旧的或共享的数据库位置,或者生成的具有一些数据的数据库,但不是您期望看到的。

如果它返回您期望的数据,那么您的抽象(存储库)在某些方面存在缺陷。第一个问题是“为什么要围绕 DbContext 实现存储库?”我对存储库的建议是通用存储库,即Repository<ProductNutrient> 是一个非常糟糕的反模式。如果您是因为遇到了一个示例而正在实施存储库,则您需要了解它的根本原因。如果您正在实施存储库以“隐藏”您正在使用 EntityFramework 或隐藏 DbContext 的事实,那么恕我直言,这是实施存储库的错误原因。考虑在 EntityFramework 上实现 Repository 模式的唯一原因是使您的代码更易于单元测试。在这样做时,我强烈建议利用 IQueryable<TEntity> 作为返回类型。 .Table 可能返回 IQueryable<ProductNutrient>,但如果它返回 DbSet<ProductNutrient>,那么您也可能只是使用 DbContext。如果它返回类似 IEnumerable<ProductNutrient> 之类的东西,那么您可能会因为您失去 EF 可以提供的大部分功能而使您的系统面临严重的性能限制。

如果您不打算在需要模拟数据层的地方使用单元测试,以便测试获得可靠的已知状态,那么实际上没有任何理由创建存储库类,并且可能会对您的数据层施加非常昂贵的限制数据访问。我在对这个问题的回答中概述了通用存储库模式和 EF 的问题:(How to set multiple services from Entity Framework Core on Repository Pattern?) 您可以使用专用集成测试数据库映像或内存数据库,或使用 DbContext 完全测试代码更多的工作,模拟 DbContext/DbSets。对于在集成之前的开发过程中重复运行的单元测试,模拟 Repository 类要简单得多。