Dapper MultiMap在使用splitOn时无法处理NULL值

24

我在使用dapper的MultiMaps时遇到了问题,尝试对包含NULL的列进行拆分。Dapper似乎没有实例化对象,我的映射函数收到的是null而不是对象。

这是我的新测试:

    class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public Category Category { get; set; }
    }
    class Category
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }
    public void TestMultiMapWithSplitWithNullValue()
    {
        var sql = @"select 1 as id, 'abc' as name, NULL as description, 'def' as name";
        var product = connection.Query<Product, Category, Product>(sql, (prod, cat) =>
        {
            prod.Category = cat;
            return prod;
        }, splitOn: "description").First();
        // assertions
        product.Id.IsEqualTo(1);
        product.Name.IsEqualTo("abc");
        product.Category.IsNotNull();
        product.Category.Id.IsEqualTo(0);
        product.Category.Name.IsEqualTo("def");
        product.Category.Description.IsNull();
    }

由于传递给映射函数的catnull,导致失败的行是product.Category.IsNotNull();

我还向Assert类添加了此方法:

public static void IsNotNull(this object obj)
{
    if (obj == null)
    {
        throw new ApplicationException("Expected not null");
    }
}

我很乐意提供帮助,在推特上联系了为Windows编写GitHub的保罗。我知道他们正在努力解决这个行尾问题。 - Sam Saffron
@SamSaffron - 谢谢你的评论。如果你解决了问题,请告诉我,我可以推送我的更改。你可以复制我的帖子中的测试内容,很明显应该在哪里进行更改;-) 顺便说一句,我喜欢GitHub for Windows。如果需要测试方面的帮助,请告诉我。 - Jakub Konecki
2
我已经为Dapper发了一个PR,应该可以解决这些行结尾问题。如果它们仍然出现,请告诉我。故事的寓意是:*如果原始人使用了autocrlf = false,请将此文件(https://gist.github.com/2802523#file_the+original+guy+used+autocrlffalse)复制粘贴为`.gitattributes` *如果他使用了autocrlf = true,请将此文件(https://gist.github.com/2802523#file_the+original+guy+used+autocrlftrue)复制粘贴为`.gitattributes` - Ana Betts
3个回答

23
这是“按设计而行”,尽管我可以重新审视它。 特别是这个行为可以帮助左连接。以这个为例:
cnn.Query<Car,Driver>("select * from Cars c left join Drivers on c.Id = CarId",
   (c,d) => {c.Driver = d; return c;}) 

问题在于如果我们允许“概括地”创建一个Driver对象,每个Car都会有一个Driver,即使连接失败的Car也不例外。

为了解决这个问题,我们可以扫描被拆分的整个段,并确保所有值都是NULL,然后再映射NULL对象。这对多重映射器的性能影响非常小。

针对你的情况,你可以插入一个替代列:

var sql = @"select 1 as id, 'abc' as name, '' as split, 
            NULL as description, 'def' as name";
    var product = connection.Query<Product, Category, Product>(sql, (prod, cat) =>
    {
        prod.Category = cat;
        return prod;
    }, splitOn: "split").First();

1
谢谢你的回答 - 我明白你的观点。我在我的项目中使用了Drivers表的主键实现了类似的解决方法。空的代理列强制为每一行创建对象,即使是那些失败的左连接,所以为了找到缺失的记录,我仍需要在映射函数中自己检查所有属性。如果Dapper可以以通用的方式处理它,而不是我在多个映射中检查多个属性的“NULL”,那就太好了。也许有一些标志可以提高性能?更详细的示例(http://code.google.com/p/dapper-dot-net/)也会受到欢迎。 - Jakub Konecki
3
我刚遇到了这个问题,之前并不知道会出现这种情况。我希望至少能有一个 splitOnNull:true 的标记来避免这个问题。在那之前,我将不得不使用代理列。 - Jay Sullivan
1
我更喜欢承担性能损失,或者使用splitOnNull:true。这是一种特殊的行为,我的同事需要“知道”才能使用Dapper。通常情况下,您可能会在外键上进行联接,因此该字段可以成为可靠的分隔符。但我仍然不喜欢开发人员必须知道这一点,如果字段恰好不为空,这种错误很容易通过QA的检查。 - Chris
@Patrick,拆分是在名称列之间进行的,因此第一个名称存储在prod.name中,第二个名称存储在cat.name中。(重复可能是有意为之,以说明这一点。) - Chris
我已经与我的团队讨论过,我们决定删除所有Dapper子对象的填充,而不是期望开发人员意识到它(以额外的查询为代价)。完全尊重Dapper的创始哲学,即ORM具有自己的观点! - Chris
显示剩余4条评论

4

我有同样的问题,我被迫选择一个虚假的“分割”列来让Dapper填充我的对象而不是只是将其置为空;

我的解决方法:

    string req = @"
SELECT
    T1.a as PropA,
    T1.b as PropB,

    1 as Split,
    T2.a as PropA,
    T2.b as PropB,

    1 as Split,
    ...
FROM
    xxx T1,
    yyy T2,
    ...";
    using (var db = new OracleConnection(...))
    {
        return db.Query(
            req,
            (T1, T2) => {
                ...
            },
            splitOn:'Split,Split,...');
    }

Dapper应该有一个选项来避免使用splitOn:'Split,Split,...'


3

对于所有需要可视化的人:

Dapper通过最后一个相等的列名进行拆分:

enter image description here

让我们交换列的位置:

enter image description here

空值问题:

enter image description here

交换列空值:

enter image description here

Spliton来拯救:

enter image description here


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