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-param ctor 호출을 0-param ctor 호출로 안전하게 교체하고 객체에 8 개의 속성을 설정할 수 있는지 여부
제임스 매닝

23
내가 "새로 만든"객체에 클래스 대신 Struct를 사용할 때 이와 동일한 오류가 발생했습니다.
HuckIt

3
TL; DR 문제는 EF-LINQ가 select 문을 EF 공급자에게 보내려고한다는 것입니다. SQL로 변환하십시오. EF-LINQ에서 나가려면 객체를 만들기 전에 ToList ()를 호출하십시오.

답변:


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로는이 작업을 수행 할 수 없으며 클래스만이 가능합니다.
naspinski

4
네, Tony의 대답은 현재 당면한 문제를 실제로 해결하기 때문에이 답변보다 낫다고 생각합니다.이 방법은 Payments 클래스의 특성을 변경하고 불변을 방지하여 문제를 우회합니다.
Stephen Holt

이것은 못 생겼다. 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에 전달하려고 시도한다는 것입니다. 복잡한 물건! ToList () 호출을 삽입하면 아직 실행되지 않은 SQL 쿼리에서 메모리의 구체적인 개체 목록으로 열거 가능 항목을 이동 한 다음 원하는 방식으로 조작 할 수 있습니다.
Stephen Holt

19
사용하지 마십시오 ToX()이, 사용 AsEnumerable().
롤링

1
.ToList () // LINQ to Collections로 전송됩니다. 나를 위해 문제를 해결하는 줄입니다.
Ram

15
이것은 일반적으로 필요한 열만 선택하는 db 수준의 모든 열을 선택합니다.
Hugh Jeffner

4
그뿐만 아니라 여러 열거 형이있을 것입니다. 이 솔루션이 마음에 들지 않습니다.
블루 바론

47

방금이 오류가 발생하면 Payment유형이 인 경우 struct동일한 오류가 발생한다고 덧붙였습니다.struct 매개 변수 인 경우 매개 변수가없는 생성자를 지원하지 않기 .

이 경우 Payment클래스 로 변환 하고 객체 초기화 구문을 사용하면 문제가 해결됩니다.


이것은 나에게서 문제를 해결합니다. 실제로 struct selector가있는이 쿼리는 LINQ-2-SQL에서 지원되며 EntityFramework로 업그레이드 할 때 문제가됩니다.
Tomas Kubes

나는 구조체가 싫어. 그들은 내가 원하는 것을 끝내지 않습니다
Simon_Weaver

DateTime내 쿼리 내에 (struct)를 만들었는데 같은 오류가 발생했습니다. 로컬 변수에 추출하면 문제가 해결되었습니다. 구조체 힌트에 감사드립니다.
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에는 매개 변수가 적은 생성자가 없습니다. 익명 개체를 채운 다음 Tuple을 선택합니다.
Tchaps

캡슐화 및 도메인을 수용하기위한 하나의 최대
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는 필요하지 않으므로`select new Payments {// init values} 일 수 있습니다
PostMan

이제 오류가 발생했습니다 : 'System.Collections.IEnumerable'을 구현하지 않기 때문에 컬렉션 초기화 프로그램으로 'Payments'유형을 초기화 할 수 없습니다.
netmajor

right-Payments 클래스의 인스턴스 대신 anon 유형을 작성하는 경우 설정할 특성이 내재적으로 읽히는 특성의 이름이므로 Muad의 코드는 양호합니다. 그러나 '실제'클래스이므로 다양한 값으로 설정할 속성을 지정해야합니다.
James Manning

1

위에서 언급 한 방법 외에도 다음과 같이 열거 가능한 컬렉션으로 구문 분석 할 수 있습니다.

(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"가 충분하다는 것을 알았습니다 (적어도 제 목적으로는).

최종 메모 :
이 솔루션을 만든 후에는 원래 동일한 코드를 공유하고이를 쿼리 구문에 맞게 적용하는 것이 간단하다고 생각했습니다. 나는 더 이상 사실이라고 생각하지 않습니다. 이 유형의 속기 구성을 사용하려면 위에서 설명한대로 객체 클래스 자체에 존재할 수있는 각각의 (쿼리, 유창한) 유창한 메소드가 필요하다고 생각합니다.

쿼리 구문의 경우 확장 메소드 (또는 사용중인 기본 클래스 외부의 일부 메소드)가 필요합니다. (쿼리 구문은 T가 아닌 IQueryable을 작동 시키려고하기 때문에)

다음은 쿼리 구문에서 작동하도록하는 데 사용한 샘플입니다. (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는 매개 변수없는 오브젝트 구성을 지원하지 않습니다. 그러나 IEnumerable 의 투영 방법 .

따라서 선택하기 전에 다음 코드를 사용하여 IQueryableIEnumerable 로 변환하십시오 .

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; }
        ...
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.