Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
733 views
in Technique[技术] by (71.8m points)

orm - RepoDb - Extract function throws exception when using ExecuteQueryMultiple

when i execute the code below, it generates an exception. where am i going wrong?

Code that generates exception specifically 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();
                    }
                }
            }

The domain entity that corresponds to the Visit table:

    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);
        }
    }

The base domain entity that all domain entities inherit from:

 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();
        }

    }

The Visit and ChiefComplaint tables:

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
);

The exception:

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) 

I tried to implement the property handler per the docs on RepoDb but with no success. Below is how I implemented the property handler.

    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);
        }
    }

In my data layer project I have a DependencyInjection.cs class that implements configuration requirements and gets called in ConfigureServices as below. This is where I specify the FluentMapper mapping.

    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;
        }
    }

The ConfigureServices section of Startup.cs in the web api project.

        public void ConfigureServices(IServiceCollection services) {
            services.AddControllers();
            services.AddDataCoreServices(Configuration.GetSection("AppSettings"));
        }
question from:https://stackoverflow.com/questions/65946130/repodb-extract-function-throws-exception-when-using-executequerymultiple

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The latest version of the library has supported the immutable classes and that it requires the ctor fields to be matching from the data reader columns.

Below is our recommendation.

Your Visit class must not have the chiefComplaints argument in the ctor.

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

Then, add an additional method to set the local variable chief complaints (or just make it a public writable property like below).

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

During the extract, simply set the property (see below).

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

Though, we have added an issue on our GH page to improve this ctor constructions.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...