无法将类型X强制转换为类型Y。LINQ to Entities仅支持将EDM原始类型或枚举类型进行转换。

4
我完全了解这个错误,但是是否有一种方法可以实现一些代码,使linq to entities理解我的自定义类型应该被视为一个decimal原始类型?
我创建了一个自定义的Money类型,它接受一个可空的decimal作为其中一个构造函数重载的参数。它具有隐式操作符重载和所有其他酷炫的特性。我想在我的linq到实体投影中使用这个自定义类型,但是标题中却出现了错误。
是否有一个属性可以声明在我的Money类型上,说:“嘿,在linq投影的目的下,我是decimal原始类型”?
是否有一种自定义表达式树/访问器的方法可以帮助在此处声明所有类型的Money都要被视为linq投影的decimal原始类型?
[HeyImADecimalForLinqProjections] // custom attribute
public class Money : IComparable<Money>
{
    public Money()
    {
        Value = decimal.Zero;
    }
    public Money(decimal? value)
    {
        if (value.HasValue)
            Value = value.Value;
        else
            Value = decimal.Zero;
    }
    public static implicit operator Money(decimal value)
    {
        return new Money(value);
    }
    public static implicit operator Money(decimal? value)
    {
        return new Money(value.Value);
    }
    ...
}

示例投影:

var items = orderQuery.Where(a => a.OrderId = 345)
    .Select(a => new OrderModel()
        {
            OrderId = a.OrderId,
            SubtotalIncludingTax = a.SubtotalIncludingTax, // no dice, SubtotalIncludingTax is of type Money - Unable to cast the type 'System.Decimal' to type 'Money'. LINQ to Entities only supports casting EDM primitive or enumeration types.
            SubtotalIncludingTaxRaw = a.SubtotalIncludingTax, // this is fine because SubtotalIncludingTaxRaw is of type decimal
        })
    .ToList();

我的当前解决方案虽然可行,但我不喜欢它。我首先将投影到一个匿名类型,然后再将匿名类型投影到我的实际模型中。这个例子非常简化,这种双重投影解决方案会导致很多重复,我宁愿避免。

// first get everything as anonymous projection objects in linq to entities
var anonymousItems = orderQuery.Where(a => a.OrderId = 345)
    .Select(a =>
        {
            OrderId = a.OrderId,
            SubtotalIncludingTax = a.SubtotalIncludingTax // decimal to decimal
    .ToList();

// now cast to our actual model since we are back in .Net world
var items = anonymousItems
    .Select(a => new OrderModel()
        {
            OrderId = a.OrderId,
            SubtotalIncludingTax = a.SubtotalIncludingTax // decimal to Money
    .ToList();

这只是一个猜测,但你能否设置另一个隐式操作符,使其反向工作,即将Money转换为decimal?我不知道这是否足以满足linq的要求,但值得一试:https://dotnetfiddle.net/QLaccg - Paul Griffin
它实际上对MoneydecimalMoneyNullable<decimal>都有隐式转换,但是不起作用。我还实现了一堆接口,希望其中一个能做到,但是同样没有成功。IComparable、IComparable<string>、IComparable<decimal>、IComparable<decimal?>、IComparable<Money>、IEquatable<string>、IEquatable<decimal>、IEquatable<decimal?>、IEquatable<Money>、IFormattable、IValueProvider、IValidatableObject、IConvertible、ICloneable - TugboatCaptain
1个回答

2

目前,Entity Framework 不支持这样的类型转换。您可以通过一个映射属性和一个未映射属性来实现一个解决方案,例如:

public decimal RawTotalIncludingTax {get;set;}
public Money TotalIncludingTax 
{ 
   get { return RawTotalIncludingtax; }
   set { RawTotalIncludingTax = value; }
}

这可能会将匿名类型转换为具体类型的选择项保存下来,但仍然存在类似的问题。更有可能解决问题的方法是使用扩展方法执行以下操作:
using System;

public static class MoneyExtensions
{
    public static string Format(this decimal val){
        if(val < 0)
            return "(" + Math.Abs(val).ToString("N2") + ")";
        else
            return val.ToString("N2");
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(2.2m.Format());
        Console.WriteLine((-12.42m).Format());
    }
}

这样你就不需要自定义类型,但仍然可以使用MoneyExtensions和你想要的语法!

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