我有一个相当简单的 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());
}
ProjectTo
适用于服务器评估,并且不以任何方式考虑字典(显然 EF Core 也不支持它们)。
但是您可以做的是使用
ProjectTo
和 then Map
将您需要的内容提取到字典中。