返回匿名类型结果?

时间:2009-02-10 23:10:44

标签: c# linq linq-to-sql

使用下面的简单示例,使用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中指定该类型?还是有另一种更简单的方法吗?

16 个答案:

答案 0 :(得分:194)

我倾向于采用这种模式:

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

这意味着你有一个额外的类,但它编码快速简单,易于扩展,可重用和类型安全。

答案 1 :(得分:66)

可以返回匿名类型but it really isn't pretty

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

就我个人而言,我希望C#获得“命名匿名类型” - 即与匿名类型相同的行为,但是使用名称和属性声明,但就是这样。

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

答案 2 :(得分:16)

只是加上我的两分钱值得:-) 我最近学会了一种处理匿名对象的方法。它只能在面向.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's advice:确保您的[项目]目标框架是“.NET Framework 4”而不是“.NET Framework” 4客户资料“。

答案 3 :(得分:7)

如果不经过一些诡计,你就无法返回匿名类型。

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

有很多C#元组实现,使用一个shown here,你的代码会像这样工作。

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 :(得分:7)

你可以这样做:


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

答案 5 :(得分:7)

您必须首先使用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() 。它立即进行查询并从数据库中获取数据。第二个技巧是选择项目并使用对象初始化程序来生成包含项目的新对象。

希望这有帮助。

答案 6 :(得分: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;
}

答案 7 :(得分:3)

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

如果您有很多狗,请使用DataLoadOptions.LoadWith来减少数据库调用次数。

答案 8 :(得分:3)

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

您可能需要安装System.ValueTuple nuget包。

答案 9 :(得分: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;
}

答案 10 :(得分: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结构。

答案 11 :(得分:0)

如果你的数据库中的关系设置与BreedId上的foriegn密钥限制有关,你不是已经得到了吗?

DBML relationship mapping

所以我现在可以打电话:

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将有助于减少数据库调用。

答案 12 :(得分:0)

BreedId表中的

Dog显然是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);        
}

答案 13 :(得分:0)

如果主要想法是使发送到数据库服务器的SQL select语句只有必需的字段,而不是所有的Entity字段,那么你可以这样做:

FileOutputStream

答案 14 :(得分: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);

答案 15 :(得分:0)

这不能完全回答您的问题,但是Google根据关键字将我引到了这里。这是您可以从列表中查询匿名类型的方法:

var anon = model.MyType.Select(x => new { x.Item1, x.Item2});