LINQ В обновлении свойств линии во время соединения

У меня есть два предмета, A и B для этого обсуждения. Я могу присоединиться к этим объектам (таблицам) через общую связь или внешний ключ. Я использую linq для этого объединения, и я хочу только вернуть ObjectA в свой результирующий набор; однако я хотел бы обновить свойство ObejctA данными из ObjectB во время соединения, так что ObjectAs, которые я получаю из моего запроса LINQ, немного отличаются от их исходного состояния на выбранном носителе.

Вот мой запрос, вы можете видеть, что я просто хотел бы сделать что-то вроде 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;

Ответы

Ответ 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);

ИЗМЕНИТЬ

Или используйте локальную лямбда-функцию, например:

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

Ответ 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, возможно, есть некоторые хакерские способы, которыми вы могли бы это сделать с помощью бесплатных API-интерфейсов, но не очень. (edit - like этот другой ответ)

Ответ 3

Сначала расширьте Linq, чтобы иметь параметр "Каждый", создав класс LinqExtensions.

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

Затем вы можете использовать Join, чтобы вернуть список новых объектов, содержащих исходные объекты, с соответствующим значением. Каждый из них будет перебирать их, позволяя вам назначать или передавать значения в качестве параметров для каждого объекта.

Пример присваивания:

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 не должны быть одного типа. Я сделал это со списком объектов, соединенных с Словарем (например, с поиском). Плохо, что не ясно, что происходит. Лучше пропустить каждое расширение и написать его вот так.

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 является ответом.

Ответ 4

Я делаю левое соединение здесь, поэтому у меня все еще есть данные из объекта A, даже если соответствующее свойство в объекте B равно null. Поэтому, если соответствующее свойство в объекте B равно нулю, вы должны определить, что делать в объекте A. Я все время использую этот оператор для объединения двух наборов данных. Вам не нужно исчерпывающе перечислять все свойства объекта A и как они отображаются, вам нужно всего лишь указать значения, которые вы хотите обновить с помощью объектаB. Предварительно существующие значения в объекте A безопасны, если не определено сопоставление с объектом 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;
    }))()

Ответ 5

Можно ли попробовать оператор let? (не на моей машине dev, чтобы проверить это сам):

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

Ответ 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;
        });