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

假设我有两个表:

Dogs:   Name, Age, BreedId
Breeds: BreedId, 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;
}

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


如果你要归还《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;
}

如果您希望Breed立即加载而不是延迟加载,只需使用适当的DataLoadOptions构造。


您可以返回匿名类型,但这并不漂亮。

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

就我个人而言,我希望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 expecting you to use (say) Breed rather 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...


不,如果不使用一些技巧,就不能返回匿名类型。

如果您不使用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);
    }
}

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

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


如果你在你的数据库中有一个关系设置,在BreedId上有一个外键约束,你还没有得到吗?

所以我现在可以调用:

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


我倾向于这样的模式:

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

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


你可以这样做:


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

只是补充一下我的意见:-) 我最近学习了一种处理匿名对象的方法。它只能在针对。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客户端配置文件”。


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

希望这能有所帮助。


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

BreedId in the Dog table is obviously a foreign key to the corresponding row in the Breed table. If you've got your database set up properly, LINQ to SQL should automatically create an association between the two tables. The resulting Dog class will have a Breed property, and the Breed class should have a Dogs collection. Setting it up this way, you can still return IEnumerable<Dog>, which is an object that includes the breed property. The only caveat is that you need to preload the breed object along with dog objects in the query so they can be accessed after the data context has been disposed, and (as another poster has suggested) execute a method on the collection that will cause the query to be performed immediately (ToArray in this case):

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

现在我意识到编译器不让我返回一组匿名类型,因为它期待狗,但有没有一种方法来返回这个而不必创建一个自定义类型?

使用Use对象可返回匿名类型列表,而无需创建自定义类型。 这将在没有编译器错误的情况下工作(在。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;
}

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

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

在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包。


尝试这样获取动态数据。您可以转换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);

这并没有完全回答你的问题,但谷歌根据关键字引导我到这里。这是从列表中查询匿名类型的方法:

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