代码之家  ›  专栏  ›  技术社区  ›  Nick Craver

LINQ外部联接动态OrderBy

  •  4
  • Nick Craver  · 技术社区  · 16 年前

    在类似以下的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处理它?

    附加说明:添加时 .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,它都会恢复为按排序 (网格上的默认排序)。在我们的网格控件中,这样的连接类数量非常有限,每个类都有一个开关和表达式用例根本不是问题。有没有关于解决方案或完全不同的方法的想法?

    3 回复  |  直到 16 年前
        1
  •  3
  •   StriplingWarrior    16 年前

    我习惯于在代码中使用LINQtoEntities,但我在LINQPad中使用LINQtoSQL的次数已经足够多了,我对它已经相当熟悉了。我不是

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

    所有结果的线索类型均为null。LINQtoSQL只将空引用视为具有所有空属性的值。以下是生成的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]
    

    注意左边 参加这不符合你的要求吗?

    使查询动态化可能相当困难,这取决于您使其动态化的程度。如果您希望能够根据要返回的任何属性,基于传递到方法中的字符串值进行排序,则有一种解决方案:

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

        2
  •  2
  •   Community Mohan Dere    9 年前

    我刚刚尝试了与您类似的设置,这应该可以工作:

    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 .

        3
  •  0
  •   JustLoren    16 年前

    添加.OrderBy时(s=> s、 主地址)我们无法获得订单

    这是因为PrimaryAddress只存在于代码中。LINQ语句将输出并获取所有行以生成一个地址,然后在代码中将它们合并在一起。 您是否尝试过某种形式的.OrderBy(a=>a.Address)?也就是说,引用子表的列而不是派生名称。

    推荐文章