【问题标题】:How to unpack an expression tree and check for null values如何解压表达式树并检查空值
【发布时间】:2014-04-09 10:22:44
【问题描述】:

参考下面的 2 个类,我经常写这样的 LINQ 语句..

using (var db = new DBContext())
{
    var result = db.Countries
        .Select(c => new
        {
            c.Name,
            c.Leader != null ? c.Leader.Title : String.Empty,
            c.Leader != null ? c.Leader.Firstname : String.Empty,
            c.Leader != null ? c.Leader.Lastname : String.Empty
        });
}

public class Country
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Leader Leader { get; set; }
}

public class Leader
{
    public string Title { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}

我的问题是我必须不断重复对子导航属性的 null 检查,我想知道是否有一种方法可以使用某种表达式树来动态提取属性值,同时检查 null 值以及它们是否不存在发回一个空字符串,类似于下面的方法..

public class Country
{
    // Properties //

    public string SafeGet(Expression<Func<Country, string>> fnc)
    {
        // Unpack fnc and check for null on each property?????
    }

}

用法:

using (var db = new DBContext())
{
    var result = db.Countries
        .Select(c => new
        {
            c.Name,
            c.SafeGet(l => l.Leader.Title),
            c.SafeGet(l => l.Leader.Firstname),
            c.SafeGet(l => l.Leader.Lastname)
        });
}

如果有人能提供一个很好的基本示例,因为除了创建表达式树之外,我对表达式树没有太多经验。

谢谢。

更新 -> 会像下面这样工作吗?

public string GetSafe(Expression<Func<Country, string>> fnc)
{
    var result = fnc.Compile().Invoke(this);
    return result ?? string.Empty;
}

【问题讨论】:

  • 坦率地说,这是可行的,但会带来太多麻烦而收效甚微(几乎可以肯定,您只需要支持特定的构造,否则您会做更多的工作而没有实际利益)。您建议的解决方案不起作用。
  • 看来您可以通过首先使用var defaultLeader = new Leader { Title = "", Firstname = "" } 创建一个具有空属性的领导者然后使用空合并(c.Leader ?? defaultLeader).Title 来实现更简洁的效果。额外奖励:也适用于所有其他属性类型。
  • @Jon - 我在想我可以将 GetSafe 方法放在一个基本泛型类中,以供许多其他类使用。此外,在我的示例中,我只是使用 Leader 属性但是在实践中可能有多达 5 或 6 个不同的属性。我想这意味着我必须为您提出的解决方案创建那么多 defaultProperties?
  • 是的,你需要很多。
  • 看看 NullSafe here。但是当你向下更深时它真的很有帮助,在这里它实际上不会为你节省很多打字时间。

标签: c# linq expression lambda


【解决方案1】:

我认为不需要表达。我会简单地选择一个扩展方法,比如

    public static class ModelExtensions
    {
         // special case for string, because default(string) != string.empty 
         public static string SafeGet<T>(this T obj, Func<T, string> selector)
         {
              try {
                  return selector(obj) ?? string.Empty;
              }
              catch(Exception){
                  return string.Empty;
              }
         }
   }

它适用于所有类,您可以进一步实现其他数据类型。用法和你一样。

【讨论】:

  • 不应该也包括捕获 NullReferenceExceptions 吗?
【解决方案2】:

我认为你想要这样的东西:

public static class ModelExtensions
{
    public static TResult SafeGet<TSource, TResult>(this TSource obj, System.Func<TSource, TResult> selector) where TResult : class
    {
        try
        {
            return selector(obj) ?? default(TResult);
        }
        catch(System.NullReferenceException e)
        {
            return default(TResult);
        }
    }
}

【讨论】:

  • 嗨@golergka,您的代码无法编译,我收到以下错误“Operator '??'不能应用于“TResult”和“TResult”类型的操作数"
  • 嗨@gorlenka,default(string) 不像@Grant 所期望的那样string.Empty
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-30
  • 2016-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多