Why DapperRow.GetType() return null?

心已入冬 提交于 2021-02-07 09:10:39

问题


As far as I knew, Object.GetType() should never return null. (related discussion)

Dapper .Query() return private class DapperRow instances to be treated as dynamic objects. I found a strange thing: DapperRow's .GetType() return null.

Here's the sample code to reproduce the problem. Create a C# project, reference Dapper and open a connection to SQL Server (or other database), use .Query() to execute simple select query and retrieve the first row of result. Use GetType() to get the type of result object, the return value is null.

using (SqlConnection cn = new SqlConnection(csSql))
{
    var rec = cn.Query("select getdate() as D").Single();
    var t = rec.GetType(); // t == null
    Console.WriteLine(t.Name); // null reference exception
}

I suspect that dynamic or private type is the cause of null, so I write my class library for test:

namespace Lib
{
  public class Blah
  {
    public static dynamic SecretObject;
    static Blah()
    {
        SecretObject = new PrivateType();
    }
  }
  class PrivateType
  {
  }
} 

In another project, get the dynamic type static field and call GetType():

    dynamic obj = Lib.Blah.SecretObject;
    Console.WriteLine(obj.GetType().Name); // "Lib.PrivateType"

According to the test result, even cast private type as dynamic, I still can get the private type information from GetType(), why DapperRow.GetType() return null?


回答1:


DapperRow is specifically built and utilized within Dapper to provide highly optimized row returns without reiterating header information. This is to help condense the size of the object and reduce redundant data, making it more efficient.

However, it would appear that the StackExchange team took the meta programming even further than a first glance would indicate.

DapperRow implements the System.Dynamic.IDynamicMetaObjectProvide interface, which requires that the GetMetaObject method be implemented:

System.Dynamic.DynamicMetaObject System.Dynamic.IDynamicMetaObjectProvider.GetMetaObject(
    System.Linq.Expressions.Expression parameter)
{
    return new DapperRowMetaObject(parameter, 
        System.Dynamic.BindingRestrictions.Empty, this);
}

DapperRowMetaObject is a custom implementation of DynamicMetaObject that essentially hijacks and overrides what methods can be invoked against the dynamic type and what those calls should translate to. In this case, calls to anything other than the DapperRow's IDictionary.Item getter or the DapperRow.SetValue will fail since they are always routed to those two calls, but the value will be defaulted to null for any "get" calls where the target property does not exist in the table.

public bool TryGetValue(string name, out object value)
{
    var index = table.IndexOfName(name);
    if (index < 0)
    { // doesn't exist
        value = null;
        return false;
    }
    ...
}

At that point, any methods invoked on a null dynamic value will throw a RuntimeBinderException:

RuntimeBinderException: Cannot perform runtime binding on a null reference

You can easily test this hypothesis by replacing GetType() with another call that will throw the exact same exception:

var rec = cn.Query("select getdate() as D").Single();
var t = rec.AsEnumerable();
Console.WriteLine(t.ToList());

Keep in mind, the underlying type information of any properties on the dynamic object itself can still be accessed directly:

var rec = cn.Query("select getdate() as D").Single();
var t = rec.D.GetType();
Console.WriteLine(t.Name);


来源:https://stackoverflow.com/questions/42966787/why-dapperrow-gettype-return-null

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!