tags:

views:

461

answers:

4

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 to return ObjectA in my result set; however, I would like to update a property of ObejctA with data from ObjectB during the join so that the ObjectAs I get out of my LINQ query are "slightly" different from their original state in the storage medium of choice.

Here is my query, you can see that I would just like to be able to do something like objectA.SomeProperty = objectB.AValueIWantBadly

I know I could do a new in my select and spin up new OBjectAs, but I would like to avoid that if possible and simply update a field.

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;
A: 

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;
no, you can't do that; let only creates new variables - it doesn't do assignment.
Marc Gravell
I tried this an the above comment is correct... no go.
JPrescottSanders
+1  A: 

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)

Marc Gravell
I am pulling from a DB, but I don't want these changes to be posted back to the DB, it's just allow this "new" object A to carry with it some different data.
JPrescottSanders
+1  A: 

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 );
MartinStettner
A: 

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.

TwistedStem