问题描述
在我的程序中,我加入了两个IEnumerables并将它们转换为字典,如下所示:
//list1 is a list of all contracts,list2 is a list of special contracts
var list1 = //List of objects
Var list2 = //List of objects
var combined = List1.Join(list2,c=> s.contractNumber,dto => dto.contract,(s,dto) => dto)
.ToDictonary( x => x.Contract,y => y.Date);
var final = list1.Select(contractNumber =>{
var number = contract.ContractNumber;
contract.Date = combined.ContainsKey(number) ? combined[number] : string.Empty;
}
简短版本是这样的:我们使用list1填充了要在前端显示的表,但是公司要求进行更改,这些更改需要显示来自单独API调用的信息。 List1没有包含日期字段,而list2有。因此,我们将属性添加到list1,然后使用字典根据键值对填充该属性。
他们又提出了在前端显示list2中另一个字段的请求,所以我想使用这样的元组:
.ToDictionary(x=>x.Contract,y => Tuple.Create(y.Date,y.Type));
contract.Date = combined.ContainsKey(number) ? combined[number] : string.Empty; //It breaks due to converting tuple<string,string> to string
解决方法
在我看来,list1是一系列相似项的序列,我们称它们为Contracts
。每个Contract
都有一个属性ContractNumber
。
List2也是一系列相似项。不知道其中有什么,但是我们称它们为ContractDates
。每个ContractDate
都有属性Contract
和一个Date
。
哦,我几乎忘记了:ContractDate.Contract
是ContractDate所属合同的ContractNumber。因此,如果ContractDate.Contract等于14,则此ContractDate属于编号为14的合同。
最后:关系是一对零或一:每个合同都有零个或一个ContractDate,每个ContractDate恰好属于一个合同,即外键合同引用的合同。
所以可能有些合同没有ContractDate
似乎您想要所有合同(的几个属性),每个合同都带有其ContractDate
的日期。如果合同没有ContractDate,那么您想为Date使用默认值(在您的情况下为String.Empty)
您是对的,每当您有一个表项时,其中每个表项都包含来自另一个表的零个或多个项,并且您想要合并这两个表,就可以使用某种联接。
如果要使用每个项目以及一个和另一个项目,请使用加入。如果您希望每个项目都包含零个或多个其他项目,请使用 GroupJoin 。
在以下情况下,请使用GroupJoin:
- 零个或多个学生的学校
- 零个或多个订单的客户
- 使用零个或多个ContractDate(在您的情况下:零个或一个)进行合同
如果需要,请使用Join:
- 参加一所唯一学校的学生
- 与客户下订单的订单
- 与外键引用的合同的合同日期。
您遇到了问题,因为方向错误:您应该使用one of the overloads of GroupJoin而不是Join。
对于您而言,最好将重载与参数resultSelector一起使用:对于每个具有零个或多个ContractDates(在您的情况下为零或一个)的Contract,创建一个新对象:
IEnumerable<Contract> contracts = ...
IEnumerable<ContractDate> contractDates = ...
var contractsWithTDates = contracts.GroupJoin(contractDates,contract => contract.ContractNumber,// from every contract take the number
contractDate => contractDate.Contract,// from every contractDate take the foreign key
// parameter resultSelector: for every Contract,with its zero or more ContractDates
// make one new object:
(contract,contractDatesOfThisContract) => new ...);
现在您想要什么作为结果对象:
-
我想要合同的几个参数
-
我想要一个唯一的ContractDate的日期,如果没有ContractDate,则想要String.Empty
const字符串defaultContractDate = String.Empty; var contractWithTDates = contract.GroupJoin(contractDates, ...
(contract,contractDatesOfThisContract) => new { // Select the zero or more properties of the contract that you want: ContractNumber = contract.ContractNumber,CustomerId = contract.CustomerId,... // Select the properties of the contract dates of this contract that you want // if there is one: use the date // if there is none: use the defaultContractDate // if there is more than one,which you don't expect,use the first contractDate Date = contractDatesOfThisContract .Select(contractDate => contractDate.Date) .FirstOrDefault() ?? defaultContractDate,});
换句话说:对于contractDatesOfThisContract中的每个Date(您希望其中的最大日期),选择属性Date。从产生的日期序列中,选择第一个,或者如果没有第一个,则返回“默认”,在这种情况下为空。
操作员?? (null-coalescing运算符):如果FirstOrDefault的结果为非null,则使用此值;如果为null,则使用defaultContractDate。