-->

How to select non nullables from nullable relation

2019-09-09 04:59发布

问题:

I am making a reporting tool that is based off Entity Framework and Scott Guthrie's Dynamic Linq library.

I have run into a snag when trying to select a non nullable field from a related table when the related record isnt always there. For example, I have a Participant table that has a nullable foreign key to a Team table. This is because some participants will be on a team and some wont. The snag is that I want to pull a report that shows a list of participants along with some of their team information IF they are on a team. One of the columns on the team table isn't nullable so when I try to select it using either a standard projection or a dynamic one:

    var standardProjection = data.Select(i => new
    {
        i.FirstName,
        i.ParticipantTeam.CaptainPickupFlg <--- Non Nullable Boolean
    });

    var dynamicProjection = data.Select("new (FirstName, ParticipantTeam.CaptainPickupFlg)");

I get an error when trying to enumerate the results:

"The cast to value type 'Boolean' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."

How can I avoid this error and just have the ParticipantTeam.CaptainPickupFlg just materialized as a nullable bool in the anonymous type?

ScottGu's Dynamic Linq: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

回答1:

data.Where( i => i.ParticipantTeam != null ).Select( i=> new
    {
        i.FirstName,
        i.ParticipantTeam.CaptainPickupFlg
    });

If you need all the participants, you have two options:

  • If you can afford the nullable field in returned anonymous type:

    var standardProjection = data.Select(i => new
    {
        i.FirstName,
        CaptainPickupFlg = (bool?)i.ParticipantTeam.CaptainPickupFlg
    });
    
  • If that's not an option for you, you should decide on the default value for that field:

    var standardProjection = data.Select(i => new
    {
        i.FirstName,
        CaptainPickupFlg = ((bool?)i.ParticipantTeam.CaptainPickupFlg) ?? false
    });
    

    that has the default value of false.