LINQ to Entities仅支持无参数构造函数和初始化程序


132

我在此linq表达式中有此错误:

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              (
                                  nalTmp.Dziecko.Imie,
                                  nalTmp.Dziecko.Nazwisko,
                                  nalTmp.Miesiace.Nazwa,
                                  nalTmp.Kwota,
                                  nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  nalTmp.DataRozliczenia,
                                  nalTmp.TerminPlatnosci
                              )).ToList();

知道如何解决这个问题吗?我尝试用表达的任何组合...:/


1
您可以显示付款课程吗?还是至少在这里调用了ctor,特别是8参数ctor调用是否可以安全地换成0参数ctor调用并在对象上设置8属性?
詹姆斯·曼宁

23
当对我正在“更新”的对象使用Struct而不是Class时,出现了相同的错误。
HuckIt

3
TL; DR的问题是EF-LINQ试图将选择语句发送到EF提供程序,即。将其转换为SQL。要摆脱EF-LINQ,请在创建任何对象之前调用ToList()。

Answers:


127

没有有关“付款”的更多信息,这无济于事,但是假设您要创建一个Payments对象并根据列值设置其某些属性:

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              {
                                  Imie = nalTmp.Dziecko.Imie,
                                  Nazwisko = nalTmp.Dziecko.Nazwisko,
                                  Nazwa= nalTmp.Miesiace.Nazwa,
                                  Kwota = nalTmp.Kwota,
                                  NazwaRodzajuOplaty = nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  NazwaTypuOplaty = nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  DataRozliczenia = nalTmp.DataRozliczenia,
                                  TerminPlatnosci = nalTmp.TerminPlatnosci,
                              }).ToList();

10
这很好用,别忘了为该类添加一个空的构造函数。
现场直播

58
只是为了增加这个答案,您不能使用Structs做到这一点,只能使用Classes-我花了一点时间才弄清楚!
naspinski

4
是的,我认为Tony的答案比这一答案要好,因为它实际上解决了眼前的迫在眉睫的问题,而这一答案通过更改Payments类的性质并可能防止其不变而避免了这一问题。
Stephen Holt 2014年

这看起来很丑。EF6还有更好的方法吗?
工具包

115

如果您仍想使用构造函数进行初始化而不是使用属性(有时出于初始化目的需要此行为),请通过调用ToList()或来枚举查询ToArray(),然后使用Select(…)。因此,它将使用LINQ to Collections以及无法使用参数调用构造函数的局限性Select(…)将消失。

因此,您的代码应如下所示:

var naleznosci = db.Naleznosci
                          .Where(nalTmp => nalTmp.idDziecko == idDziec)
                          .ToList() // Here comes transfer to LINQ to Collections.
                          .Select(nalImp => new Payments
                              (
                                  nalTmp.Dziecko.Imie,
                                  nalTmp.Dziecko.Nazwisko,
                                  nalTmp.Miesiace.Nazwa,
                                  nalTmp.Kwota,
                                  nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  nalTmp.DataRozliczenia,
                                  nalTmp.TerminPlatnosci
                              ))
                          .ToList();

21
只是为了阐明其工作原理,最初声明的代码的问题在于,Entity Framework尝试将构造函数调用与LINQ查询的其余部分一起传递给SQL,当然,SQL无法进行构造复杂的物体!通过插入ToList()调用,您可以将可枚举数从尚未执行的SQL查询移至内存中的对象的具体列表,然后您可以按照自己喜欢的任何方式对其进行操作。
Stephen Holt 2014年

19
不要ToX()为此使用,请使用AsEnumerable()
罗林

1
.ToList()//传递到LINQ到Collections。是为我解决问题的那条线。
2015年

15
请注意,这将选择数据库级别的所有列,而通常情况下,它将仅选择所需的列
Hugh Jeffner 2016年

4
不仅如此,您可能还会有多个枚举。我不喜欢这种解决方案。
Bluebaron '16

47

我本人刚刚遇到此错误,我想我要补充一点,如果Payment类型是a struct,那么您也会遇到相同的错误,因为struct类型不支持无参数构造函数。

在这种情况下,转换Payment为类并使用对象初始化器语法将解决此问题。


这解决了我的问题。实际上,LINQ-2-SQL支持带有结构选择器的查询,在升级到EntityFramework时会出现问题。
Tomas Kubes 2015年

我讨厌结​​构。他们永远都不会做我想做的事
Simon_Weaver

DateTime在我的查询中创建了一个(是一个结构),这会导致相同的错误。将其提取到本地变量为我修复。感谢您的结构提示。
LuckyLikey

20

如果您像我一样,并且不想为正在构建的每个查询填充属性,则可以使用另一种方法来解决此问题。

var query = from orderDetail in context.OrderDetails
            join order in context.Orders on order.OrderId equals orderDetail.orderId
            select new { order, orderDetail };

此时,您具有一个包含匿名对象的IQueryable。如果要使用构造函数填充自定义对象,则可以执行以下操作:

return query.ToList().Select(r => new OrderDetails(r.order, r.orderDetail));

现在,您的自定义对象(将两个对象作为参数)可以根据需要填充属性。


这为我工作,并成为最干净的解决方案。那些建议消除构造函数并使用初始化器语法的人一定不能在构造函数中具有逻辑。那是我唯一一次依靠构造函数来填充对象属性的情况。感谢你的分享。
Bonez024

9

首先,我会避免使用

from ....
select new Payments
{
  Imie = nalTmp.Dziecko.Imie,
  ....
}

这需要一个空的构造函数,并且忽略封装,因此您说的是new Payments()是没有任何数据的有效付款,但是该对象必须至少具有一个值,并且可能还取决于您的域,并且具有其他必填字段。

最好有一个用于必填字段的构造函数,但只带所需的数据:

from ....
select new
{
  Imie = nalTmp.Dziecko.Imie,
  Nazwisko = nalTmp.Dziecko.Nazwisko
  ....
}
.ToList() // Here comes transfer to LINQ to Collections.
.Select(nalImp => new Payments
 (
  nalTmp.Imie,//assume this is a required field
  ...........
  )
  {
     Nazwisko = nalTmp.Nazwisko //optional field
  })
.ToList();

这是较小的邪恶。
Chalky

我也喜欢这样的东西。我尝试使用Tuple,但Tuple没有参数较少的构造函数。我填充了一个匿名对象,然后选择“元组”。
速记

一个拥抱封装和域的方法
inrandomwetrust

2

您可以尝试执行相同的操作,但是要使用扩展方法。数据库使用的提供者是什么?

var naleznosci = db.Naleznosci
                          .Where<TSource>(nalTmp => nalTmp.idDziecko == idDziec)
                          .Select<TSource, TResult>(
                             delegate(TSource nalTmp) { return new Payments
                             (
                                 nalTmp.Dziecko.Imie,
                                 nalTmp.Dziecko.Nazwisko,
                                 nalTmp.Miesiace.Nazwa,
                                 nalTmp.Kwota,
                                 nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                 nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                 nalTmp.DataRozliczenia,
                                 nalTmp.TerminPlatnosci
                             ); })
                          .ToList();

2

只是ToList()DbSet中前Select声明..实际DbSet保存为一个查询,它尚未完成。调用后,ToList()您正在处理对象,然后可以在查询中使用非默认构造函数。

这不是使用时间上最有效的方法,但是在小型设备上是一个选择。


1

是的,尝试这样。

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments()
                              {
                                  Dziecko.Imie,
                                  Dziecko.Nazwisko,
                                  Miesiace.Nazwa,
                                  Kwota,
                                  RodzajeOplat.NazwaRodzajuOplaty,
                                  RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  DataRozliczenia,
                                  TerminPlatnosci
                              }).ToList();

这将使用无参数构造函数来创建您的Payment对象,然后初始化花括号内列出的属性 { }


3
仅供参考(),无需使用Payemnts,因此可以是“选择新的付款{//初始值}
PostMan 2010年

现在我有错误:无法使用集合初始化程序初始化类型“ Payments”,因为它没有实现“ System.Collections.IEnumerable”
netmajor 2010年

正确-如果您要创建匿名类型(而不是Payments类的实例),那么Muad的代码就可以了,因为要设置的属性将隐式地是从中读取属性的名称。但是,由于这是一个“真实”类,因此您需要指定要设置为各种值的属性。
詹姆斯·曼宁

1

除了上述方法之外,您还可以将其解析为Enumerable集合,如下所示:

(from x in table
....
).AsEnumerable()
.Select(x => ...)

这还有一个额外的好处,就是在构建匿名对象时使生活更轻松,例如:

 (from x in tableName
select x.obj)
.Where(x => x.id != null)
.AsEnumerable()
.Select(x => new {
   objectOne = new ObjectName(x.property1, x.property2),
   parentObj = x
})
.ToList();

但是请记住,将集合解析为Enumerable会将其拉入内存,因此可能会占用大量资源!在此应谨慎使用。


1

另外,如果要使用具有多个对象的构造函数进行初始化,则如果Linq不返回任何值,则可能会出错。

因此,您可能需要执行以下操作:

(from x in table_1
   join y in table_2
   on x.id equals y.id
   select new {
   val1 = x,
   val2 = y
})
.DefaultIfEmpty()
.ToList()
.Select(a => new Val_Constructor(a.val1 != null ? a.val1 : new Val_1_Constructor(),
                            a.val2 != null ? a.val2 : new Val_2_Constructor()))
.ToList();

1

很抱歉参加聚会晚了,但是我发现了这一点之后,我认为应该共享它,因为它是我可以找到的最干净,最快,也节省内存的实现。

根据您的示例,您将编写:

public static IQueryable<Payments> ToPayments(this IQueryable<Naleznosci> source)
{
  Expression<Func<Naleznosci, Payments>> createPayments = naleznosci => new Payments
  {
    Imie = source.Dziecko.Imie,
    Nazwisko = source.Dziecko.Nazwisko,
    Nazwa= source.Miesiace.Nazwa,
    Kwota = source.Kwota,
    NazwaRodzajuOplaty = source.RodzajeOplat.NazwaRodzajuOplaty,
    NazwaTypuOplaty = source.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
    DataRozliczenia = source.DataRozliczenia,
    TerminPlatnosci = source.TerminPlatnosci,
  };

  return source.Select(createPayments);
}

这里的最大优势(如Damien Guard在链接的评论中指出的那样)是:

  • 使您避免在每次出现时都使用初始化模式。
  • 可以通过var foo = createPayments(bar);和通过myIQueryable.ToPayments()使用。

1

今天我遇到了同样的问题,我的解决方案与Yoda列出的解决方案相似,但是它仅适用于流利的语法。

使我的解决方案适应您的代码:我向对象类添加了以下静态方法

    /// <summary>
    /// use this instead of a parameritized constructor when you need support
    /// for LINQ to entities (fluent syntax only)
    /// </summary>
    /// <returns></returns>
    public static Func<Naleznosci, Payments> Initializer()
    {
        return n => new Payments
        {
             Imie = n.Dziecko.Imie,
             Nazwisko = n.Dziecko.Nazwisko,
             Nazwa = n.Miesiace.Nazwa,
             Kwota = n.Kwota,
             NazwaRodzajuOplaty = n.RodzajeOplat.NazwaRodzajuOplaty,
             NazwaTypuOplaty = n.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
             DataRozliczenia = n.DataRozliczenia,
             TerminPlatnosc = n.TerminPlatnosci
        };
    }

然后将基本查询更新为以下内容:

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select new Payments.Initializer());

从逻辑上讲,这等效于James Manning的解决方案,其优点是将成员初始化的膨胀推到类/数据传输对象上

注意:最初,我使用的是比“ Initializer”更具有描述性的名称,但是在回顾我的用法之后,我发现“ Initilizer”就足够了(至少出于我的目的)。

最后说明:
提出此解决方案后,我本来以为共享相同的代码并使之适用于查询语法也很简单。我不再相信情况会如此。我认为,如果您希望能够使用这种速记类型,则需要为上述每个(查询,流利)流利语言提供一个方法,该方法可以存在于对象类本身中。

对于查询语法,将需要扩展方法(或所使用的基类之外的某些方法)。(因为查询语法要操作IQueryable而不是T)

这是我最终使它适用于查询语法的示例。(Yoda已对此进行了详细说明,但我认为用法可能会更清楚,因为一开始我没有得到它)

/// <summary>
/// use this instead of a parameritized constructor when you need support
/// for LINQ to entities (query syntax only)
/// </summary>
/// <returns></returns>
public static IQueryable<Payments> Initializer(this IQueryable<Naleznosci> source)
{
    return source.Select(
        n => new Payments
        {
            Imie = n.Dziecko.Imie,
            Nazwisko = n.Dziecko.Nazwisko,
            Nazwa = n.Miesiace.Nazwa,
            Kwota = n.Kwota,
            NazwaRodzajuOplaty = n.RodzajeOplat.NazwaRodzajuOplaty,
            NazwaTypuOplaty = n.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
            DataRozliczenia = n.DataRozliczenia,
            TerminPlatnosc = n.TerminPlatnosci
    };
}

和用法

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select nalTmp).Initializer().ToList();

当我意识到我的初始答案不能很好地扩展时,添加了有关查询语法完整性的部分。在查询语法方面,@ yoda的答案可能更好。
wode

0

尽管回答晚了,它仍然可以帮助陷入困境的人。由于LINQ to实体不支持无参数对象构造。但是,IEnumerable的投影方法。

因此,在选择之前,只需将IQueryable转换为使用以下代码 IEnumerable

var result = myContext.SomeModelClass.AsEnumerable().Select(m => m.ToString());

它将正常工作。但是,它当然会失去本机查询的好处。


0
IQueryable<SqlResult> naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              {
                                  Imie = nalTmp.Dziecko.Imie,
                                  Nazwisko = nalTmp.Dziecko.Nazwisko,
                                  Nazwa= nalTmp.Miesiace.Nazwa,
                                  Kwota = nalTmp.Kwota,
                                  NazwaRodzajuOplaty =                          nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                              NazwaTypuOplaty = nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                              DataRozliczenia = nalTmp.DataRozliczenia,
                              TerminPlatnosci = nalTmp.TerminPlatnosci,
                          });
Repeater1.DataSource  = naleznosci.ToList(); 
Repeater1.DataBind();


public class SqlResult
{
        public string Imie { get; set; }
        public string Nazwisko { get; set; }
        ...
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.