使用Dapper.net进行多映射查询时出现意外行为

时间:2011-07-15 11:01:00

标签: c# asp.net mapping dapper

我刚刚开始关注Dapper.net并且刚刚尝试了一些不同的查询,其中一个产生了我不希望的奇怪结果。

我有两张桌子 - Photos& PhotoCategories,与CategoryID

相关

照片表

PhotoId (PK - int)  
CategoryId (FK - smallint)  
UserId (int)

PhotoCategories表

CategoryId (PK - smallint)  
CategoryName (nvarchar(50))

我的2个课程:

public class Photo
{
    public int PhotoId { get; set; }
    public short CategoryId { get; set; }
    public int UserId { get; set; }
    public PhotoCategory PhotoCategory { get; set; }
}

public class PhotoCategory
{
    public short CategoryId { get; set; }
    public string CategoryName { get; set; }
{

我想使用多映射来返回Photo的实例,其中包含相关PhotoCategory的填充实例。

var sql = @"select p.*, c.* from Photos p inner 
            join PhotoCategories c 
            on p.CategoryID = c.CategoryID where p.PhotoID = @pid";

cn.Open();
var myPhoto = cn.Query<Photo, PhotoCategory, Photo>(sql, 
               (photo, photoCategory) => { photo.PhotoCategory = photoCategory; 
                                           return photo; }, 
               new { pid = photoID }, null, true, splitOn: "CategoryID").Single();

执行此操作时,并不是所有属性都会被填充(尽管数据库表和我的对象之间的名称相同。

我注意到,如果我don't'选择p。*等'在我的SQL,而不是。

我明确说明了这些字段。

我想从查询中返回EXCLUDING p.CategoryId,然后填充所有内容(显然除了我从select语句中排除的Photo对象的CategoryId)。

但我希望能够在查询中包含该字段,并将其与SQL中查询的所有其他字段一起填充。

我可以从我的CategoryId类中排除Photo属性,并在需要ID时始终使用Photo.PhotoCategory.CategoryId

但在某些情况下,当我得到一个实例时,我可能不想填充PhotoCategory对象 Photo对象。

有谁知道上述行为发生的原因?这对Dapper来说是正常的吗?

3 个答案:

答案 0 :(得分:5)

我刚刚为此做了一个修复:

    class Foo1 
    {
        public int Id;
        public int BarId { get; set; }
    }

    class Bar1
    {
        public int BarId;
        public string Name { get; set; }
    }

    public void TestMultiMapperIsNotConfusedWithUnorderedCols()
    {

        var result = connection.Query<Foo1,Bar1,
                      Tuple<Foo1,Bar1>>(
                         "select 1 as Id, 2 as BarId, 3 as BarId, 'a' as Name",
                         (f,b) => Tuple.Create(f,b), splitOn: "BarId")
                         .First();

        result.Item1.Id.IsEqualTo(1);
        result.Item1.BarId.IsEqualTo(2);
        result.Item2.BarId.IsEqualTo(3);
        result.Item2.Name.IsEqualTo("a");

    }

如果first类型中有一个字段,那么多映射器就会感到困惑,这个字段也恰好位于second类型中...... AND ...用作分割点。

现在要克服急速,允许Id字段显示在第一种类型的任何位置。为了显示。

说我们有:

classes: A{Id,FooId} B{FooId,Name}
splitOn: "FooId"
data: Id, FooId, FooId, Name

旧的拆分方法没有考虑它映射的实际底层类型。所以......它映射了Id => AFooId, FooId, Name => B

新方法了解A中的道具和字段。当它第一次遇到流中的FooId时,它不会启动拆分,因为它知道A有一个名为FooId的属性需要映射,下次它看到{{1}它会分裂,产生预期的结果。

答案 1 :(得分:0)

我遇到了类似的问题。这与子和父对于正在拆分的字段具有相同名称的事实有关。以下例如有效:

class Program
{
    static void Main(string[] args)
    {
        var createSql = @"
            create table #Users (UserId int, Name varchar(20))
            create table #Posts (Id int, OwnerId int, Content varchar(20))

            insert #Users values(99, 'Sam')
            insert #Users values(2, 'I am')

            insert #Posts values(1, 99, 'Sams Post1')
            insert #Posts values(2, 99, 'Sams Post2')
            insert #Posts values(3, null, 'no ones post')
        ";

        var sql =
        @"select * from #Posts p 
        left join #Users u on u.UserId = p.OwnerId 
        Order by p.Id";

        using (var connection = new SqlConnection(@"CONNECTION STRING HERE"))
        {
            connection.Open();
            connection.Execute(createSql);

            var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post; }, splitOn: "UserId");
            var apost = data.First();

            apost.Content = apost.Content;
            connection.Execute("drop table #Users drop table #Posts");
        }
    }
}

class User
{
    public int UserId { get; set; }
    public string Name { get; set; }
}
class Post
{
    public int Id { get; set; }
    public int OwnerId { get; set; }
    public User Owner { get; set; }
    public string Content { get; set; }
}

但以下不是因为“UserId”用于表和两个对象。

class Program
{
    static void Main(string[] args)
    {
        var createSql = @"
            create table #Users (UserId int, Name varchar(20))
            create table #Posts (Id int, UserId int, Content varchar(20))

            insert #Users values(99, 'Sam')
            insert #Users values(2, 'I am')

            insert #Posts values(1, 99, 'Sams Post1')
            insert #Posts values(2, 99, 'Sams Post2')
            insert #Posts values(3, null, 'no ones post')
        ";

        var sql =
        @"select * from #Posts p 
        left join #Users u on u.UserId = p.UserId 
        Order by p.Id";

        using (var connection = new SqlConnection(@"CONNECTION STRING HERE"))
        {
            connection.Open();
            connection.Execute(createSql);

            var data = connection.Query<Post, User, Post>(sql, (post, user) => { post.Owner = user; return post; }, splitOn: "UserId");
            var apost = data.First();

            apost.Content = apost.Content;
            connection.Execute("drop table #Users drop table #Posts");
        }
    }
}

class User
{
    public int UserId { get; set; }
    public string Name { get; set; }
}
class Post
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public User Owner { get; set; }
    public string Content { get; set; }
}

在这种情况下,Dapper的映射似乎变得非常混乱。认为这描述了这个问题,但我们可以采用一种解决方案/解决方法(除了OO设计决策)吗?

答案 2 :(得分:0)

我知道这个问题已经过时了,但我认为我会在2分钟内找出明显的答案:只需在一张表中输入一个ID:

即:

SELECT
    user.Name, user.Email, user.AddressId As id, address.*
FROM 
    User user
    Join Address address
    ON user.AddressId = address.AddressId