使用Dapper进行JOIN操作

4

我有一个类似以下这样的带有多个连接的查询:

var sqlFindByProviderNameAndProviderSubjectId = $@"
     SELECT u.*, la.*, p.*, cp.*, scr.*, lm.*, wm.*
     FROM [user].[User] u
     LEFT JOIN [user].[LinkedAccount] la ON u.Id = la.UserId
     LEFT JOIN [user].[PatientProfile] p ON u.Id = p.UserId
     LEFT JOIN [user].[CareProviderProfile] cp ON u.Id = cp.UserId                
     LEFT JOIN [user].[Screening] scr ON u.Id = scr.UserId
     LEFT JOIN [user].[LengthMeasurement] lm ON scr.Id = lm.ScreeningId
     LEFT JOIN [user].[WeightMeasurement] wm ON scr.Id = wm.ScreeningId                 
                WHERE u.Id = (
                    SELECT UserId
                    FROM [user].[LinkedAccount] la
                    WHERE la.ProviderName = @ProviderName AND la.ProviderSubjectId = @ProviderSubjectId)";

我使用Dapper将表格转化为强类型实体,例如:
await UnitOfWork.Connection.QueryAsync<User, LinkedAccount, PatientProfile, CareProviderProfile, Screening, LengthMeasurement, WeightMeasurement, User>(query,
    (u, la, p, cp, scr, lm, wm) =>
    {
        // mapping code here
    }

这个可以正常工作。

然而,我需要在查询中添加一个额外的JOIN,但不幸的是QueryAsync<>最多只能接受7个参数/类型,而我有更多。

是否有其他方法将多个JOIN中的表映射到实体中?


除非您使用以下方法,否则必须单独调用构建实体结构,然后在代码中将其关联到父实体。多重映射仅嵌套到七层。 - Greg
2个回答

3

Dapper不关心你的连接操作,它所关心的是表格结构。该框架默认使用id列或自定义SplitOn将表格结构解析为对象。因此,表格结构如下:

Tabular:
Id | A | Id | B | Id | C | Id | D | Id | E | Id | F | Id | G | Id | H

您正在告诉Dapper存在八个实体,Dapper通过id列来执行此操作。现在,如果您有一个实体,但不需要以这种方式进行抽象化,例如A、B和C应该在单个实体中,则可以通过创建以下结构告诉Dapper不要为它们创建单独的实体。

Tabular:
Id | A | B | C | Id | D | Id | E | Id | F | Id | G | Id | H

我想要表达的观点是,一个实体不应该代表一个表格,而是代表你的意图的对象。例如,用户可能有地址信息,在数据库中它们可能是分开的表格,但在您的应用程序中它们可以是一个对象。
基于数据表,我假设您可以合并一些表格而不会失去业务意图。这样,根据拆分,您可以解决问题,否则我不确定如何超出 Dapper 内置的七个拆分功能。

感谢您的澄清。但是这些表对应着不同的实体。我想我需要将它拆分成两个查询... - L-Four

0

所以这就是我最终做的事情。

我的主要查询仅检索用户、关联账户和个人资料:

var sqlFindByProviderNameAndProviderSubjectId = $@"
            SELECT u.*, la.*, p.*, cp.*
            FROM [user].[User] u
            LEFT JOIN [user].[LinkedAccount] la ON u.Id = la.UserId
            LEFT JOIN [user].[PatientProfile] p ON u.Id = p.UserId
            LEFT JOIN [user].[CareProviderProfile] cp ON u.Id = cp.UserId                
            WHERE u.Id = (
                SELECT UserId
                FROM [user].[LinkedAccount] la
                WHERE la.ProviderName = @ProviderName AND la.ProviderSubjectId = @ProviderSubjectId)";

应用层可以指定一个布尔值IncludeScreening,如果为真,我还会进行第二次调用以获取筛选信息:
var sqlFindScreeningForUser = $@"
                SELECT scr.*, lm.*, wm.*, n.*
                FROM [user].[Screening] scr                
                LEFT JOIN [user].[LengthMeasurement] lm ON scr.Id = lm.ScreeningId
                LEFT JOIN [user].[WeightMeasurement] wm ON scr.Id = wm.ScreeningId
                LEFT JOIN [user].[Note] n ON scr.Id = n.ScreeningId
                WHERE scr.UserId = @UserId";

...然后将生成的Screening设置给用户。


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