I have a query with a number of joins like:
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)";
I use dapper to dematerialize the tables into strongly typed entities like:
await UnitOfWork.Connection.QueryAsync<User, LinkedAccount, PatientProfile, CareProviderProfile, Screening, LengthMeasurement, WeightMeasurement, User>(query,
(u, la, p, cp, scr, lm, wm) =>
{
// mapping code here
}
This works fine.
However, I have to add one additional JOIN to the query, but unfortunately the QueryAsync<> takes maximum 7 parameters/types, and I have more.
Is there another way to map multiple tables from a JOIN into entities?
Dapper does not care about your joins, what Dapper does care about is the tabular structure. The framework uses an id column by default, or a custom SplitOn
to parse the tabular structure into an object. So a tabular structure in the following manner:
Tabular:
Id | A | Id | B | Id | C | Id | D | Id | E | Id | F | Id | G | Id | H
You are telling Dapper that eight entities exists, Dapper does this by the id column. Now, if you have an entity but it does not require to be abstracted in this manner, for instance A, B, and C should be in a single entity then you can tell Dapper to not create a separate entity for them by producing the following structure.
Tabular:
Id | A | B | C | Id | D | Id | E | Id | F | Id | G | Id | H
The point I am making is an entity should not represent a table, but an object that represents your intent. For instance a user may have address information, but in a database they'll likely be separate tables but in your application they may be one object.
Based on the data tables I assume you can merge a couple without loosing business intent. Which based on the split would resolve your issue, otherwise I am not sure how you would exceed the seven split functionality built into Dapper.
So this is what I ended up doing.
My main query only retrieves user, linked accounts, and profile:
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)";
The application layer can specify a boolean IncludeScreening
, and if true, I also do a second call to get the screening information:
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";
...and the resulting Screening
is then set on the user.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.