当结果集有未映射的列时,如何使Dapper.NET抛出?

时间:2015-02-23 16:28:43

标签: dapper

使用下面的示例代码作为上下文...当我运行此查询时,我得到了' Id'字段返回默认值(对于int为0)。我想告诉dapper以一种方式运行,如果结果集中有一列没有映射到我的结果对象上的属性,它将抛出异常。 (我知道问题只是我需要删除SQL查询中的额外内容,但我有兴趣让它更明确地暴露自己)

我一直无法找到有关此主题的任何内容。如果Dapper甚至可以这样做,请告诉我。

提前致谢(除了这个问题,对于那些没有采取过任何措施的人来说,Dapper真的是切片面包以来最棒的东西!)。

class CustomerRecord
{
    public int Id { get; set; }
    public string Name { get; set; }
}

CustomerRecord[] GetCustomerRecords()
{
    CustomerRecord[] ret;
    var sql = @"SELECT 
                 CustomerRecordId AS Idd, 
                 CustomerName as Name
                 FROM CustomerRecord";

    using (var connection = new SqlConnection(this.connectionString))
    {
        ret = connection.Query<CustomerRecord>(sql).ToArray();
    }

    return ret;
}

3 个答案:

答案 0 :(得分:4)

您可以使用Dapper的DefaultTypeMap创建自己的类型映射,并在找不到该成员时抛出异常:

public class ThrowWhenNullTypeMap<T> : SqlMapper.ITypeMap
{
    private readonly SqlMapper.ITypeMap _defaultTypeMap = new DefaultTypeMap(typeof(T));

    public ConstructorInfo FindConstructor(string[] names, Type[] types)
    {
        return _defaultTypeMap.FindConstructor(names, types);
    }

    public ConstructorInfo FindExplicitConstructor()
    {
        return _defaultTypeMap.FindExplicitConstructor();
    }

    public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
    {
        return _defaultTypeMap.GetConstructorParameter(constructor, columnName);
    }

    public SqlMapper.IMemberMap GetMember(string columnName)
    {
        var member = _defaultTypeMap.GetMember(columnName);
        if (member == null)
        {
            throw new Exception();
        }
        return member;
    }
}

这样做的缺点是,您必须为每个实体配置所有类型映射:

SqlMapper.SetTypeMap(typeof(CustomerRecord), typeof(ThrowWhenNullTypeMap<CustomerRecord>));

然而,这可以使用反射进行配置。

答案 1 :(得分:3)

我是在I solved this same problem for the IEnumerable<dynamic> methods in Dapper之后来到这里的。然后我找到了提案solve the issue for Query<T>;但这似乎无处可去。

我的回答建立在@HenkMollema提出的答案的基础上,并在解决方案中使用他的课程,因此对他有所帮助......

解决IEnumerable&lt; dynamic&gt;方案,我创建了一个“SafeDynamic”类(按照上面的链接看看)。我将静态“Create”方法重构为扩展方法:

public static class EnumerableDynamicExtensions
{
    public static IEnumerable<dynamic> Safe(this IEnumerable<dynamic> rows)
    {
        return rows.Select(x => new SafeDynamic(x));
    }
}

然后我创建了一个DapperExtensions类来提供Query和Read的'Safe'版本(在QueryMultiple之后使用Read),给我...

internal static class DapperExtensions
{
    public static IEnumerable<dynamic> SafeQuery(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = default(int?), CommandType? commandType = default(CommandType?))
    {
        return cnn.Query(sql, param, transaction, buffered, commandTimeout, commandType).Safe();
    }

    public static IEnumerable<dynamic> SafeRead(this SqlMapper.GridReader gridReader, bool buffered = true)
    {
        return gridReader.Read(buffered).Safe();
    }
}

为了解决这个问题,我添加了一个“SafeQuery&lt; T&gt;” DapperExtensions的方法,负责为您设置类型映射:

    private static readonly IDictionary<Type, object> TypesThatHaveMapper = new Dictionary<Type, object>();

    public static IEnumerable<T> SafeQuery<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = default(int?), CommandType? commandType = default(CommandType?))
    {
        if (TypesThatHaveMapper.ContainsKey(typeof(T)) == false)
        {
            SqlMapper.SetTypeMap(typeof(T), new ThrowWhenNullTypeMap<T>());
            TypesThatHaveMapper.Add(typeof(T), null);
        }

        return cnn.Query<T>(sql, param, transaction, buffered, commandTimeout, commandType);
    }

因此,如果原始海报将对Query的调用更改为SafeQuery,则应该按照他的要求进行操作

编辑25/1/17 改进以避免静态字典上的线程问题:

    private static readonly ConcurrentDictionary<Type, object> TypesThatHaveMapper = new ConcurrentDictionary<Type, object>();


    public static IEnumerable<T> SafeQuery<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = default(int?), CommandType? commandType = default(CommandType?))
    {
        TypesThatHaveMapper.AddOrUpdate(typeof(T), AddValue, UpdateValue);
        return cnn.Query<T>(sql, param, transaction, buffered, commandTimeout, commandType);
    }

    private static object AddValue(Type type)
    {
        SqlMapper.SetTypeMap(type, XXX); // Apologies... XXX is left to the reader, as my implementation has moved on significantly.
        return null;
    }

    private static object UpdateValue(Type type, object existingValue)
    {
        return null;
    }

答案 2 :(得分:1)

我想通过提供一个包含他的&#34; SafeQuery&#34;的视觉工作室项目来扩展@Richardissimo的答案。延伸到Dapper,包裹得很好,整洁,经过测试。

https://github.com/LarrySmith-1437/SafeDapper

我现在在我的所有项目中使用它来帮助保持DAL清除错误映射的数据,并感觉需要共享。我本来会发布一个Nuget,但是对Dapper本身的依赖使得发布项目更加容易,消费者可以更新对他们想要的Dapper版本的引用。所有人都身体健康。