0

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

1 回答 1

0

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

以下是我们的建议。

您的Visit班级不能chiefComplaints在 ctor 中有参数。

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 页面上添加了一个问题来改进这个 ctor 结构。

于 2021-02-06T13:37:00.550 回答