如何使用 AutoMapper 从 `Dictionary<A, B>` 投影到 `Dictionary<A,C>`?

问题描述 投票:0回答:1

我有一个相当简单的 AutoMapper 设置,我想将

IDictionary<Guid, From>
映射到
Dictionary<Guid, To>
。这与
IMapper.Map
配合得很好,但我真正想做的是使用
IQueryable.ProjectTo
。但是,这会导致以下测试失败:

using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Xunit;

public class AutoMapperTests()
{
    private readonly IMapper mapper = new MapperConfiguration(
        (cfg) =>
        {
            cfg.CreateMap<From, To>();
        }
    ).CreateMapper();

    [Fact]
    public void AutoMapperConfigurationIsValid() =>
        mapper.ConfigurationProvider.AssertConfigurationIsValid();

    [Fact]
    public void CanMapFromDictToDict()
    {
        var key = Guid.NewGuid();
        var fromDict = CreateFromDict(key, key.ToString());

        var toDict = new[] { fromDict }
            .AsEnumerable()
            .Select(it => mapper.Map<IDictionary<Guid, To>>(fromDict))
            .Single();

        Assert.Single(toDict);
        Assert.True(toDict.ContainsKey(key));
        Assert.True(toDict[key].Id == key.ToString());
    }

    [Fact]
    public void CanProjectFromDictToDict()
    {
        var key = Guid.NewGuid();
        var fromDict = CreateFromDict(key, key.ToString());

        var toDict = new[] { fromDict }
            .AsQueryable()
            .ProjectTo<IDictionary<Guid, To>>(mapper.ConfigurationProvider)
            .Single();

        Assert.Single(toDict);
        Assert.True(toDict.ContainsKey(key));
        Assert.True(toDict[key].Id == key.ToString());
    }

    private IDictionary<Guid, From> CreateFromDict(Guid key, string id) =>
        new Dictionary<Guid, From>
        {
            {
                key,
                new() { Id = id }
            }
        };

    public class From
    {
        public required string Id { get; set; }
    }

    public class To
    {
        public required string Id { get; set; }
    }
}
[xUnit.net 00:00:00.15]     AutoMapperTests.CanProjectFromDictToDict [FAIL]
  Failed AutoMapperTests.CanProjectFromDictToDict [7 ms]
  Error Message:
   System.InvalidOperationException : Missing map from System.Collections.Generic.IDictionary`2[System.Guid,AutoMapperTests+From] to System.Collections.Generic.IDictionary`2[System.Guid,AutoMapperTests+To]. Create using CreateMap<IDictionary`2, IDictionary`2>.
  Stack Trace:
     at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.PolymorphicMaps(ProjectionRequest& request)
   at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.CreateProjection(ProjectionRequest request)
   at AutoMapper.Internal.LockingConcurrentDictionary`2.<>c__DisplayClass2_1.<.ctor>b__1()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.get_Value()
   at AutoMapper.Internal.LockingConcurrentDictionary`2.GetOrAdd(TKey& key)
   at AutoMapper.QueryableExtensions.Impl.ProjectionBuilder.GetProjection(Type sourceType, Type destinationType, Object parameters, MemberPath[] membersToExpand)
   at AutoMapper.QueryableExtensions.Extensions.ToCore(IQueryable source, Type destinationType, IConfigurationProvider configuration, Object parameters, IEnumerable`1 memberPathsToExpand)
   at AutoMapper.QueryableExtensions.Extensions.ToCore[TResult](IQueryable source, IConfigurationProvider configuration, Object parameters, IEnumerable`1 memberPathsToExpand)
   at AutoMapper.QueryableExtensions.Extensions.ProjectTo[TDestination](IQueryable source, IConfigurationProvider configuration, Object parameters, Expression`1[] membersToExpand)
   at AutoMapper.QueryableExtensions.Extensions.ProjectTo[TDestination](IQueryable source, IConfigurationProvider configuration, Expression`1[] membersToExpand)
   at AutoMapperTests.CanProjectFromDictToDict() in /<redacted>/AutoMapperTests.cs:line 43
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

Failed!  - Failed:     1, Passed:     2, Skipped:     0, Total:     3, Duration: 21 ms

添加请求的映射会导致所有测试失败:

    private readonly IMapper mapper = new MapperConfiguration(
        (cfg) =>
        {
            cfg.CreateMap<From, To>();
            cfg.CreateMap<IDictionary<Guid, From>, IDictionary<Guid, To>>();
        }
    ).CreateMapper();
[xUnit.net 00:00:00.13]     AutoMapperTests.AutoMapperConfigurationIsValid [FAIL]
[xUnit.net 00:00:00.13]     AutoMapperTests.CanProjectFromDictToDict [FAIL]
[xUnit.net 00:00:00.13]     AutoMapperTests.CanMapFromDictToDict [FAIL]
  Failed AutoMapperTests.AutoMapperConfigurationIsValid [1 ms]
  Error Message:
   System.ArgumentException : Incorrect number of arguments supplied for call to method 'To get_Item(System.Guid)' (Parameter 'property')
  Stack Trace:
     at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreatePropertyMapFunc(MemberMap memberMap, Expression destination, MemberInfo destinationMember)
   at AutoMapper.Execution.TypeMapPlanBuilder.AddPropertyMaps(List`1 actions)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateAssignmentFunc(Expression createDestination)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateMapperLambda()
   at AutoMapper.TypeMap.CreateMapperLambda(IGlobalConfiguration configuration)
   at AutoMapper.TypeMap.Seal(IGlobalConfiguration configuration)
   at AutoMapper.MapperConfiguration.<.ctor>g__Seal|20_0()
   at AutoMapper.MapperConfiguration..ctor(MapperConfigurationExpression configurationExpression)
   at AutoMapper.MapperConfiguration..ctor(Action`1 configure)
   at AutoMapperTests..ctor() in /<redacted>/AutoMapperTests.cs:line 10
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
  Failed AutoMapperTests.CanProjectFromDictToDict [1 ms]
  Error Message:
   System.ArgumentException : Incorrect number of arguments supplied for call to method 'To get_Item(System.Guid)' (Parameter 'property')
  Stack Trace:
     at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreatePropertyMapFunc(MemberMap memberMap, Expression destination, MemberInfo destinationMember)
   at AutoMapper.Execution.TypeMapPlanBuilder.AddPropertyMaps(List`1 actions)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateAssignmentFunc(Expression createDestination)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateMapperLambda()
   at AutoMapper.TypeMap.CreateMapperLambda(IGlobalConfiguration configuration)
   at AutoMapper.TypeMap.Seal(IGlobalConfiguration configuration)
   at AutoMapper.MapperConfiguration.<.ctor>g__Seal|20_0()
   at AutoMapper.MapperConfiguration..ctor(MapperConfigurationExpression configurationExpression)
   at AutoMapper.MapperConfiguration..ctor(Action`1 configure)
   at AutoMapperTests..ctor() in /<redacted>/AutoMapperTests.cs:line 10
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
  Failed AutoMapperTests.CanMapFromDictToDict [1 ms]
  Error Message:
   System.ArgumentException : Incorrect number of arguments supplied for call to method 'To get_Item(System.Guid)' (Parameter 'property')
  Stack Trace:
     at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreatePropertyMapFunc(MemberMap memberMap, Expression destination, MemberInfo destinationMember)
   at AutoMapper.Execution.TypeMapPlanBuilder.AddPropertyMaps(List`1 actions)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateAssignmentFunc(Expression createDestination)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateMapperLambda()
   at AutoMapper.TypeMap.CreateMapperLambda(IGlobalConfiguration configuration)
   at AutoMapper.TypeMap.Seal(IGlobalConfiguration configuration)
   at AutoMapper.MapperConfiguration.<.ctor>g__Seal|20_0()
   at AutoMapper.MapperConfiguration..ctor(MapperConfigurationExpression configurationExpression)
   at AutoMapper.MapperConfiguration..ctor(Action`1 configure)
   at AutoMapperTests..ctor() in /<redacted>/AutoMapperTests.cs:line 10
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)

Failed!  - Failed:     3, Passed:     0, Skipped:     0, Total:     3, Duration: 2 ms

如何让投影正常工作?

编辑

@LucianBargaoanu 询问“[...] 在没有客户端评估的情况下,这在 EF Core 中是否可能实现?”。因此我添加了以下测试用例,该用例很好地通过了。

    [Fact]
    public void CanProjectManuallyFromDictToDict()
    {
        var key = Guid.NewGuid();
        var fromDict = CreateFromDict(key, key.ToString());

        var toDict = new[] { fromDict }
            .AsQueryable()
            .Select(it => fromDict.ToDictionary(it => it.Key, it => new To { Id = it.Value.Id }))
            .Single();

        Assert.Single(toDict);
        Assert.True(toDict.ContainsKey(key));
        Assert.True(toDict[key].Id == key.ToString());
    }
c# linq .net-core automapper
1个回答
0
投票

ProjectTo
适用于服务器评估,并且不以任何方式考虑字典(显然 EF Core 也不支持它们)。

但是您可以做的是使用

ProjectTo
then
Map
将您需要的内容提取到字典中。

© www.soinside.com 2019 - 2024. All rights reserved.