2009-04-02 13 views
13

Tengo dos obeots, A & B para esta discusión. Puedo unir estos objetos (tablas) a través de una relación común o clave externa. Estoy utilizando linq para hacer esto unirse y solo quiero devolver ObjectA en mi conjunto de resultados; sin embargo, me gustaría actualizar una propiedad de ObejctA con datos de ObjectB durante la unión para que los ObjectAs que obtengo de mi consulta LINQ sean "ligeramente" diferentes de su estado original en el medio de almacenamiento de elección.LINQ In Line Property Update During Join

Aquí es mi consulta, se puede ver que me gustaría simplemente ser capaz de hacer algo como objectA.SomeProperty = objectB.AValueIWantBadly

Sé que podría hacer una nueva en mi selecto y girar nuevos OBjectAs , pero me gustaría evitar eso si es posible y simplemente actualizar un campo.

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; 

Respuesta

22

Añadir un método de actualización de su claseA

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

a continuación, utilizar

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

EDITAR:

o utilizar una función lambda locales como:

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

esto no funciona para linq-to-sql donde tengo una propiedad que tiene el atributo '[NotMapped]' – th1rdey3

0

¿Puedes probar la declaración de let? (no en mi máquina de desarrollo para probar esto yo mismo):

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

no, no se puede hacer eso; solo crea nuevas variables; no hace la asignación. –

+0

Intenté esto y el comentario anterior es correcto ... no ir. – JPrescottSanders

2

De la palabra "tablas", parece que está obteniendo estos datos de una base de datos. En ese caso; no: no puedes hacer esto. Lo más cerca que se puede hacer sería para seleccionar los objetos y las columnas adicionales, y actualizar las propiedades después:

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 
} 

Si usted hacía LINQ a objetos, tal vez hay algunas maneras hacky que podría hacerlo con fluidez APIs - no es bonita, sin embargo. (Edit - como this other reply)

+0

Estoy sacando de una base de datos, pero no quiero que estos cambios se publiquen en la base de datos, solo permite que este "nuevo" objeto A lleve consigo datos diferentes. – JPrescottSanders

1

Primero extienda Linq para tener una opción Cada creando una clase llamada LinqExtensions.

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

Luego puede usar Unirse para devolver una lista de objetos nuevos que contienen los objetos originales con su valor apropiado. The Each repetirá sobre ellos permitiéndole asignar o pasar los valores como parámetros a cada objeto.

ejemplo Asignación:

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

Parámetro que pasa ejemplo:

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

Lo bueno de esto es que Objecta y ObjectB no tienen que ser del mismo tipo. He hecho esto con una lista de objetos unidos a un Diccionario (como una búsqueda). Lo malo es que no está claro qué está pasando. Sería mejor omitir cada extensión y escribirla así.

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

Sin embargo, para mayor claridad, Probablemente ello:

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

Tendrá que devolver todo el Objecta en su nuevo objeto, porque va a ser de sólo lectura y la única razón por la que esto funciona es porque se hace referencia a los objetos de una colección, lo que le permite realizar cambios en las propiedades de los objetos.

Pero al final sería más claro omitir el LINQ join all together y simplemente recorrer las colecciones y buscar coincidencias, esto ayudará con el mantenimiento futuro. LINQ es increíble, pero al igual que cuando tienes un martillo no hace que todo sea un clavo, cuando tienes una colección no significa que LINQ es la respuesta.

1

Estoy haciendo un left join aquí, así que todavía tengo todos los datos de objectA incluso si la propiedad correspondiente en objectB es nula. Entonces, si la propiedad correspondiente en el objeto B es nula, entonces debe definir qué hacer en el objeto A. Uso esta declaración todo el tiempo para unir dos conjuntos de datos. No necesita enumerar exhaustivamente todas las propiedades en objectA y cómo se mapean, solo necesita listar los valores que desea actualizar con objectB. Los valores preexistentes en el objeto A son seguros a menos que se defina una asignación al objeto B.

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

puede probar siguiendo ..

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