【问题标题】:LINQ expression for climbing object hierarchy?用于攀爬对象层次结构的 LINQ 表达式?
【发布时间】:2010-12-17 17:15:13
【问题描述】:

我有一些代码需要扫描分层数据库架构,寻找架构中定义的具有某些有趣属性的属性。我想制作这些属性名称的(平面)列表。

为了(希望)减少混淆,我将架构中定义的类称为“种类”,因为该架构没有描述 C# 类层次结构。

架构可作为 .NET 对象树提供给我;我不需要解析任何 XML 或任何东西。问题是我将在不同点进入模式树,我需要确保我知道从基本类型继承的有趣属性以及直接定义在我的类型上的属性正在看。

results = new List<PropertyDefinition>;

foreach (Kind objKind in objDescription.PossibleKinds)
{
    // Iterate up the schema hierarchy
    while (objKind != null) 
    {
        foreach (PropertyDefinition prop in objKind.PropertyDefinitions)
        {
            if (prop.IsInteresting)
                results.Add(prop);
        }
        // Move up a level in the hierarchical relationship
        objKind = objKind.BaseKind;
    }
}

无论如何,我想知道是否可以编写等效的 LINQ 语句。最外层的 foreach 循环是微不足道的(为了清楚起见,实际上我省略了另一个),但我不确定是否可以在 LINQ 查询中捕获层次结构的迭代。

var query = from objKind in objDescription.PossibleKinds
            // What goes here?
            from prop in objKind.PropertyDescriptions
            where prop.IsInteresting
            select prop;

我想这类似于编写一个 LINQ 查询,该查询从链表中的一个节点开始,并不断迭代链表直到它到达末尾。这可能吗?

【问题讨论】:

    标签: c# linq recursion linq-to-objects


    【解决方案1】:

    您不能真正使用“纯”LINQ 做到这一点,但您可以将其与枚举器方法混合使用,这样您就会走上正轨。例如,在 Kind 类型上定义一个扩展方法,如下所示:

    public static IEnumerable<Kind> GetInstanceAndBaseKinds(
        this Kind instance)
    {
        while (instance != null)
        {
            yield return instance;
            instance = instance.BaseKind;
        }
    }
    

    现在您可以在 LINQ 查询中使用此方法:

    from kind in objDescription.PossibleKinds
    from baseKind in kind.GetInstanceAndBaseKinds()
    from property in baseKind.PropertyDefinitions
    where property.IsInteresting
    select property;
    

    【讨论】:

    • 打败我。我几乎有相同的答案。我只是使用了一种扩展方法,而不是 Kind 类定义本身中的一种。否则相同。你得到了我的支持。
    • 在这种情况下,我无法控制 Kind 类的定义,所以看起来扩展方法也很有用。谢谢你们!
    【解决方案2】:

    Here is a post 引入了一个扩展方法Descendants(),它可能会引导你走向正确的方向。

    【讨论】:

      【解决方案3】:

      你可以递归Action&lt;Kind, List&lt;PropertyDefinition&gt;:

      Action<Kind, List<PropertyDefinition> action = null;
      action = (k, l) => {
        if (k == null) return;
      
        foreach (var definition in k.PropertyDefinitions)
        {
          if (definition.IsInteresting)
            l.Add(definition);
        }
      
        action(k.BaseKind, l);
      };
      
      var results = new List<PropertyDefinition>();
      foreach (var kind in objDescription.PossibleKinds)
      {
        action(kind, results);
      }
      

      请注意,您需要如何将声明与操作的分配分开。希望对您有所帮助。

      【讨论】:

        【解决方案4】:

        请考虑以下建议:

        使用LinqToSQL映射表'dbo.Groups',生成如下类Group

        [Table(Name = "dbo.Groups")]
        public partial class Group
        {
            [Column(Storage = "_Id", AutoSync = AutoSync.OnInsert, DbType = "Int NOT NULL IDENTITY", IsPrimaryKey = true, IsDbGenerated = true)]
            public int Id
            {
                get { return this._Id; }
                set { this._Id = value; }
            }
        
            [Column(Storage = "_ParentId", DbType = "Int")]
            public System.Nullable<int> ParentId
            {
                get { return this._ParentId; }
                set { this._ParentId = value; }
            }
            [Association(Name = "Group_Group", Storage = "_Children", ThisKey = "Id", OtherKey = "ParentId")]
            public EntitySet<Group> Children
            {
                get { return this._Children; }
                set { this._Children.Assign(value); }
            }    
        }
        

        然后在 LinqExtension 之后添加(how-to-get-a-tree-structured-table-data-by-linq 提供)

        public static class LinqExtensions
        {
            static public IEnumerable<T> Descendants<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> DescendBy)
            {
                foreach (T value in source)
                {
                    yield return value;
        
                    foreach (T child in DescendBy(value).Descendants<T>(DescendBy))
                    {
                        yield return child;
                    }
                }
            }
        }
        

        最后,在我的 Group Class 中添加了以下方法:

        public partial class Group
        {
        
            public IEnumerable<Group> Descendants()
            {
                return LinqExtensions.Descendants(Children, c => c.Children);
            }
            public IEnumerable<Group> Genealogy()
            {
                Group[] ancestor = new Group[] { this };
                return ancestor.Concat(LinqExtensions.Descendants(Children, c => c.Children));
            }
        }
        

        如果我理解了你的问题,家谱法可能会对你有所帮助。

        【讨论】:

        • 很明显他在寻找父母,而不是孩子。
        • 你说得对,加布。随着 Ancestors 属性的添加,我的 Group 类变得可以双向导航。谢谢你
        【解决方案5】:

        虽然 Steven 的回答可能更好,但我使用的实现是对象上的 Ancestors 属性:

        partial class Kind
        {
            public IEnumerable<Kind> Ancestors
            {
                get
                {
                    for (var p = BaseKind; p != null; p = p.BaseKind)
                        yield return p;
                }
            }
        
            public IEnumerable<Kind> ThisAndAncestors
            {
                get
                {
                    for (var p = this; p != null; p = p.BaseKind)
                        yield return p;
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-07-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多