Linq OrderBy依赖于其他实体

时间:2017-01-01 22:18:19

标签: c# linq reflection

我有一些实体集合,我需要根据它们之间的依赖关系进行排序。这是一个例子,因为它很难解释:

public class A : I {
    private B objB;
    public B propB { get{ return objB; } }
    // Some other fields and properties.
}

public class B : I { /* Some fields and properties. */ }

public class C : I {
    private A objA;
    public A propA { get{ return objA; } }
    // Some other fields and properties.
}

public interface I {}

问题是我需要将数据导入到这些类型的集合中,但我需要按特定顺序导入,因为如果我先导入A个对象,我将无法链接相应的{{1因为它还不存在。

所以我想要的是以一种方式对我的集合进行排序,以正确的顺序导入所有依赖项(没有循环依赖关系)。我无法想出一个能做到这一点的linq声明。

B

我在考虑可能会在lists.OrderBy(l => l. ??? ); 中获取typesList的所有类型T的列表List<T>,并以某种方式使用反射计算{{1}中的字段数在} lists中,但似乎......效率不高?

编辑:意识到我的结构的措辞有点模糊 基本上TtypesList。这是一个结果示例:

lists

1 个答案:

答案 0 :(得分:1)

我知道这样做的最简单方法是构建一个新的集合,当你构建它时,看看你是否在类中看到任何已知的类型。如果你确实看到一个已知的类型,就在第一次发现之前将其置于最后,或者如果没有找到已知的类型则将它放在最后。一旦你有了这个集合,就会以相反的顺序枚举集合,并且它将以反向依赖顺序排列。

以下示例与

类似
var sortedLists = lists.OrderByTypeDependency();

如何实现LINQ样式扩展方法:

static class ExtensionMethods
{
    public static IEnumerable<T> OrderByTypeDependency<T>(this IEnumerable<T> items)
    {
        LinkedList<T> knownItems = new LinkedList<T>();

        foreach (var item in items)
        {
            var itemType = item.GetType();
            var itemPropertyTypes =
                itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .Select(x => x.PropertyType);
            var itemFieldTypes =
                itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .Select(x => x.FieldType);

            //Create a set of all types internal to type we are checking on.
            HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));

            bool found = false;
            for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
            {
                var knownItemType = knownItemNode.Value.GetType();
                if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
                {
                    knownItems.AddBefore(knownItemNode, item);
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                knownItems.AddLast(item);
            }
        }

        //output the result in reverse order.
        for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
        {
            yield return knownItemNode.Value;
        }
    }
}

编辑:目前尚不清楚你是否传递了类型列表或对象列表。如果您传递的是类型列表,则只需对代码进行一些小调整,只需删除两个.GetType()来电,然后从泛型转为仅接受IEnumerable<Type>

static class ExtensionMethods
{
    public static IEnumerable<Type> OrderByTypeDependency(this IEnumerable<Type> items)
    {
        LinkedList<Type> knownItems = new LinkedList<Type>();

        foreach (var item in items)
        {
            var itemType = item;
            var itemPropertyTypes =
                itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .Select(x => x.PropertyType);
            var itemFieldTypes =
                itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .Select(x => x.FieldType);

            //Create a set of all types internal to type we are checking on.
            HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));

            bool found = false;
            for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
            {
                var knownItemType = knownItemNode.Value;
                if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
                {
                    knownItems.AddBefore(knownItemNode, item);
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                knownItems.AddLast(item);
            }
        }
        for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
        {
            yield return knownItemNode.Value;
        }
    }
}

<强>更新

根据你对问题的更新,只要内部列表在列表中保存相同类型的对象,这样我们就可以检查列表中的第一项以找到它的类型,原始代码的这个修改将进行排序你需要。

static class ExtensionMethods
{
    public static IEnumerable<T> OrderByTypeDependency<T>(this IEnumerable<T> outerList)
        where T: IList
    {
        LinkedList<T> knownItems = new LinkedList<T>();

        foreach (var innerList in outerList)
        {
            if(innerList.Count == 0)
                continue;
            var itemType = innerList[0].GetType();
            var itemPropertyTypes = itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .Select(x => x.PropertyType);
            var itemFieldTypes = itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
                    .Select(x => x.FieldType);

            //Create a set of all types internal to type we are checking on.
            HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));

            bool found = false;
            for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
            {
                var knownItemType = knownItemNode.Value[0].GetType();
                if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
                {
                    knownItems.AddBefore(knownItemNode, innerList);
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                knownItems.AddLast(innerList);
            }
        }
        for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
        {
            yield return knownItemNode.Value;
        }
    }
}