【问题标题】:Extract objects from a nested object based on a property value and project to a new class根据属性值从嵌套对象中提取对象并投影到新类
【发布时间】:2018-10-31 14:55:09
【问题描述】:

我有以下嵌套输入类

 //InputClass
public class MessageViewModel 
{
    public bool IsSelected { get; set; }

    public string Subject { get; set; }

    public DateTime CreationDate { get; set; }
    public List<MessageViewModel> Messages { get; set; }
}

我想创建以下类型的新实例:

 //Target Class
public class DestinationClass
{
    public string Subject { get; set; }
}

对于每个具有 IsSelected = true 属性的 MessageViewModel 实例 在这种情况下,最好的解决方案是什么?这可以使用 AutoMapper 实现吗?

编辑:

假设我有以下对象:

var vm = new MessageViewModel
        {
            Messages = new List<MessageViewModel> {
                            new MessageViewModel {
                                IsSelected = true,
                                    Messages = new List<MessageViewModel> {
                                            new MessageViewModel { IsSelected = true,
                                                                    Messages = new List<MessageViewModel> { new MessageViewModel { },
                                                                                                            new MessageViewModel { },
                                                                        new MessageViewModel { } } } } } }
        };

我想遍历它并仅提取和映射 IsSelected 等于 true 的 MessageViewModel。

已解决:

使用以下代码:

 public static class TraversalHelper
{
    public static void TraverseAndExecute<T>(this T composite, Func<T, IEnumerable<T>> selectChildren, Action<T> action)
        where T : class
    {
        action.Invoke(composite);
        composite.TraverseAndExecute(selectChildren, action, new List<T> { composite });
    }

    private static void TraverseAndExecute<T>(this T composite, Func<T, IEnumerable<T>> selectChildren, Action<T> action, IList<T> invokedComponents)
        where T : class
    {
        invokedComponents = invokedComponents ?? new List<T>();
        var components = selectChildren(composite) ?? new T[] { };
        foreach (var component in components)
        {
            if (!invokedComponents.Contains(component))
            {
                action.Invoke(component);
                invokedComponents.Add(component);
                component.TraverseAndExecute(selectChildren, action, invokedComponents);
            }
            else
            {

            }
        }
    }
}

我设法迭代了我的分层对象:

 var vm = new MessageViewModel
        {
            Messages = new List<MessageViewModel> {
                            new MessageViewModel {
                                IsSelected = true,
                                    Messages = new List<MessageViewModel> {
                                            new MessageViewModel {
                                                IsSelected = true,
                                                Messages = new List<MessageViewModel> {
                                                    new MessageViewModel { },
                                                    new MessageViewModel { },
                                                    new MessageViewModel { } } } } } }
        };

        var results = new List<DestinationClass>();

        vm.TraverseAndExecute(_ => _.Messages, _ => {
            if(_.IsSelected == true)
            {
                results.Add(new DestinationClass { Subject = _.Subject });
            }
        });

【问题讨论】:

  • 出于可读性原因,您不应在 C# lambda 中使用下划线。它应该是 Message 的“m”或更好的“message”。您可以使用 Linq 语句对其进行重构,以将其转换为 DestinationClass 的 IEnumerable。此外,您可以将 if 语句简化为 if(_.IsSelected)

标签: c# linq mapping automapper


【解决方案1】:

由于您的类是嵌套层次结构,因此您需要一个扩展方法。

具有更多 LINQ 特征的一个是:

public static IEnumerable<T> Flatten<T>(this T current, Func<T, IEnumerable<T>> childrenFn) {
    var working = new Stack<T>();
    working.Push(current);

    while (working.Count > 0) {
        current = working.Pop();
        yield return current;

        if (childrenFn(current) != null)
            foreach (var child in childrenFn(current))
                working.Push(child);
    }
}

这需要一个父对象和一个函数,该函数从父对象返回子对象列表并返回所有展平的对象。

现在你可以用它来产生你的答案:

var ans = vm.Flatten(mvm => mvm.Messages)
            .Where(mvm => mvm.IsSelected)
            .Select(mvm => new DestinationClass() { Subject = mvm.Subject });

【讨论】:

  • 如果我有消息的多级层次结构,这会起作用吗?列表列表的列表... IsSelected 等于 true ?我认为这是我的问题。谢谢
  • 您的多级层次结构有多深?
  • 应该在一般情况下工作,n个级别,这将是最好的方法。
  • 您可以使用扩展方法 SelectMany() 来展平嵌套的集合。 Sould 返回您所有 MessageViewModel 的一个 IEnumerable,然后您可以使用 Select 将它们转换为您的 DestinationClass 的新 IEnumerable
  • stackoverflow.com/questions/6501096/… 这就是我想要的。非常感谢
【解决方案2】:

在这种情况下,我将使用 Message ViewModel 的 IEnumerable 作为 DestinationClass 中的构造函数参数。然后,您可以使用 System.Linq 遍历 MessageViewModels 集合,并选择条件为真的每个模型并将其转换为 DestinationClass 的 IEnumerable。

这只是解决此问题的一种方法。如果您可以向我们提供有关您的解决方案架构的更多详细信息,我们将更容易为您提供支持。问候 EnvyIT

【讨论】:

  • 我关心的是嵌套,我不知道如何遍历这个对象,因为它可以有很深的层次结构。请按照我的编辑:D
猜你喜欢
  • 2019-03-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 2013-10-11
  • 2021-05-21
  • 2022-11-06
  • 2015-02-18
  • 1970-01-01
相关资源
最近更新 更多