使用下面的简单示例,使用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创建自己的类,并在选择中指定该类型?或者还有其他更简单的方法吗?


当前回答

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

希望这能有所帮助。

其他回答

如果你要归还《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构造。

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

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

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

就我个人而言,我希望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...

如果主要的想法是让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;
        }
    }
}