LINQ Outer加入动态OrderBy

时间:2009-09-25 12:14:04

标签: c# linq linq-to-sql gridview linqdatasource

在如下的Linq声明中(缩小到问题的相关部分):

var sites = from s in DataContext.Sites
            join add in DataContext.Address 
              on s.PrimaryAddress equals add into sa
    from a in sa.DefaultIfEmpty()
            select new {
                        s.Id,
                        s.SiteName,
                        PrimaryAddress = a
                       };

我们遇到的问题是,最终基于GridView / LinqDataSource组合的控件无法在PrimaryAddress连接类上正确排序(忽略它)。对于像这样的所有连接类,我们看到了相同的行为。 GridView有什么办法可以处理这个问题吗? 或者,有没有一种方法可以通过表达式在动态OrderBy中的代码中处理它?<​​/ strong>

附加说明:添加.OrderBy(s => s.PrimaryAddress)时我们得到Cannot order by type 'Address',在执行.OrderBy(gs => gs.PrimaryBusinessAddress.AddressLine1)时我们得到Specified method is not supported.当对LinqDataSource.OrderBy本身进行排序时,为时已晚。 ..只对当前页面上的记录进行排序,而不是对整个集合进行排序。

为清楚起见,这是网格中地址的格式:

public partial class Address
{
  public override string ToString()
  {
    return string.Format("{0} {1}{2} {3}",
    AddressLine1,
    City,
    !string.IsNullOrEmpty(State) && State != "None" ? ", " + State : string.Empty,
    !string.IsNullOrEmpty(Country) ? string.Format("({0})", Country) : string.Empty);
  }
}

如果我们可以按AddressLine1 + City + State + Country排序,那就足够了,但我不确定如何通过表达式树来执行此操作...无论我们通过表达式指定什么OrderBy,它都会恢复为排序通过 s.SiteName (网格上的默认排序)。这样的联接类数量非常有限,在我们的网格控件中显示,有一个开关和每个的表达式案例根本不会成为问题。对解决方案的任何想法,或完全不同的方法?

3 个答案:

答案 0 :(得分:3)

我习惯在代码中使用Linq to Entities,但我在LINQPad中经常使用LINQ to SQL,我已经对它非常熟悉了。我不是完全确定我知道你遇到困难的地方,但我认为以下情况应该有效:

var sites = from s in DataContext.Sites
            orderby s.PrimaryAddress.AddressLine1,
                     s.PrimaryAddress.City,
                     s.PrimaryAddress.State,
                     s.PrimaryAddress.Country
            select new 
            {
                s.Id,
                s.SiteName,
                s.PrimaryAddress
            };

让我知道是否有一些我不理解的东西。

更新

我不确定为什么这不适合你。我刚刚在LINQPad(LINQ to SQL模式)中执行了以下操作:

from p in person
orderby p.clue_type.clue_type_id,
        p.clue_type.clue_type
select new
{
    p.person_id, p.clue_type
}

结果都有clue_type = null。 LINQ to SQL只将空引用视为具有全零属性的值。这是生成的SQL:

SELECT TOP (10) [t0].[person_id], [t2].[test], [t2].[clue_type_id], [t2].[clue_type]
FROM [person] AS [t0]
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[clue_type_id], [t1].[clue_type]
    FROM [clue_types] AS [t1]
    ) AS [t2] ON [t2].[clue_type_id] = [t0].[clue_type_id]
ORDER BY [t2].[clue_type_id], [t2].[clue_type]

注意LEFT OUTER JOIN。这不会做你要求的吗?

更新2

使查询动态化可能相当困难,具体取决于您制作动态的动态。如果您希望能够根据传递给方法的字符串值按照您要返回的任何属性进行排序,那么这是一个解决方案:

public class SiteDisplayInfo
{
    public int Id {get;set;}
    public string SiteName {get;set;}
    public string PrimaryAddress {get;set;}

    public static readonly Dictionary<string, Func<IQueryable<Site>, IOrderedQueryable<Site>>> OrderByFuncs = 
    new Dictionary<string, Func<IQueryable<Site>, IOrderedQueryable<Site>>>
    {
        {"Id", q => q.OrderBy(s => s.Id)},
        {"SiteName", q => q.OrderBy(s => s.SiteName)},
        {"PrimaryAddress", 
        q => q.OrderBy(s => s.PrimaryAddress.AddressLine1)
                             .ThenBy(s => s.PrimaryAddress.City)}
    };
}

...

public IEnumerable<SiteDisplayInfo> GetSites(string orderByString)
{
    IQueryable<Site> sites = DataBase.Sites;
    if (orderByString != null && SiteDisplayInfo.OrderByFuncs.ContainsKey(orderByString))
    {
        sites = SiteDisplayInfo.OrderByFuncs[orderByString](sites);
    }
    var query = from s in sites
                select new SiteDisplayInfo
                {
                    Id = s.Id,
                    SiteName = s.SiteName,
                    PrimaryAddress = s.PrimaryAddress.AddressLine1 + s.PrimaryAddress.City
                };
    return query.ToList();
}

还有一些其他方法可以做类似的事情,但这会给你一个大致的想法。

答案 1 :(得分:2)

我刚刚尝试了类似的设置,这应该有效:

var sites = from s in dc.Sites
    join addr in dc.Addresses
        on s.PrimaryAddress equals addr into sa
    from a in sa.DefaultIfEmpty()
    orderby a.AddressLine1, a.City, a.State, a.Country /* insert orderby here */
    select new
        {
            s.Id,
            s.SiteName,
            PrimaryAddress = a
        };

如果您想更改orderby的方向,只需将降序添加到每一列:

orderby a.AddressLine1 descending, a.City descending, a.State descending, a.Country descending

尽管这种方法有效,但它并不完全是动态的。您必须重写查询以按其他方向或列顺序排序。

如果这是您想要的,我建议使用方法方法和一些额外的管道,或使用动态OrderBy的完全动态方法,允许您将列名指定为文字。对于后者,请检查此topic和此blogpost

答案 2 :(得分:0)

  

添加.OrderBy时(s =&gt;   s.PrimaryAddress)我们得到无法订购   按类型'地址'

那是因为PrimaryAddress只存在于代码中。 LINQ语句将要出去并获取所有行来创建一个地址,然后在代码中将它们合并在一起。 你试过某种形式的.OrderBy(a =&gt; a.Address)吗?即,引用子表的列而不是派生名称。