使用可为空的属性过滤嵌套列表

时间:2019-03-31 19:26:43

标签: c# linq

说我有以下类结构

public class EmailActivity {
    public IEnumerable<MemberActivity> Activity { get; set; }
    public string EmailAddress { get; set; }
}

public class MemberActivity {
    public EmailAction? Action { get; set; }
    public string Type { get; set; }
}

public enum EmailAction {
    None = 0,
    Open = 1,
    Click = 2,
    Bounce = 3
}

我希望基于EmailActivity的存在来过滤MemberActivity对象的列表,其中{null}与提供的EmailAction匹配项列表匹配的非空EmailAction。我只想将EmailAddress属性返回为List<string>

据我所知

List<EmailAction> activityTypes; // [ EmailAction.Open, EmailAction.Bounce ]

List<string> activityEmailAddresses =
    emailActivity.Where(
        member => member.Activity.Where(
            activity => activityTypes.Contains(activity.Action)
        )
    )
    .Select(member => member.EmailAddress)
    .ToList();

但是我收到一条错误消息“ CS1503参数1:无法从'EmailAction转换?”到“ EmailAction””

如果然后修改activityTypes以允许空值List<EmailAction?>,我将得到以下信息:“ CS1662无法将lambda表达式转换为预期的委托类型,因为该块中的某些返回类型不能隐式转换为委托返回类型”。

问题是嵌套的.Where返回列表,但是父级.Where需要布尔结果。我该如何解决这个问题?

我意识到我可以使用嵌套循环,但是我想提高我的C#技能!

2 个答案:

答案 0 :(得分:1)

就性能而言,使用List.Contains并不理想,HashSet是一个更好的选择,如果您希望在包含搜索到的操作之一时立即选择电子邮件地址,则可以使用Any

var activityTypes = new HashSet<EmailAction>() { EmailAction.Open, EmailAction.Bounce };

List<string> activityEmailAddresses =
    emailActivity.Where(
        member => member.Activity.Any(
            activity => activity.Action.HasValue && 
                        activityTypes.Contains(activity.Action.Value)
    )
)
.Select(activity => activity.EmailAddress)
.ToList();

答案 1 :(得分:0)

您想使用All还是Any取决于您是否希望每场比赛或至少一场比赛...

HashSet<EmailAction> activityTypes = new HashSet<EmailAction> { EmailAction.None };

var emailActivity = new List<EmailActivity>
{
    new EmailActivity { Activity = new List<MemberActivity>{ new MemberActivity { Action = EmailAction.None } }, EmailAddress = "a" },
    new EmailActivity { Activity = new List<MemberActivity>{ new MemberActivity { Action = EmailAction.Click } }, EmailAddress = "b" }
};

// Example with Any but All can be used as well
var activityEmailAddresses = emailActivity
    .Where(x => x.Activity.Any(_ => _.Action.HasValue && activityTypes.Contains(_.Action.Value)))
    .Select(x => x.EmailAddress)
    .ToArray();
// Result is [ "a" ]