C# 加入期间的 LINQ 内联属性更新
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/709560/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
LINQ In Line Property Update During Join
提问by JPrescottSanders
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.
我有两个对象,A 和 B 用于此讨论。我可以通过公共关系或外键连接这些对象(表)。我正在使用 linq 进行此连接,并且只想在结果集中返回 ObjectA;但是,我想在连接期间用来自 ObjectB 的数据更新 ObejctA 的属性,以便我从 LINQ 查询中得到的 ObjectAs 与它们在选择的存储介质中的原始状态“略有不同”。
Here is my query, you can see that I would just like to be able to do something like objectA.SomeProperty = objectB.AValueIWantBadly
这是我的查询,您可以看到我只是希望能够执行诸如 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.
我知道我可以在我的选择中做一个新的并启动新的 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;
采纳答案by MartinStettner
Add an update method to your ClassA
向 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:
或者使用本地 lambda 函数,例如:
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 );
回答by JPrescottSanders
can u try the let statement? (not at my dev machine to test this out myself):
你可以试试 let 语句吗?(不是在我的开发机器上自己测试这个):
return from objectA in GetObjectAs()
join objectB in GetObjectBs()
on objectA.Id equals objectB.AId
let objectA.SomeProperty = objectB.AValueIWantBadly
select objectA;
回答by Marc Gravell
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)
如果您正在使用 LINQ-to-Objects,那么您可能可以使用一些 hacky 的方式使用流畅的 API 来完成它——不过并不漂亮。(编辑 - 像这个其他回复)
回答by Larry Dukek
First extend Linq to have an Each option by creating a class called LinqExtensions.
首先通过创建一个名为 LinqExtensions 的类来扩展 Linq 以具有 Each 选项。
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.
然后,您可以使用 Join 返回包含具有适当值的原始对象的新对象列表。Each 将遍历它们,允许您将值作为参数分配或传递给每个对象。
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.
这样做的好处是 ObjectA 和 ObjectB 不必是相同的类型。我已经使用加入字典的对象列表(如查找)完成了此操作。坏事是不清楚发生了什么。你最好跳过 Each 扩展并像这样写。
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.
您将需要在新对象中返回整个 ObjectA,因为它将是只读的,并且这样做的唯一原因是因为引用了集合中的对象,允许您对对象的属性进行更改。
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.
但最终最明显的是跳过 LINQ 连接,只循环遍历集合并查找匹配项,这将有助于未来的维护。LINQ 很棒,但就像当你有一把锤子时,它不会把所有东西都变成钉子,当你有一个集合时,这并不意味着 LINQ 就是答案。
回答by user1170104
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.
我在这里进行左连接,因此即使 objectB 中的相应属性为空,我仍然拥有 objectA 中的所有数据。因此,如果 objectB 中的相应属性为 null,那么您必须定义在 objectA 中要做什么。我一直使用这个语句来连接两组数据。您无需详尽列出 objectA 中的所有属性及其映射方式,只需列出要使用 objectB 更新的值。除非定义了到 objectB 的映射,否则 objectA 中预先存在的值是安全的。
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;
}))()
回答by Towhidul Islam Tuhin
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;
});