如何解包表达式树并检查空值

3
关于下面的两个类,我经常编写如下的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; }
}

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

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;
}

坦白地说,这是可行的,但为了微不足道的收益而付出太多麻烦(几乎可以肯定,您只需要支持特定的结构,否则您将为没有实际好处而做更多的工作)。您提出的解决方案行不通。 - Jon
1
似乎您可以通过首先创建一个空属性的领导者 var defaultLeader = new Leader { Title = "", Firstname = "" },然后使用空合并运算符 (c.Leader ?? defaultLeader).Title 来实现更简洁的内容。额外的奖励:对于所有其他属性类型也适用。 - Jon
@Jon - 我在考虑将GetSafe方法放在一个基本的泛型类中,以供其他多个类使用。此外,在我的示例中,我只是使用了Leader属性,但实际上可能会有多达5或6个不同的属性。我想这意味着我需要为您提出的解决方案创建那么多的defaultProperties? - Grant
是的,你需要很多。 - Jon
2
在这里看一下 NullSafe。但是当你深入到更深的层次时,它才真正有用,因为在这种情况下,它实际上并不能节省你太多的打字时间。 - voidengine
显示剩余4条评论
2个回答

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 吗? - Max Yankov

0
我认为你想要的是这样的:
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,你的代码无法编译,我得到了以下错误:“运算符'??'不能应用于类型为'TResult'和'TResult'的操作数”。 - Grant
嗨 @gorlenka,default(string)不是像@Grant期望的string.Empty - edi spring

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接