익명 유형 결과를 반환 하시겠습니까?


194

아래의 간단한 예제를 사용하면 Linq를 사용하여 여러 테이블에서 결과를 SQL로 반환하는 가장 좋은 방법은 무엇입니까?

두 개의 테이블이 있다고 가정 해보십시오.

Dogs:   Name, Age, BreedId
Breeds: BreedId, BreedName

와 함께 모든 개를 반환하고 싶습니다 BreedName. 나는 모든 개에게 아무런 문제없이 이와 같은 것을 사용해야합니다.

public IQueryable<Dog> GetDogs()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select d;
    return result;
}

그러나 품종이있는 개를 원하고 이것을 시도하면 문제가 있습니다.

public IQueryable<Dog> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result;
}

이제 컴파일러가 Dogs를 기대하기 때문에 익명의 유형 집합을 반환 할 수는 없지만 사용자 정의 유형을 만들지 않고도 이것을 반환 할 수있는 방법이 있습니까? 아니면 자신의 클래스를 만들고 DogsWithBreedNamesselect에서 해당 유형을 지정해야합니까? 아니면 다른 쉬운 방법이 있습니까?


호기심으로 인해 모든 Linq 예제가 작동하지 않는 경우 익명 유형을 사용하는 이유는 무엇입니까? 예를 들어 , 이 예다음같습니다foreach (var cust in query) Console.WriteLine("id = {0}, City = {1}", cust.CustomerID, cust.City);
Hot Licks

@Hot Licks-이 예제의 Customer 테이블은 클래스로 표시되는 엔터티입니다. 이 예제는 해당 클래스의 정의를 보여주지 않습니다.
Jonathan S.

또한 컴파일러 swizzle가 "var"을 클래스 이름으로 바꾸고 있다고 말하지 않습니다.
Hot Licks

답변:


213

나는이 패턴을 찾는 경향이있다 :

public class DogWithBreed
{
    public Dog Dog { get; set; }
    public string BreedName  { get; set; }
}

public IQueryable<DogWithBreed> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new DogWithBreed()
                        {
                            Dog = d,
                            BreedName = b.BreedName
                        };
    return result;
}

그것은 당신이 여분의 클래스를 가지고 있음을 의미하지만, 빠르고 쉽고 코딩이 쉽고, 쉽게 확장 가능하고 재사용 가능하며 유형 안전합니다.


이 방법이 마음에 들지만 이제 강아지 이름을 표시하는 방법을 잘 모르겠습니다. 결과를 DataGrid에 바인딩하는 경우 DogWithBreed 클래스에서 명시 적으로 정의하지 않고 Dog에서 속성을 가져올 수 있습니까? 또는 표시하려는 각 필드에 대해 getter / setter를 만들어야합니까?
Jonathan S.

4
DataGrid에서 속성을 "Dog.Name"으로 지정할 수 없습니까? 내가 그들을 사용하지 않는 것이 충분히 그들을 증오 왜 지금 잊지 ...
teedyay

@JonathanS. 어떻게 당신이 템플릿 열에서 이것을 했습니까? 제발 비슷한 상황에 있다고 말해주십시오
rahularyansharma

이 두 클래스를 하나로 캡슐화하는이 방법이 좋습니다. 작업하기가 쉽습니다. 현재 컨텍스트에서만 사용되는 간단한 클래스를 만드는 것은 말할 것도 없습니다.
Linger

6
이것은 실제로 OP가 가지고있는 질문에 대답하지 않습니다. "사용자 정의 유형을 만들지 않고도 이것을 반환 할 수있는 방법이 있습니까?"
tjscience

69

익명 유형을 반환 있지만 실제로는 그렇지 않습니다 .

이 경우 적절한 유형을 만드는 것이 훨씬 낫습니다. 메서드가 포함 된 형식 내에서만 사용하려는 경우 중첩 형식으로 만듭니다.

개인적으로 C #에서 "명명 된 익명 형식"을 가져 오길 원합니다. 즉 익명 형식과 같은 동작이지만 이름과 속성 선언이 있지만 그게 전부입니다.

편집 : 다른 사람들은 개를 반환 제안 한 다음 속성 경로 등을 통해 품종 이름에 액세스합니다. 이는 완전히 합리적인 접근법이지만 IME를 사용하면 원하는 데이터로 인해 특정 방식으로 쿼리를 수행 한 상황이 발생합니다 사용 - 당신은 그냥 돌아 왔을 때, 그 메타 정보가 손실 IEnumerable<Dog>- 쿼리가 될 수 있습니다 기대 당신이 (말)를 사용하는 Breed대신 Owner등 일부로드 옵션으로 인해,하지만 당신은 잊지 및 기타 속성을 사용하기 시작하면, 앱이 작동 할 수 있지만, 원래 예상했던 것만 큼 효율적이지 않습니다. 물론, 나는 쓰레기 또는 과다 최적화 등을 이야기 할 수 있습니다 ...


3
이봐, 난 남용 될 방법에 대한 두려움 때문에 기능을 원하지 않는 사람은 아니지만, 익명의 익명 유형을 전달할 수 있는지 볼 수있는 종류의 코드를 상상할 수 있습니까? (Saver)
Dave Markle 23.16에

19
우리는 약간의 학대를 볼 수 있습니다. 우리는 기본적으로 터플을 원할 때 훨씬 간단한 코드를 볼 수도 있습니다. 아니 모든 복잡한 행동의 객체가 될 필요가있다. 때때로 "그냥 데이터"가 옳은 것입니다. 물론 IMO.
Jon Skeet

1
고마워, 그래서 당신이 선호하는 것은 이것과 같은 일회성보기 인 경우에도 유형을 만드는 것입니까? 동일한 데이터를 다른 방식으로 분리하는 보고서가 많으며 이러한 유형 (DogsWithBreeds, DogsWithOwnerNames 등)을 모두 만들지 않기를 바랐습니다.
Jonathan S.

1
익명의 유형을 사용할 있도록 데이터를 필요로하는 곳에 슬라이싱 부분을 넣거나 여러 가지 방법으로 슬라이스하지 않아도됩니다 . 그것은 어떤 식 으로든 짜증이 나지만, 나는 두려워하는 삶입니다 :(
Jon Skeet

17

내 2 센트의 가치를 추가하기 위해 :-) 최근에 익명의 객체를 처리하는 방법을 배웠습니다. .NET 4 프레임 워크를 대상으로 할 때만 사용할 수 있으며 System.Web.dll에 대한 참조를 추가 할 때만 사용할 수 있지만 매우 간단합니다.

...
using System.Web.Routing;
...

class Program
{
    static void Main(string[] args)
    {

        object anonymous = CallMethodThatReturnsObjectOfAnonymousType();
        //WHAT DO I DO WITH THIS?
        //I know! I'll use a RouteValueDictionary from System.Web.dll
        RouteValueDictionary rvd = new RouteValueDictionary(anonymous);
        Console.WriteLine("Hello, my name is {0} and I am a {1}", rvd["Name"], rvd["Occupation"]);
    }

    private static object CallMethodThatReturnsObjectOfAnonymousType()
    {
        return new { Id = 1, Name = "Peter Perhac", Occupation = "Software Developer" };
    }
}

System.Web.dll에 대한 참조를 추가하려면 rushonerok의 조언 을 따라야 합니다. [프로젝트] 대상 프레임 워크가 ".NET Framework 4 클라이언트 프로파일"이 아닌 ".NET Framework 4"인지 확인하십시오.


2
ASP.NET
Mvc

8

속임수없이 익명의 유형을 반환 할 수 없습니다.

C #을 사용하지 않는 경우 원하는 유형 (구체적인 유형없이 여러 데이터를 반환)을 Tuple이라고합니다.

여기표시된 것을 사용하여 C # 튜플 구현이 많이 있으며 코드는 다음과 같이 작동합니다.

public IEnumerable<Tuple<Dog,Breed>> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new Tuple<Dog,Breed>(d, b);

    return result;
}

그리고 전화 사이트에서 :

void main() {
    IEnumerable<Tuple<Dog,Breed>> dogs = GetDogsWithBreedNames();
    foreach(Tuple<Dog,Breed> tdog in dogs)
    {
        Console.WriteLine("Dog {0} {1}", tdog.param1.Name, tdog.param2.BreedName);
    }
}

7
작동하지 않습니다. 예외 NotSupportedException이를 : 만 매개 변수가없는 생성자와 초기화는 엔티티에 LINQ에서 지원됩니다
mshsayem

1
사실, Tuple 클래스에는 기본 생성자가 없으며 이런 방식으로 LINQ to Entities에서 올바르게 작동하지 않습니다. 그러나 질문에서와 같이 LINQ to SQL에서는 제대로 작동합니다. 나는 이것을 시도하지 않았지만와 함께 작동 할 수 있습니다 ... select Tuple.Create(d, b).
joshperry

1
일부 LINQ 공급자는 튜플을 지원하지 않으므로 익명 유형을 선택하여 IEnumerable로 변환 한 다음 해당 튜플을 선택할 수 없습니까?
TehPers

8

다음과 같이 할 수 있습니다.


public System.Collections.IEnumerable GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result.ToList();
}

8

ToList()먼저 메소드 를 사용 하여 데이터베이스에서 행을 가져온 다음 항목을 클래스로 선택해야합니다. 이 시도:

public partial class Dog {
    public string BreedName  { get; set; }}

List<Dog> GetDogsWithBreedNames(){
    var db = new DogDataContext(ConnectString);
    var result = (from d in db.Dogs
                  join b in db.Breeds on d.BreedId equals b.BreedId
                  select new
                  {
                      Name = d.Name,
                      BreedName = b.BreedName
                  }).ToList()
                    .Select(x=> 
                          new Dog{
                              Name = x.Name,
                              BreedName = x.BreedName,
                          }).ToList();
return result;}

따라서 트릭이 먼저ToList() 입니다. 즉시 쿼리하여 데이터베이스에서 데이터를 가져옵니다. 두 번째 요령은 항목을 선택하고 객체 이니셜 라이저사용하여 항목이로드 된 새 객체를 생성하는 것입니다.

도움이 되었기를 바랍니다.


8

C # 7에서는 이제 tuples!를 사용할 수 있습니다. 결과를 반환하기 위해 클래스를 만들 필요가 없습니다.

다음은 샘플 코드입니다.

public List<(string Name, string BreedName)> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
             join b in db.Breeds on d.BreedId equals b.BreedId
             select new
             {
                Name = d.Name,
                BreedName = b.BreedName
             }.ToList();

    return result.Select(r => (r.Name, r.BreedName)).ToList();
}

그래도 System.ValueTuple nuget 패키지를 설치해야 할 수도 있습니다.


4

이제 컴파일러가 Dogs를 기대하기 때문에 익명 형식 집합을 반환 할 수는 없지만 사용자 지정 형식을 만들지 않고이를 반환 할 수있는 방법이 있습니까?

객체 사용을 사용 하여 사용자 정의 유형을 만들지 않고 익명 유형 목록을 반환 하십시오 . 이것은 컴파일러 오류 (.net 4.0에서)없이 작동합니다. 목록을 클라이언트에 반환 한 다음 JavaScript로 구문 분석했습니다.

public object GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result;
}

1
내 의견으로는 메소드 서명이 다음과 같은 경우 더 정확하고 읽을 수 있습니다. public IEnumerable <object> GetDogsWithBreedNames ()
pistol-pete

3

개를 선택한 다음을 사용 dog.Breed.BreedName하면 제대로 작동합니다.

개가 많은 경우 DataLoadOptions.LoadWith를 사용하여 db 호출 수를 줄이십시오.


2

익명 유형을 직접 반환 할 수는 없지만 일반적인 방법으로 반복 할 수 있습니다. 대부분의 LINQ 확장 방법도 마찬가지입니다. 거기에는 마법이 없지만 익명 형식을 반환하는 것처럼 보입니다. 매개 변수가 익명이면 결과도 익명 일 수 있습니다.

var result = Repeat(new { Name = "Foo Bar", Age = 100 }, 10);

private static IEnumerable<TResult> Repeat<TResult>(TResult element, int count)
{
    for(int i=0; i<count; i++)
    {
        yield return element;
    }
}

원래 질문의 코드를 기반으로 한 예제 아래 :

var result = GetDogsWithBreedNames((Name, BreedName) => new {Name, BreedName });


public static IQueryable<TResult> GetDogsWithBreedNames<TResult>(Func<object, object, TResult> creator)
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                    join b in db.Breeds on d.BreedId equals b.BreedId
                    select creator(d.Name, b.BreedName);
    return result;
}

0

글쎄, 당신이 개를 반환한다면, 당신은 할 것입니다 :

public IQueryable<Dog> GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    return from d in db.Dogs
           join b in db.Breeds on d.BreedId equals b.BreedId
           select d;
}

Breed가 열성적으로로드되고 지연로드되지 않게하려면 적절한 DataLoadOptions 구성을 사용하십시오 .


0

BreedIdDog테이블 분명히에서 해당 행에 대한 외래 키 Breed테이블. 데이터베이스를 올바르게 설정 한 경우 LINQ to SQL은 자동으로 두 테이블 사이에 연결을 만들어야합니다. 결과 Dog 클래스에는 Breed 속성이 있으며 Breed 클래스에는 Dogs 컬렉션이 있어야합니다. 이 방법으로 설정하면 여전히 IEnumerable<Dog>품종 속성을 포함하는 객체 인을 반환 할 수 있습니다 . 유일한 단점은 쿼리에서 개 개체와 함께 개 개체를 미리로드하여 데이터 컨텍스트가 삭제 된 후 액세스 할 수 있고 다른 포스터에서 제안한대로 컬렉션에서 메서드를 실행해야한다는 것입니다. 즉시 수행 할 쿼리 (이 경우 ToArray) :

public IEnumerable<Dog> GetDogs()
{
    using (var db = new DogDataContext(ConnectString))
    {
        db.LoadOptions.LoadWith<Dog>(i => i.Breed);
        return db.Dogs.ToArray();
    }

}

그런 다음 각 강아지의 품종에 접근하는 것은 사소한 일입니다.

foreach (var dog in GetDogs())
{
    Console.WriteLine("Dog's Name: {0}", dog.Name);
    Console.WriteLine("Dog's Breed: {0}", dog.Breed.Name);        
}

0

데이터베이스 서버로 전송 된 SQL select 문이 모든 엔티티 필드가 아닌 필수 필드 만 갖도록하는 것이 주요 아이디어라면, u는 다음을 수행 할 수 있습니다.

public class Class1
{
    public IList<Car> getCarsByProjectionOnSmallNumberOfProperties()
    {

        try
        {
            //Get the SQL Context:
            CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext dbContext 
                = new CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext();

            //Specify the Context of your main entity e.g. Car:
            var oDBQuery = dbContext.Set<Car>();

            //Project on some of its fields, so the created select statment that is
            // sent to the database server, will have only the required fields By making a new anonymouse type
            var queryProjectedOnSmallSetOfProperties 
                = from x in oDBQuery
                    select new
                    {
                        x.carNo,
                        x.eName,
                        x.aName
                    };

            //Convert the anonymouse type back to the main entity e.g. Car
            var queryConvertAnonymousToOriginal 
                = from x in queryProjectedOnSmallSetOfProperties
                    select new Car
                    {
                        carNo = x.carNo,
                        eName = x.eName,
                        aName = x.aName
                    };

            //return the IList<Car> that is wanted
            var lst = queryConvertAnonymousToOriginal.ToList();
            return lst;

        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.ToString());
            throw;
        }
    }
}

0

동적 데이터를 얻으려면 이것을 시도하십시오. List <>에 대한 코드를 변환 할 수 있습니다

public object GetDogsWithBreedNames()
{
    var db = new DogDataContext(ConnectString);
    var result = from d in db.Dogs
                 join b in db.Breeds on d.BreedId equals b.BreedId
                 select new
                        {
                            Name = d.Name,
                            BreedName = b.BreedName
                        };
    return result.FirstOrDefault();
}

dynamic dogInfo=GetDogsWithBreedNames();
var name = dogInfo.GetType().GetProperty("Name").GetValue(dogInfo, null);
var breedName = dogInfo.GetType().GetProperty("BreedName").GetValue(dogInfo, null);

0

BreedId에서 foriegn 키 제한을 사용하여 데이터베이스에 관계 설정이있는 경우 이미 얻지 않았습니까?

DBML 관계 매핑

이제 전화 할 수 있습니다.

internal Album GetAlbum(int albumId)
{
    return Albums.SingleOrDefault(a => a.AlbumID == albumId);
}

그리고 그것을 호출하는 코드에서 :

var album = GetAlbum(1);

foreach (Photo photo in album.Photos)
{
    [...]
}

따라서 귀하의 인스턴스에서 dog.Breed.BreedName과 같은 것을 호출 할 것입니다. 앞에서 말했듯이 이것은이 관계로 설정된 데이터베이스에 의존합니다.

다른 사람들이 언급했듯이 DataLoadOptions는 문제가있는 경우 데이터베이스 호출을 줄이는 데 도움이됩니다.


0

이것은 귀하의 질문에 정확하게 대답하지는 않지만 Google은 키워드를 바탕으로 저를 이끌었습니다. 다음은 목록에서 익명 유형을 쿼리하는 방법입니다.

var anon = model.MyType.Select(x => new { x.Item1, x.Item2});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.