如果属性不存在,C#会将Dapper动态更改为抱怨

时间:2016-09-13 09:58:28

标签: c# dynamic dapper

我们正在使用Dapper从我们的SQL数据库中获取数据,该数据库将我们的数据作为动态'的集合返回。我理解动态类型的强大功能,以及" duck typing"的灵活性,基本上"如果它像鸭子那样嘎嘎叫,那么它就是一只鸭子 - 我不需要声明它它是一只鸭子"。

但是,我不明白为什么如果我试图从动态对象中获取不具备的属性,为什么它没有抱怨?例如,如果我有一些东西不是一只鸭子,我打电话给#34; Quack"在它上面,我认为期望它抱怨是合理的。编辑:看到评论,这似乎是Dapper给我的动态,因为如果属性不存在,标准动态对象会给出运行时错误。

有没有办法可以让它抱怨?

我所拥有的代码是从“动态”中获取属性的一系列行。并将它们分配给强类型对象中的相应属性。属性名称并不总是合并(由于遗留数据库命名标准)。目前,如果字段名称在动态上拼写错误,那么它将无声地失败。我想要它抱怨。我不想将每行代码重写为5行"动态" /&#34中是否存在[硬编码名称]属性;如果没有抱怨" /"获取价值并将其放在正确的位置"。

编辑:这是具体的代码,如果有帮助......不正确的字段名称是" result.DecisionLevel",我没有得到运行时错误,它只是将null分配给目标属性

        var results = _connection.Query("usp_sel_Solution", new { IdCase = caseId }, commandType: CommandType.StoredProcedure);
        return results.Select(result => new Solution
        {
            IsDeleted = result.IsDeleted,
            FriendlyName = result.FriendlyName,
            DecisionLevel = (DecisionLevel?)result.DecisionLevel,
        }).ToList();

解决方案:this接受的答案,结合谢尔盖的回答,让我得到了这个解决方案:

internal class SafeDynamic : DynamicObject
{
    private readonly IDictionary<string, object> _source;

    public SafeDynamic(dynamic source)
    {
        _source = source as IDictionary<string, object>;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (_source.TryGetValue(binder.Name, out result) == false)
        {
            throw new NotSupportedException(binder.Name);
        }

        return true;
    }

    // I'll refactor this later, probably to an extension method...
    public static IEnumerable<dynamic> Create(IEnumerable<dynamic> rows)
    {
        return rows.Select(x => new SafeDynamic(x));
    }
}

对示例代码的唯一更改是将调用包装到Dapper的Query方法中:

        var results = SafeDynamic.Create(_connection.Query("usp_sel_Solution", new { IdCase = caseId }, commandType: CommandType.StoredProcedure));

感谢。

对于子孙后代,我添加了我为how to do the same thing for Query<T>提供的解决方案的链接,并注意编辑25/1/17&#34;改进以避免静态线程问题字典&#34;,也适用于此处显示的解决方案。

2 个答案:

答案 0 :(得分:1)

根据正在构建的动态对象,整个预期行为可能会有所不同。

如果动态成员不是动态对象的一部分,则不要求抛出异常。

例如:

public class MyDynamic : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
           // I always set the result to null and I return true to tell
           // the runtime that I could get the value but I'm lying it!
           result = null;
           return true;
    }
}


dynamic myDynamic = new MyDynamic();
string text = myDynamic.text; // This won't throw a runtime exception!

可能并且例如Dapper尝试获取动态成员,如果没有找到成员则不会抱怨,这可能是设计的。

ExpandoObject是一个实现IDictionary<string, object>的动态对象,因此您可以使用ContainsKey有效地检查动态对象中是否存在成员:

dynamic expando = new ExpandoObject();
expando.text = "hello world";

if((IDictionary<string, object>)expando).ContainsKey("text")) 
{
    // True
}
顺便说一句,如果第三方库(甚至是第一方库)已经实现了一个动态对象,当您访问不存在的成员时,该对象不会受到影响,您将无法强制执行相反的操作。你需要忍受它,因为这是一个设计决定。

由于 duck typing 严重依赖于文档,如果您知道动态对象以这种方式工作,那么如果它接收到属性类型的默认值,您将知道属性未设置:

dynamic dyn = ...;

// x should be null once it's set and you'll know that
// dyn had no x member...
string x = dyn.x;

答案 1 :(得分:0)

您可以向您拥有的源对象添加包装并在其中实现所需的行为(抛出或不抛出异常,提供默认值或修复属性名称)。像这样:

public class WrapperDynamic:DynamicObject {     private dynamic _source;     public WrapperDynamic(动态源代码)     {         _source = source;     }

public override bool TryGetMember(GetMemberBinder binder, out object result)
{                        
    if (_source.CheckTheProperyExist(binder))
    {
        result = _source.GetProperty(binder);
        return true;
    }
    return false;
}

}

您应该根据源对象的类型实现CheckTheProperyExist和GetProperty。

他们应该为你添加封面选择

return results.Select(x=>new WrapperDynamic(x))
.Select(result => new Solution
        {
            IsDeleted = result.IsDeleted,
            FriendlyName = result.FriendlyName,
            DecisionLevel = (DecisionLevel?)result.DecisionLevel,
        }).ToList();

您可以在此包装器中为旧名称添加名称转换。