【问题标题】:RepoDb - Extract function throws exception when using ExecuteQueryMultipleRepoDb - 使用 ExecuteQueryMultiple 时提取函数抛出异常
【发布时间】:2021-01-28 22:28:28
【问题描述】:

当我执行下面的代码时,它会生成一个异常。我哪里错了?

产生异常的代码extractor.Extract:

public Visit Get(long id, bool loadGraph = false) {
                const string sqlVisit = @"SELECT * FROM Visit WHERE Id = @VisitId;";
                const string sqlCC = @"SELECT * FROM ChiefComplaint WHERE VisitId = @VisitId;";
                var sqlParam = new { VisitId = id };
    
                if (loadGraph) {
                    using (var extractor = base.ExecuteQueryMultiple(sqlVisit + sqlCC, sqlParam)) {
                        /*fails with exception*/var v = extractor.Extract<Visit>().FirstOrDefault();
                        var cc = extractor.Extract<ChiefComplaint>().AsList();
                        return new Visit(v.Id, v.DateOfService, cc);
                    }                
                }
                else {
                    using (var extractor = base.ExecuteQueryMultiple(sqlVisit, sqlParam)) {
                        return extractor.Extract<Visit>().FirstOrDefault();
                    }
                }
            }

访问表对应的域实体:

    public class Visit : BaseEntity {

        private List<ChiefComplaint> _chiefComplaints = new List<ChiefComplaint>();
        public IReadOnlyList<ChiefComplaint> ChiefComplaints => _chiefComplaints.ToList();
        public DateTime DateOfService { get; }

        public Visit(long id, DateTime dateOfService, IEnumerable<ChiefComplaint> chiefComplaints) : base(id) {
            this.DateOfService = dateOfService;
            _chiefComplaints.AddRange(chiefComplaints);
        }
    }

所有域实体都继承自的基础域实体:

 public abstract class BaseEntity {
        public long Id { get; }

        protected BaseEntity(long id) {
            this.Id = id;
        }

        public override bool Equals(object obj) {
            if (!(obj is BaseEntity other))
                return false;

            if (ReferenceEquals(this, other))
                return true;

            if (this.GetType() != other.GetType())
                return false;

            if (this.Id == 0 || other.Id == 0)
                return false;

            return this.Id == other.Id;
        }

        public static bool operator ==(BaseEntity a, BaseEntity b) {
            if (a is null && b is null)
                return true;

            if (a is null || b is null)
                return false;

            return a.Equals(b);
        }

        public static bool operator !=(BaseEntity a, BaseEntity b) {
            return !(a == b);
        }

        public override int GetHashCode() {
            return (this.GetType().ToString() + this.Id).GetHashCode();
        }

    }

访问和首席投诉表:

CREATE TABLE [dbo].[Visit] (
    [Id]            BIGINT        IDENTITY (1, 1) NOT NULL,
    [DateOfService] DATETIME2 (7) NOT NULL
);

CREATE TABLE [dbo].[ChiefComplaint] (
    [Id]          BIGINT         IDENTITY (1, 1) NOT NULL,
    [Description] NVARCHAR (MAX) NULL,
    [HpiId]       BIGINT         NULL,
    [VisitId]     BIGINT         NULL
);

例外:

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=RepoDb
  StackTrace:
   at RepoDb.Reflection.Compiler.<>c__DisplayClass62_0`1.<GetClassPropertyParameterInfos>b__1(ParameterInfo parameterInfo)
   at System.Collections.Generic.List`1.ForEach(Action`1 action)
   at RepoDb.Reflection.Compiler.GetClassPropertyParameterInfos[TResult](IEnumerable`1 readerFieldsName, IDbSetting dbSetting)
   at RepoDb.Reflection.Compiler.GetMemberBindingsForDataEntity[TResult](ParameterExpression readerParameterExpression, IEnumerable`1 readerFields, IDbSetting dbSetting)
   at RepoDb.Reflection.Compiler.CompileDataReaderToDataEntity[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.Reflection.Compiler.CompileDataReaderToType[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.Reflection.FunctionFactory.CompileDataReaderToType[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.FunctionCache.DataReaderToTypeCache`1.Get(DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.FunctionCache.GetDataReaderToTypeCompiledFunction[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.Reflection.DataReader.<ToEnumerable>d__0`1.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at RepoDb.Extensions.EnumerableExtension.AsList[T](IEnumerable`1 value)
   at RepoDb.QueryMultipleExtractor.Extract[TEntity](Boolean isMoveToNextResult)
   at Repositories.VisitRepository.Get(Int64 id, Boolean loadGraph) 

我尝试按照 RepoDb 上的文档实现属性处理程序,但没有成功。下面是我如何实现属性处理程序。

    public class ChiefComplaintPropertyHandler : IPropertyHandler<string, ChiefComplaint> {
        public ChiefComplaint Get(string input, ClassProperty property) {
            return JsonConvert.DeserializeObject<ChiefComplaint>(input);
        }
        public string Set(ChiefComplaint input, ClassProperty property) {
            return JsonConvert.SerializeObject(input);
        }
    }

在我的数据层项目中,我有一个 DependencyInjection.cs 类,它实现了配置要求并在 ConfigureServices 中被调用,如下所示。这是我指定 FluentMapper 映射的地方。

    public static class DependencyInjection {
        public static IServiceCollection AddDataCoreServices(this IServiceCollection services, IConfigurationSection dbConfigSection) {
            SqlServerBootstrap.Initialize();
            FluentMapper
                .Entity<Visit>()
                .PropertyHandler<ChiefComplaintPropertyHandler>(v => v.ChiefComplaints, true);

            services.Configure<AppSetting>(dbConfigSection);
            services.AddTransient<IUnitOfWork, UnitOfWork>();
            services.AddTransient<IVisitRepository, VisitRepository>();

            return services;
        }
    }

Web api 项目中 Startup.cs 的 ConfigureServices 部分。

        public void ConfigureServices(IServiceCollection services) {
            services.AddControllers();
            services.AddDataCoreServices(Configuration.GetSection("AppSettings"));
        }

【问题讨论】:

  • 我将把它作为一个问题提交到我们的 Github 页面上。可以分享一下模型和表结构吗?
  • 我已更新原始帖子以反映您的要求。感谢您的帮助!
  • 谢谢,我们会尽快回复您。
  • 我相信 Extract() 调用不喜欢传递给 Visit 构造函数的对象,即 IEnumerable,只有原语。如何告诉 RepoDb 识别对象?
  • RepoDB 自动根据类属性自动投影表列,是的,它只是原始类型。如果要处理对象到列的转换,则需要一个属性处理程序。我仍然要模拟这种情况,但是在查看它时,它应该可以正常工作。无论如何,很快就会给你一个反馈

标签: orm micro-orm


【解决方案1】:

该库的最新版本已支持不可变类,并且它要求 ctor 字段与数据读取器列匹配。

以下是我们的建议。

您的 Visit 类不能在 ctor 中包含 chiefComplaints 参数。

public Visit(long id, DateTime dateOfService) : base(id)
{
    this.DateOfService = dateOfService;
}

然后,添加一个额外的方法来设置局部变量的主要投诉(或者像下面一样将其设置为公共可写属性)。

public List<ChiefComplaint> ChiefComplaints { get; set; }

在提取过程中,只需设置属性(见下文)。

var v = extractor.Extract<Visit>().FirstOrDefault();
var cc = extractor.Extract<ChiefComplaint>().AsList();
return new Visit(v.Id, v.DateOfService)
{
     ChiefComplaints = cc.AsList()
};

不过,我们在 GH 页面上添加了 issue 以改进此 ctor 构造。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    • 1970-01-01
    • 2022-07-27
    • 1970-01-01
    • 1970-01-01
    • 2015-08-26
    相关资源
    最近更新 更多