【问题标题】:LINQ to Objects Join two collections to set values in the first collectionLINQ to Objects 连接两个集合以在第一个集合中设置值
【发布时间】:2011-11-14 20:10:32
【问题描述】:

我有以下实体框架查询:

var results = from r in db.Results
              select r;

我正在使用 AutoMapper 映射到另一种类型:

var mapped = Mapper.Map<IEnumerable<Database.Result>, IEnumerable<Objects.Result>>(results);

在我的 Objects.Result 类型中,我有一个名为 reason 的属性,它不是来自数据库。它来自另一个来源,我基本上需要将其填充回我的映射类型:

var reasons = new List<Reason>
{
    new Reason { Id = 1, Reason = "asdf..." }
};

我需要将原因与我的映射集合相结合,并使用我的原因集合中的值在我的映射集合中设置 Reason 属性。这可能吗?

 // need something like this:
 mapped = from m in mapped
          join r in reasons on m.Id equals r.Id
          update m.Reason = r.Reason
          select m;

显然上面的代码不能编译,但是有没有我可以写的代码来做我想要的?

【问题讨论】:

    标签: c# .net linq-to-objects


    【解决方案1】:

    在循环中进行突变。理想情况下,Linq 应该不会对其操作的集合进行突变。使用 Linq 过滤、排序、投影您的数据,使用传统技术进行修改。

    var joinedData = from m in mapped 
                     join r in reasons on m.Id equals r.Id 
                     select new { m, r };
    
    foreach (var item in joinedData)
    {
        item.m.Reason = item.r.Reason;
    }
    

    【讨论】:

    • 谢谢。我猜在性能方面这不是一个巨大的打击,因为我在映射之前对数据进行了分页,所以第二次迭代不会有问题。
    • 如果您测量并发现这是一个性能瓶颈,请返回并解决它,如果我们必须,我们可以进行突变。但是,在我知道这是让我慢下来的原因之前,我不会这样做。
    • 我一次翻页大约 10 个项目。我非常怀疑它会导致任何性能问题:)
    • 只是想我会指出这一点,因为我很清楚集合中各个对象的引用完整性保持不变。换句话说,在通过加入然后进入循环之后,代码仍然知道您正在使用哪些对象。当您更新值时,其原始集合中的原始对象将被更新! Linq 太棒了!
    【解决方案2】:

    这可能会节省您的大量时间。 下面的代码用于连接两个集合并设置第一个集合的属性值。

    class SourceType
    {
        public int Id;
        public string Name;
        public int Age { get; set; }
        // other properties
    }
    
    class DestinationType
    {
        public int Id;
        public string Name;
        public int Age { get; set; }
        // other properties
    }
        List<SourceType> sourceList = new List<SourceType>();
        sourceList.Add(new SourceType { Id = 1, Name = "1111", Age = 35});
        sourceList.Add(new SourceType { Id = 2, Name = "2222", Age = 26});
        sourceList.Add(new SourceType { Id = 3, Name = "3333", Age = 43});
        sourceList.Add(new SourceType { Id = 5, Name = "5555", Age = 37});
    
        List<DestinationType> destinationList = new List<DestinationType>();
        destinationList.Add(new DestinationType { Id = 1, Name = null });
        destinationList.Add(new DestinationType { Id = 2, Name = null });
        destinationList.Add(new DestinationType { Id = 3, Name = null });
        destinationList.Add(new DestinationType { Id = 4, Name = null });
    
    
        var mapped= destinationList.Join(sourceList, d => d.Id, s => s.Id, (d, s) =>
        {
            d.Name = s.Name;
            d.Age = s.Age;
            return d;
        }).ToList();
    

    【讨论】:

    • 你应该解释一下这是做什么的。
    • 正是我需要的......它正在做的是在连接数据上构建一个语句 lambda,其中语句正在修改并从连接返回目标对象。注意:这仅适用于 LINQ to Objects 而不是 LINQ to SQL,因为后者需要一个表达式树类型 (Expresson) 而前者需要一个委托类型 (Func) .我需要使用基于 POCO 的 DTO 加入 EF DbSet,并且能够通过在加入前添加“.AsEnumerable”来强制 IQueryable(LINQ to SQL)为 IEnumerable(LINQ to Objects)
    • 还有一件事...@anthony-pegram 在他的回答中指出,理想情况下,LINQ 集合应该是不可变的,但我更喜欢这种更简洁的方法
    【解决方案3】:

    一种蛮力方法是:-

    foreach(var m in mapped)
    {
        m.Reason = reasons.Single(r=> r.Id == m.Id).Reason;
    }
    

    事实上,这是非常接近你的伪代码的实现。

    【讨论】:

      【解决方案4】:

      不应使用 Linq 来改变对象。话虽如此,从性能的角度来看,我也不喜欢“foreach”解决方案所需的额外循环。

      所以,这是我的解决方案:

      Func<ObjectType, AnotherType, ObjectType> Fill = delegate (ObjectType x, AnotherType a)
      {
          x.SomeProperty = a;
          x.Date = DateTime.Now;
          return x;
      };
      
      var result = from source in sources
                   join anotherSource in otherSource on source.Id equals anotherSource.Id
                   select Fill(source, anotherSource);
      

      虽然不是纯linq,但我觉得很明显会有副作用 并且没有额外的循环,也没有额外的不必要的新对象实例化。

      【讨论】:

      • 对委托的调用是否不会导致 foreach,因为委托 Invoke 无论如何都是幕后的集合?为什么不正常制作该方法?我知道你会失去灵活性,但至少你避免调用 1 个对象的 foreach :-\
      • 如果你想深入了解我的推理,这个帖子可能会说明我为什么要问你这个问题。 stackoverflow.com/questions/16220151/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-18
      相关资源
      最近更新 更多