由于实例化的值为空,所以向值类型转换失败。

7

我正在使用AutoMapper将我的实体映射到模型。

这些是我正在进行映射的模型:

public partial class Material
{
    public System.Guid Id { get; set; }
    public string Description { get; set; }
    public string EAN { get; set; }

    public virtual InventoryLine InventoryLine { get; set; }
}

public partial class InventoryLine
{
    public System.Guid MaterialId { get; set; }
    public Nullable<decimal> Quantity { get; set; }
    public decimal Price { get; set; }
    public Nullable<System.DateTime> LastInspectionDate { get; set; }
    public int TransactionsSinceLastInspection { get; set; }

    public virtual Material Material { get; set; }
}

public class InventoryLineViewModel
{
    public string EAN { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public decimal? Quantity { get; set; }
    public DateTime? LastInspectionDate { get; set; }
}

我有这个映射:

CreateMap<Material, InventoryLineViewModel>().ForMember(d => d.Price, o => o.MapFrom(s => s.InventoryLine.Price)).ForMember(d => d.Quantity, o => o.MapFrom(s => s.InventoryLine.Quantity)).ForMember(d => d.LastInspectionDate, o => o.MapFrom(s => s.InventoryLine.LastInspectionDate));

每当我运行这段代码时:
Mapper.Initialize(c => { c.AddProfile(new MapperProfile()); });
return context.Material.Include(i => i.InventoryLine).ProjectTo<InventoryLineViewModel>().ToList();

我遇到了这个错误:
转换为值类型“System.Decimal”失败,因为实例化的值为 null。结果类型的泛型参数或查询必须使用可空类型。
当我映射的所有类型都是相同数据类型时,这怎么可能发生呢?我甚至尝试在数据库和视图模型中将 Quantity 属性设置为非可空,但仍然收到相同的错误。
任何帮助将不胜感激 :-)

MaterialInventoryLine之间的关系类型是什么?我的意思是,它是“一对一”的,但哪一端是必需的,哪一端是可选的。有没有流畅的配置? - Ivan Stoev
@IvanStoev 需要 Material,而 InventoryLine 是可选的。没有流畅的配置 :-) - Nicholas Magnussen
也许有点跑题,但我会推荐你使用valueinjecter作为你的映射插件,我个人认为比AutoMapper好得多。 - silkfire
这并不是一个答案,但你可能会发现这篇文章值得一读:http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code - Tieson T.
4个回答

16

问题在于视图模型的Price属性类型是非可空的,但由于源InventoryLine是可选的,EF(如异常消息中所建议的)需要能够在源为null时存储可空值。

您可以通过以下两种方法来解决它:

(A) 使视图模型属性可为空:

public class InventoryLineViewModel
{
    public decimal? Price { get; set; }
}

(B) 保留视图模型,将映射更改如下:

.ForMember(d => d.Price, o => o.MapFrom(s => ((decimal?)s.InventoryLine.Price) ?? 0))
或者
.ForMember(d => d.Price, o => o.MapFrom(s => s.InventoryLine != null ? s.InventoryLine.Price : 0))

6

.NET 4.0中,Nullable类型有一个"GetValueOrDefault()"方法。因此,如果你将查询强制转换为Nullable,那么在完成时可以得到正确的类型。这个方法也会生成正确的单个SQL SELECT SUM查询,比其他通过linq返回整个记录集以后再求和的解决方案更快。

decimal result = ((decimal?)query.Where(w => w.Customer =="ABC").Sum(s =>  (decimal?)s.Amount)).GetValueOrDefault();

0

可能我们可以使用 ?? default


-3

我调用了.ToList(),这为我解决了这个错误。


2
这实际上是一个有效的解决方案,尽管可能需要一些澄清。错误消息的原因是当查询返回一个空集时,Sum 作用于可空序列,导致异常。在调用 Sum 之前先调用 ToList(),查询会转换为一个列表对象。因此,如果结果为空,您将得到一个空列表。Sum 的结果将为0。不会抛出任何异常。 - Derorrist
@Derorrist,是的,我同意。我也会写一些代码。 - Azri Zakaria

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接