LINQ In Line Property Update During Join

后端 未结 6 1418
难免孤独
难免孤独 2021-02-05 11:55

I have two obects, A & B for this discussion. I can join these objects (tables) via a common relationship or foreign key. I am using linq to do this join and I only want t

相关标签:
6条回答
  • 2021-02-05 12:01

    From the word "tables", it sounds like you are getting this data from a database. In which case; no: you can't do this. The closest you can do would to select the objects and the extra columns, and update the properties afterwards:

    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
    }
    

    If you were doing LINQ-to-Objects, there are perhaps some hacky ways you could do it with fluent APIs - not pretty, though. (edit - like this other reply)

    0 讨论(0)
  • 2021-02-05 12:06

    Add an update method to your ClassA

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

    then use

    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);
    

    EDIT:

    Or use a local lambda function like:

    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 );
    
    0 讨论(0)
  • 2021-02-05 12:10

    can u try the let statement? (not at my dev machine to test this out myself):

    return from objectA in GetObjectAs()   
    join objectB in GetObjectBs()
    on objectA.Id equals objectB.AId
    let objectA.SomeProperty = objectB.AValueIWantBadly
    select objectA;
    
    0 讨论(0)
  • 2021-02-05 12:22

    I am doing a left join here so I still have all the data from objectA even if the corresponding property in objectB is null. So if the corresponding property in objectB is null then you have to define what to do in objectA. I use this statement all the time for joining two sets of data. You do not need to exhaustively list all properties in objectA and how they map, you only need to list the values you want to update with objectB. Pre-existing values in objectA are safe unless a mapping to objectB is defined.

    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;
        }))()
    
    0 讨论(0)
  • 2021-02-05 12:23

    you can try by following..

                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;
            });
    
    0 讨论(0)
  • 2021-02-05 12:26

    First extend Linq to have an Each option by creating a class called LinqExtensions.

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

    Then you can use Join to return a list of new objects that contain the original objects with it's appropriate value. The Each will iterate over them allowing you to either assign or pass the values as parameters to each object.

    Assignment example:

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

    Parameter passing example:

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

    The nice thing about this is that ObjectA and ObjectB do not have to be the same type. I have done this with a list of objects joined to a Dictionary (like a lookup). Bad thing is it isn't clear what is going on. You would be better to skip the Each extention and write it like this.

    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);
    }
    

    But for more clarity I would probably do this:

    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;
    }
    

    You will need to return the whole ObjectA in your new object, because it will be readonly and the only reason this works is because the objects in a collection are referenced allowing you to make your changes to properties on the objects.

    But in the end it would be clearest to skip the LINQ join all together and just loop through the collections and look for matches, this will help with future maintenence. LINQ is awesome but just like when you have a hammer it doesn't make everything a nail, when you have a collection it doesn't mean LINQ is the answer.

    0 讨论(0)
提交回复
热议问题