C# 返回匿名类型结果?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/534690/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 07:09:29  来源:igfitidea点击:

Return anonymous type results?

c#linqlinq-to-sql

提问by Jonathan S.

Using the simple example below, what is the best way to return results from multiple tables using Linq to SQL?

使用下面的简单示例,使用 Linq to SQL 从多个表返回结果的最佳方法是什么?

Say I have two tables:

假设我有两个表:

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

I want to return all dogs with their BreedName. I should get all dogs using something like this with no problems:

我想用他们的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;
}

But if I want dogs with breeds and try this I have problems:

但是如果我想要有品种的狗并尝试这个我有问题:

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

Now I realize that the compiler won't let me return a set of anonymous types since it's expecting Dogs, but is there a way to return this without having to create a custom type? Or do I have to create my own class for DogsWithBreedNamesand specify that type in the select? Or is there another easier way?

现在我意识到编译器不会让我返回一组匿名类型,因为它期待 Dogs,但是有没有办法在不必创建自定义类型的情况下返回它?或者我是否必须创建自己的类DogsWithBreedNames并在选择中指定该类型?或者还有其他更简单的方法吗?

采纳答案by teedyay

I tend to go for this pattern:

我倾向于采用这种模式:

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

It means you have an extra class, but it's quick and easy to code, easily extensible, reusable and type-safe.

这意味着您有一个额外的类,但它可以快速且易于编码、易于扩展、可重用且类型安全。

回答by Dave Markle

Well, if you're returning Dogs, you'd do:

好吧,如果你要返回 Dogs,你会这样做:

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

If you want the Breed eager-loaded and not lazy-loaded, just use the appropriate DataLoadOptionsconstruct.

如果您想要 Breed 预先加载而不是延迟加载,只需使用适当的DataLoadOptions构造。

回答by Jon Skeet

You canreturn anonymous types, but it really isn't pretty.

可以返回匿名类型,但它确实不漂亮

In this case I think it would be far better to create the appropriate type. If it's only going to be used from within the type containing the method, make it a nested type.

在这种情况下,我认为创建适当的类型会好得多。如果只在包含该方法的类型中使用它,请将其设为嵌套类型。

Personally I'd like C# to get "named anonymous types" - i.e. the same behaviour as anonymous types, but with names and property declarations, but that's it.

我个人希望 C# 获得“命名匿名类型”——即与匿名类型相同的行为,但具有名称和属性声明,仅此而已。

EDIT: Others are suggesting returning dogs, and then accessing the breed name via a property path etc. That's a perfectly reasonable approach, but IME it leads to situations where you've done a query in a particular way because of the data you want to use - and that meta-information is lost when you just return IEnumerable<Dog>- the query may be expectingyou to use (say) Breedrather than Ownerdue to some load options etc, but if you forget that and start using other properties, your app may work but not as efficiently as you'd originally envisaged. Of course, I could be talking rubbish, or over-optimising, etc...

编辑:其他人建议返回狗,然后通过属性路径等访问品种名称。这是一个完全合理的方法,但 IME 会导致您以特定方式进行查询的情况,因为您想要的数据使用 - 当您刚返回时,元信息就会丢失IEnumerable<Dog>- 查询可能希望您使用(例如)Breed而不是Owner由于某些加载选项等,但是如果您忘记了这一点并开始使用其他属性,您的应用程序可能会工作,但是不像您最初设想的那样高效。当然,我可能会说废话,或过度优化,等等......

回答by joshperry

No you cannot return anonymous types without going through some trickery.

不,你不能不经过一些技巧就返回匿名类型。

If you were not using C#, what you would be looking for (returning multiple data without a concrete type) is called a Tuple.

如果您没有使用 C#,那么您要查找的内容(返回多个没有具体类型的数据)称为元组。

There are alot of C# tuple implementations, using the one shown here, your code would work like this.

有很多 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;
}

And on the calling site:

在调用站点上:

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

回答by Andrey Shchekin

Just select dogs, then use dog.Breed.BreedName, this should work fine.

只需选择狗,然后使用dog.Breed.BreedName,这应该可以正常工作。

If you have a lot of dogs, use DataLoadOptions.LoadWith to reduce the number of db calls.

如果你有很多狗,使用 DataLoadOptions.LoadWith 来减少 db 调用的次数。

回答by Zhaph - Ben Duguid

If you have a relationship setup in your database with a foriegn key restraint on BreedId don't you get that already?

如果您在数据库中建立了一个关系设置,并在 BreedId 上设置了外键限制,您是否已经明白了?

DBML relationship mapping

DBML 关系映射

So I can now call:

所以我现在可以调用:

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

And in the code that calls that:

在调用它的代码中:

var album = GetAlbum(1);

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

So in your instance you'd be calling something like dog.Breed.BreedName - as I said, this relies on your database being set up with these relationships.

因此,在您的实例中,您会调用诸如 dog.Breed.BreedName 之类的东西 - 正如我所说,这取决于您的数据库是否设置了这些关系。

As others have mentioned, the DataLoadOptions will help reduce the database calls if that's an issue.

正如其他人所提到的,如果这是一个问题,DataLoadOptions 将有助于减少数据库调用。

回答by tjscience

You could do something like this:

你可以这样做:


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

回答by Peter Perhá?

Just to add my two cents' worth :-) I recently learned a way of handling anonymous objects. It can only be used when targeting the .NET 4 framework and that only when adding a reference to System.Web.dll but then it's quite simple:

只是为了增加我的两分钱:-) 我最近学习了一种处理匿名对象的方法。它只能在面向 .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" };
    }
}

In order to be able to add a reference to System.Web.dll you'll have to follow rushonerok's advice: Make sure your [project's] target framework is ".NET Framework 4" not ".NET Framework 4 Client Profile".

为了能够添加对 System.Web.dll 的引用,您必须遵循rushonero 的建议:确保您的 [项目] 目标框架是“.NET Framework 4”而不是“.NET Framework 4 Client Profile”。

回答by Hakan KOSE

You must use ToList()method first to take rows from database and then select items as a class. Try this:

您必须首先使用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;}

So, the trick is first ToList(). It is immediately makes the query and gets the data from database. Second trick is Selecting items and using object initializerto generate new objects with items loaded.

所以,诀窍是首先ToList()。它立即进行查询并从数据库中获取数据。第二个技巧是选择项目并使用对象初始值设定项生成加载了项目的新对象。

Hope this helps.

希望这可以帮助。

回答by George Mamaladze

You can not return anonymous types directly, but you can loop them through your generic method. So do most of LINQ extension methods. There is no magic in there, while it looks like it they would return anonymous types. If parameter is anonymous result can also be anonymous.

您不能直接返回匿名类型,但可以通过泛型方法循环它们。大多数 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;
    }
}

Below an example based on code from original question:

下面是基于原始问题代码的示例:

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