LINQ To Objects - 加入空列表

Posted

技术标签:

【中文标题】LINQ To Objects - 加入空列表【英文标题】:LINQ To Objects - Join on empty list 【发布时间】:2017-12-25 09:03:54 【问题描述】:

我有三个从不同数据源填充的单独列表,现在我想将它们加入到 LINQ 语句中以获得最终结果。但是,完全有可能其中一个列表可能是空的。如果列表为空,我似乎不能只编写一个 LINQ 语句来加入其中的任何一个。

我已经编写了以下测试。我得到的错误是“NullReferenceException:对象引用未设置为对象的实例。”

// these colloections emulate the table structures and data
List<Agent> agents = new List<Agent>();
agents.Add(new Agent  AgentId = 1, ClientId = 11 );
agents.Add(new Agent  AgentId = 1, ClientId = 12 );
agents.Add(new Agent  AgentId = 1, ClientId = 13 );
agents.Add(new Agent  AgentId = 1, ClientId = 14 );
agents.Add(new Agent  AgentId = 2, ClientId = 21 );
agents.Add(new Agent  AgentId = 2, ClientId = 22 );
agents.Add(new Agent  AgentId = 3, ClientId = 31 );

List<Client> clients = new List<Client>();
clients.Add(new Client  ClientId = 11, ClientName = "A Client 11", Status = "A" );
clients.Add(new Client  ClientId = 12, ClientName = "A Client 12", Status = "A" );
clients.Add(new Client  ClientId = 13, ClientName = "A Client 13", Status = "A" );
clients.Add(new Client  ClientId = 14, ClientName = "A Client 14", Status = "A" );
clients.Add(new Client  ClientId = 21, ClientName = "A Client 21", Status = "A" );
clients.Add(new Client  ClientId = 22, ClientName = "A Client 22", Status = "A" );
clients.Add(new Client  ClientId = 31, ClientName = "A Client 31", Status = "A" );

// Upon initilization, there are no records here.  Eventually, this "table" will be populated, 
// but only after the user has used the app for a while. If I populate this list, it works assuming
// the agent ID I'm looking for is in the collection.  But if it's not the join fails
List<ClientAdminFee> adminFees = new List<ClientAdminFee>();
//  adminFees.Add(new ClientAdminFee  AgentId = 1, ClientId = 11, AdminFee = 0.05m, EffectiveFrom = DateTime.Parse("2017-01-01") );
//  adminFees.Add(new ClientAdminFee  AgentId = 1, ClientId = 12, AdminFee = 0.05m, EffectiveFrom = DateTime.Parse("2017-01-01") );
//  adminFees.Add(new ClientAdminFee  AgentId = 1, ClientId = 13, AdminFee = 0.05m, EffectiveFrom = DateTime.Parse("2017-01-01") );
//  adminFees.Add(new ClientAdminFee  AgentId = 1, ClientId = 14, AdminFee = 0.05m, EffectiveFrom = DateTime.Parse("2017-01-01") );

var thisAgent = 1;
var theseAgents = agents.Where(x => x.AgentId == thisAgent).ToList();
var theseClients = clients.ToList();
var theseAdminFees = adminFees.Where(x => x.AgentId == thisAgent).ToList();

var final = (from ar in theseAgents
             join c in theseClients on ar.ClientId equals c.ClientId
             join caf in theseAdminFees on new  ar.AgentId, c.ClientId  equals new  caf.AgentId, caf.ClientId  into d
             from caf in d.DefaultIfEmpty()
             select new ClientWithAdminFee
             
                 AgentId = Convert.ToInt32(ar.AgentId),
                 ClientId = Convert.ToInt32(c.ClientId),
                 ClientName = c.ClientName,
                 Status = c.Status,
                 AdminFee = caf.AdminFee ?? 0.00m,
                 EffectiveDate = caf.EffectiveFrom ?? DateTime.Now
             ).ToList();

final.Dump();

正如我所说,如果我取消注释 adminFees 条目并搜索 AgentId 1,它就可以工作。但是,如果我搜索不在该集合中的代理,我根本无法进行加入。那么,如果有条目以及没有条目,我该如何编写它才能正常工作。

【问题讨论】:

查看左外连接。见 msdn:code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b 代码似乎用DefaultIfEmpty 明确要求空值 - 所以你最好处理它。旁注:你不能在 empty list 上获得 NRE,因为它本身不为 null... 我一直在做 LEFT OUTER JOINS,并且知道它是如何工作的。在这种情况下,它并没有真正帮助我。如果我在 LINQ 语句之前检查集合是否为空,那么我不能很好地编写适用于任何一种情况的 LINQ 语句。 如果您有 LINQPad,并将此代码复制到其中并运行它,您将看到问题。然后,如果您取消注释到 adminfees 集合中的条目,然后重新运行它,您会看到它有效。应用程序需要能够处理这两种情况,我不知道如何继续。同样,这不是 LEFT OUTER JOIN 的问题,因为我一直都在这样做。 【参考方案1】:

试试这个

var final = (from ar in theseAgents
                         join c in theseClients on ar.ClientId equals c.ClientId
                         join caf in theseAdminFees on new  ar.AgentId, c.ClientId  equals new  caf.AgentId, caf.ClientId  into d
                         from caf in d.DefaultIfEmpty()
                         select new ClientWithAdminFee
                         
                             AgentId = Convert.ToInt32(ar.AgentId),
                             ClientId = Convert.ToInt32(c.ClientId),
                             ClientName = c.ClientName,
                             Status = c.Status,
                             AdminFee = caf != null ? (caf.AdminFee) : 0,
                             EffectiveDate = caf != null ? (caf.EffectiveFrom == null ? DateTime.Now : caf.EffectiveFrom) : DateTime.Now,
                         ).ToList();

【讨论】:

谢谢,大部分工作。我不得不更改我的基础对象 (ClientWithAdminFee) 以包含 Nullable 属性,但它起作用了。再次感谢您,我知道这可能是我忽略了一些简单的事情。

以上是关于LINQ To Objects - 加入空列表的主要内容,如果未能解决你的问题,请参考以下文章

从LINQ开始之LINQ to Objects(上)

LINQ to SQL class LINQ to sql Objects?

从LINQ开始之LINQ to Objects(上)

是否可以为 linq-to-objects 编译查询

是否可以为 linq-to-objects 编译查询

Linq之旅:Linq入门详解(Linq to Objects)