【问题标题】:LINQ In Line Property Update During Join连接期间的 LINQ 内联属性更新
【发布时间】:2010-10-17 02:39:24
【问题描述】:

我有两个对象,A 和 B 用于此讨论。我可以通过公共关系或外键加入这些对象(表)。我正在使用 linq 来执行此连接,我只想在结果集中返回 ObjectA;但是,我想在连接期间使用来自 ObjectB 的数据更新 ObejctA 的属性,以便我从 LINQ 查询中得到的 ObjectAs 与其在所选存储介质中的原始状态“略有”不同。

这是我的查询,您可以看到我只是希望能够执行 objectA.SomeProperty = objectB.AValueIWantBadly 之类的操作

我知道我可以在我的选择中做一个新的并启动新的 OBjectAs,但我想尽可能避免这种情况并简单地更新一个字段。

return from objectA in GetObjectAs()
   join objectB in GetObjectBs()
           on objectA.Id equals objectB.AId
           // update object A with object B data before selecting it
   select objectA;

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    为 ClassA 添加更新方法

    class ClassA {
      public ClassA UpdateWithB(ClassB objectB) {
        // Do the update
        return this;
      }
    }
    

    然后使用

    return from objectA in GetObjectAs()
       join objectB in GetObjectBs()
               on objectA.Id equals objectB.AId
               // update object A with object B data before selecting it
       select objectA.UpdateWithB(objectB);
    

    编辑

    或者使用本地 lambda 函数,例如:

    Func<ClassA, ClassB, ClassA> f = ((a,b)=> { a.DoSomethingWithB(b); return a;});
    return from objectA in GetObjectAs()
           join objectB in GetObjectBs()
           on objectA.Id equals objectB.AId
           select f(objectA , objectA );
    

    【讨论】:

    • 这不适用于 linq-to-sql 我有一个具有[NotMapped] 属性的属性
    【解决方案2】:

    从“表”这个词,听起来您是从数据库中获取这些数据。在这种情况下;不:你不能这样做。您可以做的最接近的方法是选择对象和额外的列,然后更新属性:

    var qry = from objectA in GetObjectAs()
              join objectB in GetObjectBs()
                 on objectA.Id equals objectB.AId
              select new { A = objectA,
                  objectB.SomeProp, objectB.SomeOtherProp };
    
    foreach(var item in qry) {
        item.A.SomeProp = item.SomeProp;
        item.A.SomeOtherProp = item.SomeOtherProp;
        // perhaps "yield return item.A;" here
    }
    

    如果您正在使用 LINQ-to-Objects,也许有一些您可以使用 fluent API 的 hacky 方法 - 不过,这并不漂亮。 (编辑——比如this other reply

    【讨论】:

    • 我正在从数据库中提取,但我不希望将这些更改发布回数据库,它只是允许这个“新”对象 A 携带一些不同的数据。跨度>
    【解决方案3】:

    我在这里进行左连接,因此即使 objectB 中的相应属性为空,我仍然拥有来自 objectA 的所有数据。因此,如果 objectB 中的相应属性为 null,那么您必须定义 objectA 中的操作。我一直使用这个语句来连接两组数据。您不需要详尽列出 objectA 中的所有属性以及它们如何映射,您只需列出要使用 objectB 更新的值。除非定义了到 objectB 的映射,否则 objectA 中预先存在的值是安全的。

    return from objectA in GetObjectAs()
       join objectB in GetObjectBs()
       on objectA.Id equals objectB.AId into combinedObj
       from subObject in combinedObj.DefaultIfEmpty()
       // update object A with object B data before selecting it
       select ((Func<objectAType>)(() =>
        {
            objectA.property = ((subObject == null) ? "Object B was null" : subObject.property);
            objectA.property = ((subObject == null) ? "Object B was null" : subObject.property);
            return objectA;
        }))()
    

    【讨论】:

      【解决方案4】:

      首先通过创建一个名为 LinqExtensions 的类来扩展 Linq,使其具有 Each 选项。

        public static class LinqExtensions
        {
          public static void Each<T>(this IEnumerable<T> source, Action<T> method)
          {
            foreach (var item in source)
            {
              method(item);
            }
          }
        }
      

      然后您可以使用 Join 返回一个新对象列表,其中包含具有适当值的原始对象。 Each 将遍历它们,允许您将值作为参数分配或传递给每个对象。

      作业示例:

      objectA.Join(objectB,a=>a.Id,b=>b.Id,(a,b) => new {a,b.AValueIWant}).Each(o=>o.a.SomeProperty=o.AValueIWant);
      

      参数传递示例:

      objectA.Join(objectB,a=>a.Id,b=>b.Id,(a,b) => new {a,b.AValueIWant}).Each(o=>o.a.SomeMethod(o.AValueIWant));
      

      这样做的好处是 ObjectA 和 ObjectB 不必是同一类型。我已经使用加入字典的对象列表(如查找)来完成此操作。坏事是不清楚发生了什么。你最好跳过 Each 扩展并这样写。

      foreach(var change in objectA.Join(objectB,a=>a.Id,b=>b.Id,(a,b) => new {a,b.AValueIWant}))
      {
        change.a.SomeProperty = change.AValueIWant;
        change.a.SomeMethod(change.AValueIWant);
      }
      

      但为了更清楚,我可能会这样做:

      foreach(var update in objectA.Join(objectB,objectA=>objectA.Id,objectB=>objectB.Id,(objectA,objectB) => new {objectA, Value = objectB.AValueIWant}))
      {
        update.objectA.SomeProperty = update.Value;
      }
      

      您将需要在新对象中返回整个 ObjectA,因为它将是只读的,并且这样做的唯一原因是因为引用了集合中的对象,从而允许您更改对象的属性。

      但最后最清楚的是跳过所有的 LINQ 连接,只循环遍历集合并查找匹配项,这将有助于未来的维护。 LINQ 很棒,但就像当你有一把锤子时,它不会把所有东西都变成钉子一样,当你有一个集合时,它并不意味着 LINQ 就是答案。

      【讨论】:

        【解决方案5】:

        你可以试试 let 语句吗? (不是在我的开发机器上自己测试):

        return from objectA in GetObjectAs()   
        join objectB in GetObjectBs()
        on objectA.Id equals objectB.AId
        let objectA.SomeProperty = objectB.AValueIWantBadly
        select objectA;
        

        【讨论】:

        • 不,你不能那样做; let 只创建新变量 - 它不进行赋值。
        【解决方案6】:

        您可以通过以下方式尝试..

                    var list1 = new List<ItemOne>
                                {
                                    new ItemOne {IDItem = 1, OneProperty = "1"},
                                    new ItemOne {IDItem = 2, OneProperty = null},
                                    new ItemOne {IDItem = 3, OneProperty = "3"},
                                    new ItemOne {IDItem = 4, OneProperty = "4"}
                                };
                var list2 = new List<ItemTwo>
                                {
                                    new ItemTwo {IDItem = 2, TwoProperty = "2"},
                                    new ItemTwo {IDItem = 3, TwoProperty = "3"},
                                };
        
        
                var query = list1.Join(list2, l1 => l1.IDItem, l2 => l2.IDItem, (l1, l2) =>
                {
                    l1.OneProperty = l2.TwoProperty;
                    return l1;
                });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-01-03
          • 2023-03-10
          • 1970-01-01
          • 2014-12-28
          • 2012-06-21
          • 1970-01-01
          • 2016-09-12
          • 1970-01-01
          相关资源
          最近更新 更多