gpt4 book ai didi

AutoMapper QueryableExtensions 在非规范化子对象时抛出 NullReference

转载 作者:行者123 更新时间:2023-12-04 04:51:29 28 4
gpt4 key购买 nike

是否有其他替代方法使用 AutoMapper Queryable 扩展来避免尝试从子对象映射时出现空引用异常?

背景

使用 AutoMapper Queryable 扩展投影到 CustomerViewModel 时,映射 FullAddress 属性失败并出现空引用异常。我向 AutoMapper 团队提出了一个问题 https://github.com/AutoMapper/AutoMapper/issues/351使用测试工具来重现问题。测试名为 can_map_AsQuerable_with_projection_this_FAILS 是失败的测试。

希望继续使用 AutoMapper 和 Queryable Extensions,因为代码富有表现力且易于阅读;但是,计算 FullAddress 会引发 Null Reference Exception。我知道是 FullAddress 映射导致了问题,因为如果我将其更改为 Ignore(),则映射成功。当然,测试仍然失败,因为我正在检查以确保 FullAddress 具有值。

我想出了一些替代方案,但它们不使用 AutoMapper 映射。以下测试案例中概述了这些方法中的每一种。

**can_map_AsQuerable_with_expression**
**can_map_AsQuerable_with_custom_mapping**

测试夹具如下。
namespace Test.AutoMapper
{
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }

public Address Address { get; set; }
}

public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
}

public class CustomerViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }

public string FullAddress { get; set; }
}


[TestFixture]
public class AutoMapperQueryableExtensionsThrowsNullReferenceExceptionSpec
{
protected List<Customer> Customers { get; set; }

[SetUp]
public void Setup()
{
Mapper.CreateMap<Customer, CustomerViewModel>()
.ForMember(x => x.FullAddress,
o => o.MapFrom(s => String.Format("{0}, {1} {2}",
s.Address.Street,
s.Address.City,
s.Address.State)));

Mapper.AssertConfigurationIsValid();

Customers = new List<Customer>()
{
new Customer() {
FirstName = "Mickey", LastName = "Mouse",
Address = new Address() { Street = "My Street", City = "My City", State = "my state" }
},

new Customer() {
FirstName = "Donald", LastName = "Duck",
Address = new Address() { Street = "My Street", City = "My City", State = "my state" }
}
};
}

[Test]
public void can_map_single()
{
var vm = Mapper.Map<CustomerViewModel>(Customers[0]);
Assert.IsNotNullOrEmpty(vm.FullAddress);
}

[Test]
public void can_map_multiple()
{
var customerVms = Mapper.Map<List<CustomerViewModel>>(Customers);
customerVms.ForEach(x => Assert.IsNotNullOrEmpty(x.FullAddress));
}

/// <summary>
/// This does NOT work, throws NullReferenceException.
/// </summary>
/// <remarks>
/// System.NullReferenceException : Object reference not set to an instance of an object.
/// at AutoMapper.MappingEngine.CreateMapExpression(Type typeIn, Type typeOut)
/// at AutoMapper.MappingEngine.CreateMapExpression(Type typeIn, Type typeOut)
/// at AutoMapper.MappingEngine.<CreateMapExpression>b__9<TSource,TDestination>(TypePair tp)
/// at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
/// at AutoMapper.MappingEngine.CreateMapExpression()
/// at AutoMapper.QueryableExtensions.ProjectionExpression`1.To()
/// </remarks>
[Test]
public void can_map_AsQuerable_with_projection_this_FAILS()
{
var customerVms = Customers.AsQueryable().Project().To<CustomerViewModel>().ToList();
customerVms.ForEach(x => Assert.IsNotNullOrEmpty(x.FullAddress));
}

[Test]
public void can_map_AsQuerable_with_expression()
{
var customerVms = Customers.AsQueryable().Select(ToVM.ToCustomerViewModelExpression()).ToList();
customerVms.ForEach(x => Assert.IsNotNullOrEmpty(x.FullAddress));
}

[Test]
public void can_map_AsQuerable_with_custom_mapping()
{
var customerVms = Customers.AsQueryable().Select(ToVM.ToCustomerViewModel).ToList();
customerVms.ForEach(x => Assert.IsNotNullOrEmpty(x.FullAddress));
}
}

public static class ToVM
{
public static CustomerViewModel ToCustomerViewModel(this Customer source)
{
return new CustomerViewModel()
{
FirstName = source.FirstName,
LastName = source.LastName,
FullAddress = String.Format("{0}, {1} {2}",
source.Address.Street,
source.Address.City,
source.Address.State)
};
}

public static Expression<Func<Customer, CustomerViewModel>> ToCustomerViewModelExpression()
{
return source => source.ToCustomerViewModel();
}
}
}

最佳答案

我找到了一个使用 AutoMapper 和 Queryable Extensions 的工作解决方案。问题在于在投影中使用 String.Format。解决方法是将所有必要的属性(Street、City 和State)添加到CustomViewModel,然后添加一个属性(FullAddress)在CustomerViewModel 中进行计算。

public class CustomerViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }

public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }

public string FullAddress
{
get
{
return String.Format("{0}, {1} {2}",
Street,
City,
State);
}
}
}

更新后的映射如下所示。请注意 FullAddress 被忽略,因为这是一个包含对 String.Format 的引用的计算字段。
        Mapper.CreateMap<Customer, CustomerViewModel>()
.ForMember(x => x.FirstName, o => o.MapFrom(s => s.FirstName))
.ForMember(x => x.LastName, o => o.MapFrom(s => s.LastName))
.ForMember(x => x.Street, o => o.MapFrom(s => s.Address.Street))
.ForMember(x => x.City, o => o.MapFrom(s => s.Address.City))
.ForMember(x => x.State, o => o.MapFrom(s => s.Address.State))
.ForMember(x => x.FullAddress, o => o.Ignore())
;

并且,通过这些修改,这个测试现在通过了。
    [Test]
public void can_map_AsQuerable_with_projection_this_FAILS()
{
var customerVms = Customers.AsQueryable().Project().To<CustomerViewModel>().ToList();
customerVms.ForEach(x => Assert.IsNotNullOrEmpty(x.FullAddress));
}

关于AutoMapper QueryableExtensions 在非规范化子对象时抛出 NullReference,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17376149/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com