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