簡體   English   中英

使用 Automapper 過濾 DTO

[英]Use Automapper to filter DTO

我正在熟悉 Automapper。 我使用這個項目模板作為起點,並且總是遇到 Automapper 的問題。

public class GetMealsToBeServedListDto : IMapFrom<SubOrder>
{
    public int SubOrderId { get; set; }
    public int SubOrderNumber { get; set; }

    public ICollection<GetMealsToBeServedListOrderedMealDto>? GetMealsToBeServedListOrderedMealDtos { get; set; }

    public void Mapping(Profile profile)
    {
        profile.CreateMap<SubOrder, GetMealsToBeServedListDto>()
            .ForMember(d => d.SubOrderId, opt => opt.MapFrom(s => s.Id))
            .ForMember(d => d.SubOrderNumber, opt => opt.MapFrom(s => s.SubOrderNumber))
            // This is the problematic where statement.
            .ForMember(d => d.GetMealsToBeServedListOrderedMealDtos, opt => opt.MapFrom(s => s.OrderedMeals != null ? s.OrderedMeals.Where(p => p.CompletedOn.HasValue && p.CompletedOn != DateTime.MinValue) :
           null));
    }
}

public class GetMealsToBeServedListOrderedMealDto : IMapFrom<OrderedMeal>
{
    public int Quantity { get; set; }    
    public DateTime CompletedOn { get; set; }


    public void Mapping(Profile profile)
    {
        profile.CreateMap<OrderedMeal, GetMealsToBeServedListOrderedMealDto>()
            .ForMember(d => d.Quantity, opt => opt.MapFrom(s => s.Quantity))
            .ForMember(d => d.CompletedOn, opt => opt.MapFrom(s => s.CompletedOn));
        
    }
}

以下是我的查詢。

return new GetMealsToBeServedListVm
    {
        GetMealsToBeServedListDtos = await _context.SubOrders
                            .AsNoTracking()
                            .Where(x => x.OrderedMeals != null && x.OrderedMeals.Any(p => p.CompletedOn.HasValue && p.CompletedOn != DateTime.MinValue))
                            .ProjectTo<GetMealsToBeServedListDto>(_mapper.ConfigurationProvider)
                            .OrderByDescending(t => t.SubOrderNumber)
                            .ToListAsync(cancellationToken)
    };

當我嘗試執行查詢時,我收到以下錯誤。 我花了幾個小時試圖解決以下問題,但徒勞無功。 我嘗試了不同的方法,例如 Automapper Condition、PreCondition 等……到目前為止都沒有奏效。 有人可以請在這里嗎?

> System.InvalidOperationException: The LINQ expression 'dtoOrderedMeal => new GetMealsToBeServedListOrderedMealDto{ 
    CompletedOn = dtoOrderedMeal.CompletedOn ?? 01/01/0001 00:00:00, 
    Quantity = dtoOrderedMeal.Quantity 
}
' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitLambda[T](Expression`1 lambdaExpression)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.TranslateInternal(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberAssignment(MemberAssignment memberAssignment)
   at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.VisitMemberInit(MemberInitExpression memberInitExpression)
   at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Visit(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalProjectionBindingExpressionVisitor.Translate(SelectExpression selectExpression, Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0`1.<ExecuteAsync>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at test.Application.Orders.Queries.GetMealsToBeServedList.GetOrderPageListQueryHander.Handle(GetMealsToBeServedListQuery request, CancellationToken cancellationToken) in E:\Dev\test\backend\src\Application\Orders\Queries\GetMealsToBeServedList\GetMealsToBeServedListQuery.cs:line 44
   at test.Application.Common.Behaviours.PerformanceBehaviour`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next) in E:\Dev\test\backend\src\Application\Common\Behaviours\PerformanceBehaviour.cs:line 31
   at test.Application.Common.Behaviours.ValidationBehaviour`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next) in E:\Dev\test\backend\src\Application\Common\Behaviours\ValidationBehaviour.cs:line 35
   at test.Application.Common.Behaviours.AuthorizationBehaviour`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next) in E:\Dev\test\backend\src\Application\Common\Behaviours\AuthorizationBehaviour.cs:line 78
   at test.Application.Common.Behaviours.UnhandledExceptionBehaviour`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next) in E:\Dev\test\backend\src\Application\Common\Behaviours\UnhandledExceptionBehaviour.cs:line 19
   at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestExceptionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestExceptionActionProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestPostProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at MediatR.Pipeline.RequestPreProcessorBehavior`2.Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate`1 next)
   at test.WebUI.Controllers.OrderController.MealToBeServedList(GetMealsToBeServedListQuery query) in E:\Dev\test\backend\src\WebUI\Controllers\OrderController.cs:line 59
   at lambda_method253(Closure , Object )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   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.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ExceptionContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()

請參閱可查詢擴展的 AutoMapper 文檔

它指出ProjectTo必須是鏈中的最后一個調用,任何過濾和排序都必須首先對實體進行,然后再投影到 DTO。 如果不是,您將收到異常,因為 Linq to SQL 不知道如何處理您的 DTO 類型。

用 ProjectTo 交換對 OrderByDescending 的調用應該可以解決問題,但是您需要在 OrderByDescending 中重寫 lambda 以使用實體屬性而不是 DTO。

我最終將過濾器從 Automapper 移開。 但我仍然想知道在這方面最好的方法是什么。

return new GetMealsToBeServedListVm
    {
        GetMealsToBeServedListDtos = await _context.SubOrders
                            .AsNoTracking()
                            .Where(x => x.OrderedMeals != null && x.OrderedMeals.Any(p => p.CompletedOn.HasValue && p.CompletedOn != DateTime.MinValue))
                            .Select(s => new SubOrder
                                      {
                                Id = s.Id,
                                SubOrderNumber = s.SubOrderNumber,
                                OrderedMeals = s.OrderedMeals.Where(x => x.CompletedOn.HasValue && x.CompletedOn != DateTime.MinValue).ToList(),
                                      }
                            ).ProjectTo<GetMealsToBeServedListDto>(_mapper.ConfigurationProvider)
                            .ToListAsync(cancellationToken)
    };

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM