我不明白为什么我小巧玲珑的 Multi-Mapper 会出现异常

I can't figure out why my dapper Multi-Mapper is giving an exception

我正在尝试将所有 EntityFramework 核心查询重写为 Dapper。在这个查询中,我遇到了一个我无法弄清楚的异常。数据在姐妹 EF Core 查询中返回。在我看来,我没有正确设置 Multi-Mapper。

这是我的小巧功能:

public sealed class DapperFunctions : IDisposable
{
    private IDbConnectionFactory _connectionFactory;

    private readonly ILogger _logger;

    public DapperFunctions(IDbConnectionFactory connectionFactory, ILogger logger)
    {
        _connectionFactory = connectionFactory;
        _logger = logger;
        _logger.ForContext<DapperFunctions>().Information("Initializing DapperFunctions");

        if (FluentMapper.EntityMaps.IsEmpty)
        {
            FluentMapper.Initialize(config =>
            {
                config.AddMap(new OrderMap());
                config.AddMap(new OrderDetailMap());
                config.AddMap(new BillToAddressMap());
                config.AddMap(new ShipToAddressMap());
                config.AddMap(new ReserveDocumentMap());
            });
        }
    }

    
    public IEnumerable<OrderDto> GetInvoicesMultiMapping(string customerId)
    {
        string sql =
            @$"SELECT [order].[SOPTYPE],
                   [order].[SOPNUMBE],
                   [order].[INVODATE],
                   [order].[ACTLSHIP],
                   [order].[DUEDATE],
                   [order].[LOCNCODE],
                   [order].[CUSTNMBR],
                   [order].[CUSTNAME],
                   [order].[SLPRSNID],
                   [order].[PYMTRCVD],
                   [order].[PYMTRMID],
                   [order].[SUBTOTAL],
                   [order].[FRTAMNT],
                   [order].[TAXAMNT],
                   [order].[DOCAMNT],
                   [detail].[ITEMNMBR],
                   [detail].[SOPTYPE],
                   [detail].[SOPNUMBE],
                   [detail].[ReqShipDate],
                   [detail].[ITEMDESC],
                   [detail].[UOFM],
                   [detail].[LOCNCODE],
                   [detail].[UNITPRCE],
                   [detail].[XTNDPRCE],
                   [detail].[QUANTITY],
                   [billtoaddr].[ShipToName] AS[BillToName],
                   [billtoaddr].[CUSTNMBR],
                   [billtoaddr].[ADRSCODE],
                   [billtoaddr].[CNTCPRSN],
                   [billtoaddr].[ADDRESS1],
                   [billtoaddr].[ADDRESS2],
                   [billtoaddr].[ADDRESS3],
                   [billtoaddr].[COUNTRY],
                   [billtoaddr].[CITY],
                   [billtoaddr].[STATE],
                   [billtoaddr].[ZIP],
                   [billtoaddr].[PHONE1],
                   [billtoaddr].[PHONE2],
                   [billtoaddr].[PHONE3],
                   [billtoaddr].[FAX],
                   [shiptoaddr].[ShipToName],
                   [shiptoaddr].[CUSTNMBR],
                   [shiptoaddr].[ADRSCODE],
                   [shiptoaddr].[CNTCPRSN],
                   [shiptoaddr].[ADDRESS1],
                   [shiptoaddr].[ADDRESS2],
                   [shiptoaddr].[ADDRESS3],
                   [shiptoaddr].[COUNTRY],
                   [shiptoaddr].[CITY],
                   [shiptoaddr].[STATE],
                   [shiptoaddr].[ZIP],
                   [shiptoaddr].[PHONE1],
                   [shiptoaddr].[PHONE2],
                   [shiptoaddr].[PHONE3],
                   [shiptoaddr].[FAX],
                   CAST([reserv].[DocumentOrigin] AS INT) AS [Origin]
            FROM[dbo].[SOP10100] AS [order]
                INNER JOIN[dbo].[SOP10200] AS [detail]
                    ON[order].[SOPTYPE] = [detail].[SOPTYPE]
                       AND[order].[SOPNUMBE] = [detail].[SOPNUMBE]
                INNER JOIN[dbo].[RM00102] AS[shiptoaddr]
                    ON[order].[PRSTADCD] = [shiptoaddr].[ADRSCODE]
                       AND[order].[CUSTNMBR] = [shiptoaddr].[CUSTNMBR]
                INNER JOIN[dbo].[RM00102] AS[billtoaddr]
                    ON[order].[PRBTADCD] = [billtoaddr].[ADRSCODE]
                       AND[order].[CUSTNMBR] = [billtoaddr].[CUSTNMBR]
                INNER JOIN[cp].[ReservedDocuments] AS[reserv]
                    ON[order].[SOPTYPE] = [reserv].[SOPTYPE]
                       AND[order].[SOPNUMBE] = [reserv].[SOPNUMBER]
            WHERE[order].[CUSTNMBR] = '{customerId}'
                  AND[order].[SOPTYPE] = 3";

        using (DbConnection db = _connectionFactory.CreateConnection())
        {
            var list = db.Query<OrderDto, OrderDetailDto, CustomerAddressDto, CustomerAddressDto, ReservedDocumentDto, OrderDto>(
                    sql,
                    (order, details, billto, shipto, reserve) =>
                    {
                        order.Details.Add(details);
                        order.Billto = billto;
                        order.Shipto = shipto;
                        order.Origin = reserve.DocumentOrigin;
                        
                        return order;
                    },
                    splitOn: "ITEMNMBR,BillToName,ShipToName,Origin", buffered: false)
                .Distinct()
                .OrderBy(x => x.Invodate)
                .ToList();

            return list;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_connectionFactory != null)
            {
                _logger.ForContext<DapperFunctions>().Information("Disposing DapperFunctions");
                //_connectionFactory.Dispose();
                _connectionFactory = null;
            }
        }
    }
}

public class OrderMap : EntityMap<OrderDto>
{
    public OrderMap()
    {
        Map(i => i.Soptype).ToColumn("SOPTYPE", false);
        Map(i => i.Sopnumbe).ToColumn("SOPNUMBE", false);
        Map(i => i.Invodate).ToColumn("INVODATE", false);
        Map(i => i.Actlship).ToColumn("ACTLSHIP", false);
        Map(i => i.Duedate).ToColumn("DUEDATE", false);
        Map(i => i.Locncode).ToColumn("LOCNCODE", false);
        Map(i => i.Custnmbr).ToColumn("CUSTNMBR", false);
        Map(i => i.Custname).ToColumn("CUSTNAME", false);
        Map(i => i.Slprsnid).ToColumn("SLPRSNID", false);
        Map(i => i.Pymtrcvd).ToColumn("PYMTRCVD", false);
        Map(i => i.Pymtrmid).ToColumn("PYMTRMID", false);
        Map(i => i.Subtotal).ToColumn("SUBTOTAL", false);
        Map(i => i.Frtamnt).ToColumn("FRTAMNT", false);
        Map(i => i.Taxamnt).ToColumn("TAXAMNT", false);
        Map(i => i.Docamnt).ToColumn("DOCAMNT", false);
    }
}

public class OrderDetailMap : EntityMap<OrderDetailDto>
{
    public OrderDetailMap()
    {
        Map(i => i.Itemnmbr).ToColumn("ITEMNMBR", false);
        Map(i => i.Soprtype).ToColumn("SOPTYPE", false);
        Map(i => i.Sopnumbe).ToColumn("SOPNUMBE", false);
        Map(i => i.ReqShipDate).ToColumn("ReqShipDate", false);
        Map(i => i.Itemdesc).ToColumn("ITEMDESC", false);
        Map(i => i.Uofm).ToColumn("UOFM", false);
        Map(i => i.Locncode).ToColumn("LOCNCODE", false);
        Map(i => i.Unitprce).ToColumn("UNITPRCE", false);
        Map(i => i.Xtndprce).ToColumn("XTNDPRCE", false);
        Map(i => i.Quantity).ToColumn("QUANTITY", false);
    }
}

public class BillToAddressMap : EntityMap<CustomerAddressDto>
{
    public BillToAddressMap()
    {
        Map(i => i.ShipToName).ToColumn("BillToName", false);
        Map(i => i.Custnmbr).ToColumn("CUSTNMBR", false);
        Map(i => i.Adrscode).ToColumn("ADRSCODE", false);
        Map(i => i.Cntcprsn).ToColumn("CNTCPRSN", false);
        Map(i => i.Address1).ToColumn("ADDRESS1", false);
        Map(i => i.Address2).ToColumn("ADDRESS2", false);
        Map(i => i.Address3).ToColumn("ADDRESS3", false);
        Map(i => i.Country).ToColumn("COUNTRY", false);
        Map(i => i.City).ToColumn("CITY", false);
        Map(i => i.State).ToColumn("STATE", false);
        Map(i => i.Zip).ToColumn("ZIP", false);
        Map(i => i.Phone1).ToColumn("PHONE1", false);
        Map(i => i.Phone2).ToColumn("PHONE2", false);
        Map(i => i.Phone3).ToColumn("PHONE3", false);
        Map(i => i.Fax).ToColumn("FAX", false);
    }
}

public class ShipToAddressMap : EntityMap<CustomerAddressDto>
{
    public ShipToAddressMap()
    {
        Map(i => i.ShipToName).ToColumn("ShipToName", false);
        Map(i => i.Custnmbr).ToColumn("CUSTNMBR", false);
        Map(i => i.Adrscode).ToColumn("ADRSCODE", false);
        Map(i => i.Cntcprsn).ToColumn("CNTCPRSN", false);
        Map(i => i.Address1).ToColumn("ADDRESS1", false);
        Map(i => i.Address2).ToColumn("ADDRESS2", false);
        Map(i => i.Address3).ToColumn("ADDRESS3", false);
        Map(i => i.Country).ToColumn("COUNTRY", false);
        Map(i => i.City).ToColumn("CITY", false);
        Map(i => i.State).ToColumn("STATE", false);
        Map(i => i.Zip).ToColumn("ZIP", false);
        Map(i => i.Phone1).ToColumn("PHONE1", false);
        Map(i => i.Phone2).ToColumn("PHONE2", false);
        Map(i => i.Phone3).ToColumn("PHONE3", false);
        Map(i => i.Fax).ToColumn("FAX", false);
    }
}

public class ReserveDocumentMap : EntityMap<ReservedDocumentDto>
{
    public ReserveDocumentMap()
    {
        Map(i => i.DocumentOrigin).ToColumn("Origin", false);
    }
}

我遇到的异常是 System.NullReferenceException: Object reference not set to an instance of an object. 如果需要,我可以提供 table 定义和 dto。任何诊断此问题的帮助将不胜感激。

更新

根据要求...

System.NullReferenceException: Object reference not set to an instance of an object.
   at DataAccessLayer.DapperFunctions.<>c.<GetInvoicesMultiMappingOld>b__6_0(OrderDto order, OrderDetailDto details, CustomerAddressDto billto, CustomerAddressDto shipto, ReservedDocumentDto reserve) in D:\Projects\NewEcommerce\DataAccessLayer\DapperFunctions.cs:line 293
   at Dapper.SqlMapper.<>c__DisplayClass160_0`8.<GenerateMapper>b__3(IDataReader r) in /_/Dapper/SqlMapper.cs:line 1568
   at Dapper.SqlMapper.MultiMapImpl[TFirst,TSecond,TThird,TFourth,TFifth,TSixth,TSeventh,TReturn](IDbConnection cnn, CommandDefinition command, Delegate map, String splitOn, IDataReader reader, Identity identity, Boolean finalize)+MoveNext() in /_/Dapper/SqlMapper.cs:line 1469
   at System.Linq.Set`1.UnionWith(IEnumerable`1 other)
   at System.Linq.Enumerable.DistinctIterator`1.FillSet()
   at System.Linq.Enumerable.DistinctIterator`1.ToArray()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.OrderedEnumerable`1.ToList()
   at DataAccessLayer.DapperFunctions.GetInvoicesMultiMappingOld(String customerId) in D:\Projects\NewEcommerce\DataAccessLayer\DapperFunctions.cs:line 289
   at EcommerceWebAPI.Services.OrderService.GetAllInvoicesWithDapperOld(User user) in D:\Projects\NewEcommerce\NewEcommerce\Services\OrderService.cs:line 85
   at EcommerceWebAPI.Controllers.OrdersController.GetAllInvoicesWithDapperOld() in D:\Projects\NewEcommerce\NewEcommerce\Controllers\OrdersController.cs:line 114
   at lambda_method753(Closure , Object , Object[] )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at EcommerceWebAPI.Helpers.JwtMiddleware.Invoke(HttpContext context, IUserService userService) in D:\Projects\NewEcommerce\NewEcommerce\Helpers\JwtMiddleware.cs:line 35
   at Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware.Invoke(HttpContext httpContext)
   at Serilog.AspNetCore.RequestLoggingMiddleware.Invoke(HttpContext httpContext)
   at EcommerceWebAPI.Startup.<>c.<<Configure>b__5_1>d.MoveNext() in D:\Projects\NewEcommerce\NewEcommerce\Startup.cs:line 209
--- End of stack trace from previous location ---
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

...DapperFunctions.cs 中的第 293 行是:order.Details.Add(details);

@jwdonahue 让我走上正轨...

在我的 OrderDto class 中,我需要一个构造函数来初始化我的字段:

public OrderDto()
{
    Details = new List<OrderDetailDto>();
    Billto = new CustomerAddressDto();
    Shipto = new CustomerAddressDto();
    Origin = Origin.Unknown;
}

是的@jwdonahue!现在一切正常。