我们正在使用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;,也适用于此处显示的解决方案。
答案 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();
您可以在此包装器中为旧名称添加名称转换。