Вернуть результаты анонимного типа? - PullRequest
181 голосов
/ 11 февраля 2009

Используя простой пример, приведенный ниже, каков наилучший способ вернуть результаты из нескольких таблиц с использованием Linq to 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, но есть ли способ вернуть это без необходимости создания пользовательского типа? Или мне нужно создать свой собственный класс для DogsWithBreedNames и указать этот тип в select? Или есть другой, более простой способ?

Ответы [ 15 ]

195 голосов
/ 11 февраля 2009

Я склонен идти по этому шаблону:

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;
}

Это означает, что у вас есть дополнительный класс, но его можно легко и быстро кодировать, легко расширять, использовать повторно и сохранять тип.

66 голосов
/ 11 февраля 2009

Вы можете вернуть анонимные типы, , но это действительно не очень .

В этом случае я думаю, что было бы гораздо лучше создать соответствующий тип. Если он будет использоваться только из типа, содержащего метод, сделайте его вложенным.

Лично я бы хотел, чтобы C # получал "именованные анонимные типы" - то есть такое же поведение, что и анонимные типы, но с именами и объявлениями свойств, но это так.

РЕДАКТИРОВАТЬ: Другие предлагают вернуть собак, а затем получить доступ к названию породы через путь свойства и т. Д. Это совершенно разумный подход, но IME приводит к ситуациям, когда вы выполняете запрос определенным образом из-за данных вы хотите использовать - и эта метаинформация теряется, когда вы просто возвращаете IEnumerable<Dog> - запрос может быть с ожиданием вашего использования (скажем) Breed вместо Owner из-за некоторых параметров загрузки и т.д., но если вы забудете об этом и начнете использовать другие свойства, ваше приложение может работать, но не так эффективно, как вы изначально предполагали. Конечно, я мог говорить чепуху или чрезмерную оптимизацию и т. Д ...

16 голосов
/ 07 января 2011

Просто, чтобы добавить ценность моих двух центов :-) Недавно я узнал способ обработки анонимных объектов. Его можно использовать только при нацеливании на платформу .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" not " Клиентский профиль .NET Framework 4 ".

7 голосов
/ 13 апреля 2011

Вы должны сначала использовать метод 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(). Он сразу делает запрос и получает данные из базы данных. Второй трюк - Выбор элементов и использование инициализатора объектов для создания новых объектов с загруженными элементами.

Надеюсь, это поможет.

7 голосов
/ 14 октября 2010

Вы можете сделать что-то вроде этого:


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();
}
7 голосов
/ 11 февраля 2009

Нет, вы не можете возвращать анонимные типы, не пройдя некоторые хитрости.

Если вы не используете 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);
    }
}
4 голосов
/ 09 апреля 2014

Теперь я понимаю, что компилятор не позволит мне вернуть набор анонимных типов, поскольку он ожидает Dogs, но есть ли способ вернуть это без необходимости создания пользовательского типа?

Используйте команду object , чтобы получить список анонимных типов без создания пользовательского типа. Это будет работать без ошибки компилятора (в .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;
}
3 голосов
/ 29 марта 2017

В C # 7 теперь вы можете использовать кортежи! ... что избавляет от необходимости создавать класс просто для возврата результата.

Вот пример кода:

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();
}

Возможно, вам потребуется установить пакет nuget System.ValueTuple.

3 голосов
/ 11 февраля 2009

Просто выберите собак, затем используйте dog.Breed.BreedName, это должно работать нормально.

Если у вас много собак, используйте DataLoadOptions.LoadWith, чтобы уменьшить количество вызовов db.

2 голосов
/ 25 июля 2012

Вы не можете возвращать анонимные типы напрямую, но вы можете зацикливать их с помощью универсального метода. Так же как и большинство методов расширения 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...