以下のコードを実行すると、例外が発生します。どこが間違っていますか?
特に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();
}
}
}
Visit テーブルに対応するドメイン エンティティ:
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();
}
}
Visit テーブルと ChiefComplaint テーブル:
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);
}
}
私のデータ層プロジェクトには、構成要件を実装し、以下のように ConfigureServices で呼び出される DependencyInjection.cs クラスがあります。ここで、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"));
}